var JsDiff = (function (){ function clonePath(path){ return { newPos: path.newPos, components: path.components.slice(0)} ; } function removeEmpty(array){ var ret = [] ; for (var i = 0; i < _AN_Read_length('length', array); i++ ){ if (array[i]) { ret.push(array[i]); } } return ret; } function escapeHTML(s){ var n = s; n = _AN_Call_replace('replace', n, /&/g, "&"); n = _AN_Call_replace("replace", n, //g, ">"); n = _AN_Call_replace("replace", n, /"/g, """); return n; } var fbDiff = function (ignoreWhitespace){ this.ignoreWhitespace = ignoreWhitespace; } ; fbDiff.prototype = { diff: function (oldString, newString){ if (newString == oldString) { return [{ value: newString} ] ; } if (!newString) { return [{ value: oldString, removed: true } ] ; } if (!oldString) { return [{ value: newString, added: true } ] ; } newString = this.tokenize(newString); oldString = this.tokenize(oldString); var newLen = _AN_Read_length("length", newString), oldLen = _AN_Read_length("length", oldString); var maxEditLength = newLen + oldLen; var bestPath = [{ newPos: -1, components: [] } ] ; var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { return bestPath[0].components; } for (var editLength = 1; editLength <= maxEditLength; editLength++ ){ for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2){ var basePath; var addPath = bestPath[diagonalPath - 1], removePath = bestPath[diagonalPath + 1]; oldPos = (removePath? removePath.newPos: 0) - diagonalPath; if (addPath) { bestPath[diagonalPath - 1] = undefined; } var canAdd = addPath && addPath.newPos + 1 < newLen; var canRemove = removePath && 0 <= oldPos && oldPos < oldLen; if (!canAdd && !canRemove) { bestPath[diagonalPath] = undefined; continue ; } if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) { basePath = clonePath(removePath); this.pushComponent(basePath.components, oldString[oldPos], undefined, true ); } else { basePath = clonePath(addPath); basePath.newPos++ ; this.pushComponent(basePath.components, newString[basePath.newPos], true , undefined); } var oldPos = this.extractCommon(basePath, newString, oldString, diagonalPath); if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) { return basePath.components; } else { bestPath[diagonalPath] = basePath; } } } } , pushComponent: function (components, value, added, removed){ var last = components[_AN_Read_length("length", components) - 1]; if (last && last.added === added && last.removed === removed) { components[_AN_Read_length("length", components) - 1] = { value: this.join(last.value, value), added: added, removed: removed} ; } else { components.push({ value: value, added: added, removed: removed} ); } } , extractCommon: function (basePath, newString, oldString, diagonalPath){ var newLen = _AN_Read_length("length", newString), oldLen = _AN_Read_length("length", oldString), newPos = basePath.newPos, oldPos = newPos - diagonalPath; while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])){ newPos++ ; oldPos++ ; this.pushComponent(basePath.components, newString[newPos], undefined, undefined); } basePath.newPos = newPos; return oldPos; } , equals: function (left, right){ var reWhitespace = /\S/; if (this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right)) { return true ; } else { return left == right; } } , join: function (left, right){ return left + right; } , tokenize: function (value){ return value; } } ; var CharDiff = new fbDiff(); var WordDiff = new fbDiff(true ); WordDiff.tokenize = function (value){ return removeEmpty(value.split(/(\s+|\b)/)); } ; var CssDiff = new fbDiff(true ); CssDiff.tokenize = function (value){ return removeEmpty(value.split(/([{}:;,]|\s+)/)); } ; var LineDiff = new fbDiff(); LineDiff.tokenize = function (value){ return value.split(/^/m); } ; return { diffChars: function (oldStr, newStr){ return CharDiff.diff(oldStr, newStr); } , diffWords: function (oldStr, newStr){ return WordDiff.diff(oldStr, newStr); } , diffLines: function (oldStr, newStr){ return LineDiff.diff(oldStr, newStr); } , diffCss: function (oldStr, newStr){ return CssDiff.diff(oldStr, newStr); } , createPatch: function (fileName, oldStr, newStr, oldHeader, newHeader){ var ret = [] ; ret.push("Index: " + fileName); ret.push("==================================================================="); ret.push("--- " + fileName + (typeof oldHeader === "undefined"? "": "\t" + oldHeader)); ret.push("+++ " + fileName + (typeof newHeader === "undefined"? "": "\t" + newHeader)); var diff = LineDiff.diff(oldStr, newStr); if (!diff[_AN_Read_length("length", diff) - 1].value) { diff.pop(); } diff.push({ value: "", lines: [] } ); function contextLines(lines){ return lines.map(function (entry){ return ' ' + entry; } ); } function eofNL(curRange, i, current){ var last = diff[_AN_Read_length('length', diff) - 2], isLast = i === _AN_Read_length('length', diff) - 2, isLastOfType = i === _AN_Read_length('length', diff) - 3 && (current.added !== last.added || current.removed !== last.removed); if (!/\n$/.test(current.value) && (isLast || isLastOfType)) { curRange.push('\\ No newline at end of file'); } } var oldRangeStart = 0, newRangeStart = 0, curRange = [] , oldLine = 1, newLine = 1; for (var i = 0; i < _AN_Read_length('length', diff); i++ ){ var current = diff[i], lines = current.lines || _AN_Call_replace('replace', current.value, /\n$/, "").split("\n"); current.lines = lines; if (current.added || current.removed) { if (!oldRangeStart) { var prev = diff[i - 1]; oldRangeStart = oldLine; newRangeStart = newLine; if (prev) { curRange = contextLines(prev.lines.slice(-4)); oldRangeStart -= _AN_Read_length("length", curRange); newRangeStart -= _AN_Read_length("length", curRange); } } curRange.push.apply(curRange, lines.map(function (entry){ return (current.added? "+": "-") + entry; } )); eofNL(curRange, i, current); if (current.added) { newLine += _AN_Read_length("length", lines); } else { oldLine += _AN_Read_length("length", lines); } } else { if (oldRangeStart) { if (_AN_Read_length("length", lines) <= 8 && i < _AN_Read_length("length", diff) - 2) { curRange.push.apply(curRange, contextLines(lines)); } else { var contextSize = Math.min(_AN_Read_length("length", lines), 4); ret.push("@@ -" + oldRangeStart + "," + (oldLine - oldRangeStart + contextSize) + " +" + newRangeStart + "," + (newLine - newRangeStart + contextSize) + " @@"); ret.push.apply(ret, curRange); ret.push.apply(ret, contextLines(lines.slice(0, contextSize))); if (_AN_Read_length("length", lines) <= 4) { eofNL(ret, i, current); } oldRangeStart = 0; newRangeStart = 0; curRange = [] ; } } oldLine += _AN_Read_length("length", lines); newLine += _AN_Read_length("length", lines); } } return ret.join('\n') + '\n'; } , applyPatch: function (oldStr, uniDiff){ var diffstr = uniDiff.split("\n"); var diff = [] ; var remEOFNL = false , addEOFNL = false ; for (var i = (diffstr[0][0] == "I"? 4: 0); i < _AN_Read_length("length", diffstr); i++ ){ if (diffstr[i][0] == "@") { var meh = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/); diff.unshift({ start: meh[3], oldlength: meh[2], oldlines: [] , newlength: meh[4], newlines: [] } ); } else if (diffstr[i][0] == '+') { diff[0].newlines.push(diffstr[i].substr(1)); } else if (diffstr[i][0] == '-') { diff[0].oldlines.push(diffstr[i].substr(1)); } else if (diffstr[i][0] == ' ') { diff[0].newlines.push(diffstr[i].substr(1)); diff[0].oldlines.push(diffstr[i].substr(1)); } else if (diffstr[i][0] == '\\') { if (diffstr[i - 1][0] == '+') { remEOFNL = true ; } else if (diffstr[i - 1][0] == '-') { addEOFNL = true ; } } } var str = oldStr.split("\n"); for (var i = _AN_Read_length("length", diff) - 1; i >= 0; i-- ){ var d = diff[i]; for (var j = 0; j < d.oldlength; j++ ){ if (str[d.start - 1 + j] != d.oldlines[j]) { return false ; } } Array.prototype.splice.apply(str, [d.start - 1, + d.oldlength] .concat(d.newlines)); } if (remEOFNL) { while (!str[_AN_Read_length("length", str) - 1]){ str.pop(); } } else if (addEOFNL) { str.push(''); } return str.join('\n'); } , convertChangesToXML: function (changes){ var ret = [] ; for (var i = 0; i < _AN_Read_length('length', changes); i++ ){ var change = changes[i]; if (change.added) { ret.push(""); } else if (change.removed) { ret.push(""); } ret.push(escapeHTML(change.value)); if (change.added) { ret.push(""); } else if (change.removed) { ret.push(""); } } return ret.join(""); } , convertChangesToDMP: function (changes){ var ret = [] , change; for (var i = 0; i < _AN_Read_length("length", changes); i++ ){ change = changes[i]; ret.push([(change.added? 1: change.removed? -1: 0), change.value] ); } return ret; } } ; } )(); if (typeof module !== "undefined") { module.exports = JsDiff; }