jsondiffpatch
Version:
Diff & Patch for Javascript objects
198 lines (175 loc) • 6.53 kB
JavaScript
var base = require('./base');
var BaseFormatter = base.BaseFormatter;
var AnnotatedFormatter = function AnnotatedFormatter() {
this.includeMoveDestinations = false;
};
AnnotatedFormatter.prototype = new BaseFormatter();
AnnotatedFormatter.prototype.prepareContext = function(context) {
BaseFormatter.prototype.prepareContext.call(this, context);
context.indent = function(levels) {
this.indentLevel = (this.indentLevel || 0) +
(typeof levels === 'undefined' ? 1 : levels);
this.indentPad = new Array(this.indentLevel + 1).join(' ');
};
context.row = function(json, htmlNote) {
context.out('<tr><td style="white-space: nowrap;">' +
'<pre class="jsondiffpatch-annotated-indent" style="display: inline-block">');
context.out(context.indentPad);
context.out('</pre><pre style="display: inline-block">');
context.out(json);
context.out('</pre></td><td class="jsondiffpatch-delta-note"><div>');
context.out(htmlNote);
context.out('</div></td></tr>');
};
};
AnnotatedFormatter.prototype.typeFormattterErrorFormatter = function(context, err) {
context.row('', '<pre class="jsondiffpatch-error">' + err + '</pre>');
};
AnnotatedFormatter.prototype.formatTextDiffString = function(context, value) {
var lines = this.parseTextDiff(value);
context.out('<ul class="jsondiffpatch-textdiff">');
for (var i = 0, l = lines.length; i < l; i++) {
var line = lines[i];
context.out('<li>' +
'<div class="jsondiffpatch-textdiff-location">' +
'<span class="jsondiffpatch-textdiff-line-number">' +
line.location.line +
'</span>' +
'<span class="jsondiffpatch-textdiff-char">' +
line.location.chr +
'</span>' +
'</div>' +
'<div class="jsondiffpatch-textdiff-line">');
var pieces = line.pieces;
for (var pieceIndex = 0, piecesLength = pieces.length; pieceIndex < piecesLength; pieceIndex++) {
var piece = pieces[pieceIndex];
context.out('<span class="jsondiffpatch-textdiff-' + piece.type + '">' +
piece.text + '</span>');
}
context.out('</div></li>');
}
context.out('</ul>');
};
AnnotatedFormatter.prototype.rootBegin = function(context, type, nodeType) {
context.out('<table class="jsondiffpatch-annotated-delta">');
if (type === 'node') {
context.row('{');
context.indent();
}
if (nodeType === 'array') {
context.row('"_t": "a",', 'Array delta (member names indicate array indices)');
}
};
AnnotatedFormatter.prototype.rootEnd = function(context, type) {
if (type === 'node') {
context.indent(-1);
context.row('}');
}
context.out('</table>');
};
AnnotatedFormatter.prototype.nodeBegin = function(context, key, leftKey, type, nodeType) {
context.row('"' + key + '": {');
if (type === 'node') {
context.indent();
}
if (nodeType === 'array') {
context.row('"_t": "a",', 'Array delta (member names indicate array indices)');
}
};
AnnotatedFormatter.prototype.nodeEnd = function(context, key, leftKey, type, nodeType, isLast) {
if (type === 'node') {
context.indent(-1);
}
context.row('}' + (isLast ? '' : ','));
};
/* jshint camelcase: false */
AnnotatedFormatter.prototype.format_unchanged = function() {
return;
};
AnnotatedFormatter.prototype.format_movedestination = function() {
return;
};
AnnotatedFormatter.prototype.format_node = function(context, delta, left) {
// recurse
this.formatDeltaChildren(context, delta, left);
};
var wrapPropertyName = function(name) {
return '<pre style="display:inline-block">"' + name + '"</pre>';
};
var deltaAnnotations = {
added: function(delta, left, key, leftKey) {
var formatLegend = ' <pre>([newValue])</pre>';
if (typeof leftKey === 'undefined') {
return 'new value' + formatLegend;
}
if (typeof leftKey === 'number') {
return 'insert at index ' + leftKey + formatLegend;
}
return 'add property ' + wrapPropertyName(leftKey) + formatLegend;
},
modified: function(delta, left, key, leftKey) {
var formatLegend = ' <pre>([previousValue, newValue])</pre>';
if (typeof leftKey === 'undefined') {
return 'modify value' + formatLegend;
}
if (typeof leftKey === 'number') {
return 'modify at index ' + leftKey + formatLegend;
}
return 'modify property ' + wrapPropertyName(leftKey) + formatLegend;
},
deleted: function(delta, left, key, leftKey) {
var formatLegend = ' <pre>([previousValue, 0, 0])</pre>';
if (typeof leftKey === 'undefined') {
return 'delete value' + formatLegend;
}
if (typeof leftKey === 'number') {
return 'remove index ' + leftKey + formatLegend;
}
return 'delete property ' + wrapPropertyName(leftKey) + formatLegend;
},
moved: function(delta, left, key, leftKey) {
return 'move from <span title="(position to remove at original state)">index ' +
leftKey + '</span> to ' +
'<span title="(position to insert at final state)">index ' +
delta[1] + '</span>';
},
textdiff: function(delta, left, key, leftKey) {
var location = (typeof leftKey === 'undefined') ?
'' : (
(typeof leftKey === 'number') ?
' at index ' + leftKey :
' at property ' + wrapPropertyName(leftKey)
);
return 'text diff' + location + ', format is ' +
'<a href="https://code.google.com/p/google-diff-match-patch/wiki/Unidiff">' +
'a variation of Unidiff</a>';
}
};
var formatAnyChange = function(context, delta) {
var deltaType = this.getDeltaType(delta);
var annotator = deltaAnnotations[deltaType];
var htmlNote = annotator && annotator.apply(annotator,
Array.prototype.slice.call(arguments, 1));
var json = JSON.stringify(delta, null, 2);
if (deltaType === 'textdiff') {
// split text diffs lines
json = json.split('\\n').join('\\n"+\n "');
}
context.indent();
context.row(json, htmlNote);
context.indent(-1);
};
AnnotatedFormatter.prototype.format_added = formatAnyChange;
AnnotatedFormatter.prototype.format_modified = formatAnyChange;
AnnotatedFormatter.prototype.format_deleted = formatAnyChange;
AnnotatedFormatter.prototype.format_moved = formatAnyChange;
AnnotatedFormatter.prototype.format_textdiff = formatAnyChange;
/* jshint camelcase: true */
exports.AnnotatedFormatter = AnnotatedFormatter;
var defaultInstance;
exports.format = function(delta, left) {
if (!defaultInstance) {
defaultInstance = new AnnotatedFormatter();
}
return defaultInstance.format(delta, left);
};