<!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;
}]);