jsondiffpatch
Version:
Diff & Patch for Javascript objects
137 lines (126 loc) • 4.13 kB
JavaScript
/* global diff_match_patch */
var TEXT_DIFF = 2;
var DEFAULT_MIN_LENGTH = 60;
var cachedDiffPatch = null;
var getDiffMatchPatch = function(required) {
/*jshint camelcase: false */
if (!cachedDiffPatch) {
var instance;
if (typeof diff_match_patch !== 'undefined') {
// already loaded, probably a browser
instance = typeof diff_match_patch === 'function' ?
new diff_match_patch() : new diff_match_patch.diff_match_patch();
} else if (typeof require === 'function') {
try {
var dmpModuleName = 'diff_match_patch_uncompressed';
var dmp = require('../../public/external/' + dmpModuleName);
instance = new dmp.diff_match_patch();
} catch (err) {
instance = null;
}
}
if (!instance) {
if (!required) {
return null;
}
var error = new Error('text diff_match_patch library not found');
error.diff_match_patch_not_found = true;
throw error;
}
cachedDiffPatch = {
diff: function(txt1, txt2) {
return instance.patch_toText(instance.patch_make(txt1, txt2));
},
patch: function(txt1, patch) {
var results = instance.patch_apply(instance.patch_fromText(patch), txt1);
for (var i = 0; i < results[1].length; i++) {
if (!results[1][i]) {
var error = new Error('text patch failed');
error.textPatchFailed = true;
}
}
return results[0];
}
};
}
return cachedDiffPatch;
};
var diffFilter = function textsDiffFilter(context) {
if (context.leftType !== 'string') {
return;
}
var minLength = (context.options && context.options.textDiff &&
context.options.textDiff.minLength) || DEFAULT_MIN_LENGTH;
if (context.left.length < minLength ||
context.right.length < minLength) {
context.setResult([context.left, context.right]).exit();
return;
}
// large text, try to use a text-diff algorithm
var diffMatchPatch = getDiffMatchPatch();
if (!diffMatchPatch) {
// diff-match-patch library not available, fallback to regular string replace
context.setResult([context.left, context.right]).exit();
return;
}
var diff = diffMatchPatch.diff;
context.setResult([diff(context.left, context.right), 0, TEXT_DIFF]).exit();
};
diffFilter.filterName = 'texts';
var patchFilter = function textsPatchFilter(context) {
if (context.nested) {
return;
}
if (context.delta[2] !== TEXT_DIFF) {
return;
}
// text-diff, use a text-patch algorithm
var patch = getDiffMatchPatch(true).patch;
context.setResult(patch(context.left, context.delta[0])).exit();
};
patchFilter.filterName = 'texts';
var textDeltaReverse = function(delta) {
var i, l, lines, line, lineTmp, header = null,
headerRegex = /^@@ +\-(\d+),(\d+) +\+(\d+),(\d+) +@@$/,
lineHeader, lineAdd, lineRemove;
lines = delta.split('\n');
for (i = 0, l = lines.length; i < l; i++) {
line = lines[i];
var lineStart = line.slice(0, 1);
if (lineStart === '@') {
header = headerRegex.exec(line);
lineHeader = i;
lineAdd = null;
lineRemove = null;
// fix header
lines[lineHeader] = '@@ -' + header[3] + ',' + header[4] + ' +' + header[1] + ',' + header[2] + ' @@';
} else if (lineStart === '+') {
lineAdd = i;
lines[i] = '-' + lines[i].slice(1);
if (lines[i - 1].slice(0, 1) === '+') {
// swap lines to keep default order (-+)
lineTmp = lines[i];
lines[i] = lines[i - 1];
lines[i - 1] = lineTmp;
}
} else if (lineStart === '-') {
lineRemove = i;
lines[i] = '+' + lines[i].slice(1);
}
}
return lines.join('\n');
};
var reverseFilter = function textsReverseFilter(context) {
if (context.nested) {
return;
}
if (context.delta[2] !== TEXT_DIFF) {
return;
}
// text-diff, use a text-diff algorithm
context.setResult([textDeltaReverse(context.delta[0]), 0, TEXT_DIFF]).exit();
};
reverseFilter.filterName = 'texts';
exports.diffFilter = diffFilter;
exports.patchFilter = patchFilter;
exports.reverseFilter = reverseFilter;