<!DOCTYPE html>
<html lang="en" id="ng-app" ng-app="diffDemo">

  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>angular-diff-match-patch</title>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" />
    <!--[if lte IE 7]>
		<script src="//cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
	<![endif]-->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.19/angular.min.js"></script>
    <script src="//neil.fraser.name/software/diff_match_patch/svn/trunk/javascript/diff_match_patch.js"></script>
    <script src="angular-diff-match-patch.js"></script>
    <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.js"></script>
    <script>
		var app = angular.module('diffDemo', ['ui.bootstrap','diff-match-patch']);
		app.controller('diffCtrl', function($scope) {
			$scope.left = ["I am the very model of a modern Major-General,",
						   "I've information vegetable, animal, and mineral,",
						   "I know the kings of England, and I quote the fights historical,",
						   "From Marathon to Waterloo, in order categorical."].join('\n');

			$scope.right = ["I am the very model of a cartoon individual,",
						    "My animation's comical, unusual, and whimsical,",
						    "I know the kings of England, and I quote the fights historical,",
						    "From wicked puns and stupid jokes to anvils that drop on your head."].join('\n');
		});
	</script>
    <style>
		pre.textdiff {
		word-break: normal;
		word-wrap: normal;
		}

		.match, .textdiff span {
			color: gray;
		}

		.ins, ins {
			color: black;
			background: #bbffbb;
		}

		.del, del {
			color: black;
			background: #ffbbbb;
		}

		.noselect {
			-webkit-touch-callout: none;
			-webkit-user-select: none;
			-khtml-user-select: none;
			-moz-user-select: none;
			-ms-user-select: none;
			user-select: none;
		}
		div.col-md-6 p textarea.inputBox {
			height: 120px;
		}
	</style>
  </head>

  <body ng-controller="diffCtrl">
    <div class="container-fluid">
      <div class="row text-center">
        <h1>angular-diff-match-patch</h1>
      </div>
      <div class="panel panel-default">
        <div class="panel-body">
          <div class="row">
            <div class="col-md-6">
              <h3>Left side</h3>
              <p>
                <textarea class="inputBox form-control" ng-model="left"></textarea>
              </p>
            </div>
            <div class="col-md-6">
              <h3>Right side</h3>
              <p>
                <textarea class="inputBox form-control" ng-model="right"></textarea>
              </p>
            </div>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-6">
          <div class="panel panel-default">
            <div class="panel-body">
              <pre class="textdiff" diff="" left-obj="left" right-obj="right"></pre>
            </div>
            <div class="panel-footer">diff</div>
          </div>
        </div>
        <div class="col-md-6">
          <div class="panel panel-default">
            <div class="panel-body">
              <pre class="textdiff" processing-diff="" left-obj="left" right-obj="right"></pre>
            </div>
            <div class="panel-footer">processingDiff</div>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-6">
          <div class="panel panel-default">
            <div class="panel-body">
              <pre class="textdiff" semantic-diff="" left-obj="left" right-obj="right"></pre>
            </div>
            <div class="panel-footer">semanticDiff</div>
          </div>
        </div>
        <div class="col-md-6">
          <div class="panel panel-default">
            <div class="panel-body">
              <pre class="textdiff" line-diff="" left-obj="left" right-obj="right"></pre>
            </div>
            <div class="panel-footer">lineDiff</div>
          </div>
        </div>
      </div>
    </div>
  </body>

</html>
https://github.com/amweiss/angular-diff-match-patch
/* global DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL, diff_match_patch */
angular.module('diff-match-patch', [])
	.factory('dmp', function() {

		var displayType = {
			INSDEL: 0,
			LINEDIFF: 1
		};

		function diffClass(op) {
			switch(op) {
				case DIFF_INSERT: return 'ins';
				case DIFF_DELETE: return 'del';
				case DIFF_EQUAL: return 'match';
			}
		}

		function diffSymbol(op) {
			switch(op) {
				case DIFF_EQUAL: return ' ';
				case DIFF_INSERT: return '+';
				case DIFF_DELETE: return '-';
			}
		}

		function diffTag(op) {
			switch(op) {
				case DIFF_EQUAL: return 'span';
				case DIFF_INSERT: return 'ins';
				case DIFF_DELETE: return 'del';
			}
		}

		function getHtmlPrefix(op, display) {
			var retVal = '';
				switch(display) {
					case displayType.LINEDIFF:
						retVal = '<div class="'+diffClass(op)+'"><span class="noselect">'+diffSymbol(op)+'</span>';
						break;
					case displayType.INSDEL:
						retVal = '<'+diffTag(op)+'>';
						break;
				}
			return retVal;
		}

		function getHtmlSuffix(op, display) {
			var retVal = '';
				switch(display) {
					case displayType.LINEDIFF:
						retVal = '</div>';
						break;
					case displayType.INSDEL:
						retVal = '</'+diffTag(op)+'>';
						break;
				}
			return retVal;
		}

		function createHtmlLines(text, op) {
			var lines = text.split('\n');
			for (var y = 0; y < lines.length; y++) {
				if (lines[y].length === 0) continue;
				lines[y] = getHtmlPrefix(op, displayType.LINEDIFF) + lines[y] + getHtmlSuffix(op, displayType.LINEDIFF);
			}
			return lines.join('');
		}

		function createHtmlFromDiffs(diffs, display) {
			var html = [];
			for (var x = 0; x < diffs.length; x++) {
				var op = diffs[x][0];
				var text = diffs[x][1];
				if (display === displayType.LINEDIFF) {
					html[x] = createHtmlLines(text, op);
				} else {
					html[x] = getHtmlPrefix(op, display) + text + getHtmlSuffix(op, display);
				}
			}
			return html.join('');
		}

		return {
			createDiffHtml: function(left, right) {
				if (left && right) {
					var dmp = new diff_match_patch();
					var diffs = dmp.diff_main(left, right);
					return createHtmlFromDiffs(diffs, displayType.INSDEL);
				} else {
					return '';
				}
			},

			createProcessingDiffHtml: function(left, right) {
				if (left && right) {
					var dmp = new diff_match_patch();
					var diffs = dmp.diff_main(left, right);
					//dmp.Diff_EditCost = 4;
					dmp.diff_cleanupEfficiency(diffs);
					return createHtmlFromDiffs(diffs, displayType.INSDEL);
				} else {
					return '';
				}
			},

			createSemanticDiffHtml: function(left, right) {
				if (left && right) {
					var dmp = new diff_match_patch();
					var diffs = dmp.diff_main(left, right);
					dmp.diff_cleanupSemantic(diffs);
					return createHtmlFromDiffs(diffs, displayType.INSDEL);
				} else {
					return '';
				}
			},

			createLineDiffHtml: function(left, right) {
				if (left && right) {
					var dmp = new diff_match_patch();
					var a = dmp.diff_linesToChars_(left, right);
					var diffs = dmp.diff_main(a.chars1, a.chars2, false);
					dmp.diff_charsToLines_(diffs, a.lineArray);
					return createHtmlFromDiffs(diffs, displayType.LINEDIFF);
				} else {
					return '';
				}
			}
		};
	})
	.directive('diff', ['$compile', 'dmp', function factory($compile, dmp) {
		var ddo = {
				scope: {
						left: '=leftObj',
						right: '=rightObj'
				},
				link: function postLink(scope, iElement) {
						scope.$watchGroup(['left', 'right'], function() {
							iElement.html(dmp.createDiffHtml(scope.left, scope.right));
							$compile(iElement.contents())(scope);
						});
				}
		};
		return ddo;
	}])
	.directive('processingDiff', ['$compile', 'dmp', function factory($compile, dmp) {
		var ddo = {
				scope: {
						left: '=leftObj',
						right: '=rightObj'
				},
				link: function postLink(scope, iElement) {
						scope.$watchGroup(['left', 'right'], function() {
							iElement.html(dmp.createProcessingDiffHtml(scope.left, scope.right));
							$compile(iElement.contents())(scope);
						});
				}
		};
		return ddo;
	}])
	.directive('semanticDiff', ['$compile', 'dmp', function factory($compile, dmp) {
		var ddo = {
				scope: {
						left: '=leftObj',
						right: '=rightObj'
				},
				link: function postLink(scope, iElement) {
						scope.$watchGroup(['left', 'right'], function() {
							iElement.html(dmp.createSemanticDiffHtml(scope.left, scope.right));
							$compile(iElement.contents())(scope);
						});
				}
		};
		return ddo;
	}])
	.directive('lineDiff', ['$compile', 'dmp', function factory($compile, dmp) {
		var ddo = {
				scope: {
						left: '=leftObj',
						right: '=rightObj'
				},
				link: function postLink(scope, iElement) {
						scope.$watchGroup(['left', 'right'], function() {
							iElement.html(dmp.createLineDiffHtml(scope.left, scope.right));
							$compile(iElement.contents())(scope);
						});
				}
		};
		return ddo;
	}]);