liberty-prettydiff
Version:
Language aware code comparison tool for several web based languages. It also beautifies, minifies, and a few other things.
860 lines (854 loc) • 74.1 kB
JavaScript
/*global ace, define, global, module*/
/*jshint laxbreak: true*/
/***
Completely rewritten by Austin Cheney on 2009-04-29 to avoid accessing
the DOM.
This is part of jsdifflib v1.0. <https://github.com/cemerick/jsdifflib>
Copyright (c) 2007, Snowtide Informatics Systems, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
* Neither the name of the Snowtide Informatics Systems nor the names
of its contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***/
/* Author: Chas Emerick <cemerick@snowtide.com> */
/* completely rewritten by Austin Cheney */
/**
* Output - an array of three indexes:
* 1) Diff result as a HTML table
* 2) Number of errors after the number of error lines used for total
* total error count when added to the next index
* 3) Number of error lines in the HTML table
*/
(function () {
"use strict";
var diffview = function diffview_(options) {
(function diffview__options() {
options.diff = (typeof options.diff === "string")
? options
.diff
.replace(options.functions.binaryCheck, "")
.replace(/\r\n?/g, "\n")
: "";
options.source = (typeof options.source === "string")
? options
.source
.replace(options.functions.binaryCheck, "")
.replace(/\r\n?/g, "\n")
: "";
if (options.semicolon === true) {
options.source = options.source.replace(/;\n/g, "\n").replace(/;$/, "");
options.diff = options.diff.replace(/;\n/g, "\n").replace(/;$/, "");
}
}());
//diffview application contains three primary parts
// 1. opcodes - performs the 'largest common subsequence' calculation to
// determine which lines are different. I did not write this logic. I have
// rewritten it for performance, but original logic is still intact.
// 2. charcomp - performs the 'largest common subsequence' upon characters
// of two compared lines.
// 3. The construction of the output into the 'node' array errorout is a count
// of differences
var errorout = 0,
//diffline is a count of lines that are not equal
diffline = 0,
//tab is a construct of a standard indentation for code
tab = (function diffview__tab() {
var a = 0,
output = [];
if (options.inchar === "") {
return "";
}
for (a = 0; a < options.insize; a += 1) {
output.push(options.inchar);
}
return output.join("");
}()),
//translates source code from a string to an array by splitting on line breaks
stringAsLines = function diffview__stringAsLines(str) {
var lines = (options.diffcli === true)
? str
: str
.replace(/&/g, "&")
.replace(/&#lt;/g, "$#lt;")
.replace(/&#gt;/g, "$#gt;")
.replace(/</g, "$#lt;")
.replace(/>/g, "$#gt;");
return lines.split("\n");
},
//array representation of base source
baseTextArray = stringAsLines(options.source),
//array representation of new source
newTextArray = stringAsLines(options.diff),
// the core algorithm. This logic is not mine even though I have largely
// rewritten it for performance. It determines the largest common subsequence
// calculations between lines of code
opcodes = (function diffview__opcodes() {
var junkdict = {},
isbjunk = function diffview__opcodes_isbjunk(key) {
if (junkdict.hasOwnProperty(key)) {
return junkdict[key];
}
},
sourceFirst = [],
sourceSecond = [],
secondInContext = [],
reverse = false,
matching_blocks = [],
answer = [],
get_matching_blocks = function diffview__opcodes_getMatchingBlocks() {
var a = 0,
matchingLen = 0,
lowFirst = 0,
highFirst = 0,
lowSecond = 0,
highSecond = 0,
bestLongestFirst = 0,
bestLongestSecond = 0,
bestLongestSize = 0,
matchFirstPrior = 0,
matchFirstNew = 0,
matchSecondPrior = 0,
matchSecondNew = 0,
matchSizePrior = 0,
matchSizeNew = 0,
sourceFirstLength = sourceFirst.length,
sourceSecondLength = sourceSecond.length,
matchInstance = [],
queueInstance = [],
non_adjacent = [],
queue = [
[0, sourceFirstLength, 0, sourceSecondLength]
],
matchingSort = function diffview__opcodes_getMatchingBlocks_ntuplecomp(x, y) {
var b = 0,
end = Math.max(x.length, y.length);
for (b = 0; b < end; b += 1) {
if (x[b] < y[b]) {
return -1;
}
if (x[b] > y[b]) {
return 1;
}
}
return (x.length === y.length)
? 0
: ((x.length < y.length)
? -1
: 1);
},
find_longest_match = function diffview__opcodes_getMatchingBlocks_findLongestMatch(lowFirst, highFirst, lowSecond, highSecond) {
var b = 0,
c = 0,
sContextLength = secondInContext.length,
sContextCompareLine = 0,
distance = 0,
priorLine = [
0, 0
],
bestFirst = lowFirst,
bestSecond = lowSecond,
bestsize = 0;
for (b = lowFirst; b < highFirst; b += 1) {
for (c = 0; c < sContextLength; c += 1) {
if (secondInContext[c][1] === sourceFirst[b]) {
if (sourceFirst[b] !== sourceSecond[b] || b === highFirst - 1 || sourceFirst[b + 1] === sourceSecond[b + 1]) {
sContextCompareLine = secondInContext[c][0];
break;
}
}
}
if (c !== sContextLength) {
if (sContextCompareLine >= lowSecond) {
if (sContextCompareLine >= highSecond) {
break;
}
if (priorLine[0] === sContextCompareLine - 1) {
distance = priorLine[1] + 1;
} else {
distance = 1;
}
if (distance > bestsize) {
bestFirst = b - distance + 1;
bestSecond = sContextCompareLine - distance + 1;
bestsize = distance;
}
}
priorLine = [sContextCompareLine, distance];
}
}
while (bestFirst > lowFirst && bestSecond > lowSecond && isbjunk(sourceSecond[bestSecond - 1]) === undefined && sourceFirst[bestFirst - 1] === sourceSecond[bestSecond - 1]) {
bestFirst -= 1;
bestSecond -= 1;
bestsize += 1;
}
while (bestFirst + bestsize < highFirst && bestSecond + bestsize < highSecond && isbjunk(sourceSecond[bestSecond + bestsize]) === undefined && sourceFirst[bestFirst + bestsize] === sourceSecond[bestSecond + bestsize]) {
bestsize += 1;
}
while (bestFirst > lowFirst && bestSecond > lowSecond && isbjunk(sourceSecond[bestSecond - 1]) !== undefined && sourceFirst[bestFirst - 1] === sourceSecond[bestSecond - 1]) {
bestFirst -= 1;
bestSecond -= 1;
bestsize += 1;
}
while (bestFirst + bestsize < highFirst && bestSecond + bestsize < highSecond && isbjunk(sourceSecond[bestSecond + bestsize]) !== undefined && sourceFirst[bestFirst + bestsize] === sourceSecond[bestSecond + bestsize]) {
bestsize += 1;
}
return [bestFirst, bestSecond, bestsize];
};
while (queue.length > 0) {
queueInstance = queue.pop();
lowFirst = queueInstance[0];
highFirst = queueInstance[1];
lowSecond = queueInstance[2];
highSecond = queueInstance[3];
matchInstance = find_longest_match(lowFirst, highFirst, lowSecond, highSecond);
bestLongestFirst = matchInstance[0];
bestLongestSecond = matchInstance[1];
bestLongestSize = matchInstance[2];
if (bestLongestSize > 0) {
matching_blocks.push(matchInstance);
if (lowFirst < bestLongestFirst && lowSecond < bestLongestSecond) {
queue.push([lowFirst, bestLongestFirst, lowSecond, bestLongestSecond]);
}
if (bestLongestFirst + bestLongestSize < highFirst && bestLongestSecond + bestLongestSize < highSecond) {
queue.push([
bestLongestFirst + bestLongestSize,
highFirst,
bestLongestSecond + bestLongestSize,
highSecond
]);
}
}
}
matching_blocks.sort(matchingSort);
matchingLen = matching_blocks.length;
for (a = 0; a < matchingLen; a += 1) {
matchFirstNew = matching_blocks[a][0];
matchSecondNew = matching_blocks[a][1];
matchSizeNew = matching_blocks[a][2];
if (matchFirstPrior + matchSizePrior === matchFirstNew && matchSecondPrior + matchSizePrior === matchSecondNew) {
matchSizePrior += matchSizeNew;
} else {
if (matchSizePrior > 0) {
non_adjacent.push([matchFirstPrior, matchSecondPrior, matchSizePrior]);
}
matchFirstPrior = matchFirstNew;
matchSecondPrior = matchSecondNew;
matchSizePrior = matchSizeNew;
}
}
if (matchSizePrior > 0) {
non_adjacent.push([matchFirstPrior, matchSecondPrior, matchSizePrior]);
}
non_adjacent.push([sourceFirstLength, sourceSecondLength, 0]);
return non_adjacent;
};
if (options.source === "" || options.diff === "") {
return "";
}
(function diffview__opcodes_diffArray() {
(function diffview__opcodes_diffArray_determineReverse() {
if (baseTextArray.length > newTextArray.length) {
reverse = true;
sourceFirst = newTextArray;
sourceSecond = baseTextArray;
} else {
sourceFirst = baseTextArray;
sourceSecond = newTextArray;
}
}());
(function diffview__opcodes_diffArray_clarity() {
var a = 0,
b = 0,
sourceLine = "",
ssLen = sourceSecond.length;
for (a = 0; a < ssLen; a += 1) {
sourceLine = sourceSecond[a];
for (b = secondInContext.length - 1; b > -1; b -= 1) {
if (secondInContext[b][1] === sourceLine) {
break;
}
}
if (b > -1) {
if (ssLen >= 200 && 100 > ssLen) {
secondInContext.splice(b, 1);
}
} else {
secondInContext.push([a, sourceLine]);
}
}
}());
(function diffview__opcodes_diffArray_algorithm() {
var a = 0,
matchingFirst = 0,
matchingSecond = 0,
matchingSize = 0,
tag = "",
firstSize = 0,
secondSize = 0,
blocks = get_matching_blocks(),
blockLength = blocks.length,
closerMatch = function diffview__opcodes_diffArray_algorithm_closerMatch(current, next, compare) {
var diffspot = function diffview__opcodes_diffArray_algorithm_closerMatch_diffspot(test, base) {
var b = 0,
cleanedTest = test
.replace(/^(\s+)/, "")
.split(""),
minSize = Math.min(cleanedTest.length, base.length);
for (b = 0; b < minSize; b += 1) {
if (cleanedTest[b] !== base[b]) {
return b;
}
}
return b;
},
cleanedCompare = compare
.replace(/^(\s+)/, "")
.split(""),
test = diffspot(next, cleanedCompare) - diffspot(current, cleanedCompare);
if (test > 0) {
return true;
}
return false;
};
for (a = 0; a < blockLength; a += 1) {
matchingFirst = blocks[a][0];
matchingSecond = blocks[a][1];
matchingSize = blocks[a][2];
tag = "";
if (firstSize < matchingFirst && secondSize < matchingSecond) {
if (firstSize - secondSize !== matchingFirst - matchingSecond && secondSize - matchingSecond < 3 && firstSize - matchingFirst < 3) {
if (reverse === true && firstSize - matchingFirst > secondSize - matchingSecond) {
if (closerMatch(sourceSecond[secondSize], sourceSecond[secondSize + 1], sourceFirst[firstSize]) === true) {
answer.push([
"delete", secondSize, secondSize + 1,
firstSize,
firstSize
]);
answer.push([
"replace", secondSize + 1,
matchingSecond,
firstSize,
matchingFirst
]);
} else {
answer.push(["replace", secondSize, matchingSecond, firstSize, matchingFirst]);
}
} else if (reverse === false && matchingSecond - secondSize > matchingFirst - firstSize) {
if (closerMatch(sourceSecond[secondSize], sourceSecond[secondSize + 1], sourceFirst[firstSize]) === true) {
answer.push([
"insert", firstSize, firstSize, secondSize, secondSize + 1
]);
answer.push([
"replace", firstSize, matchingFirst, secondSize + 1,
matchingSecond
]);
} else {
answer.push(["replace", firstSize, matchingFirst, secondSize, matchingSecond]);
}
} else {
tag = "replace";
}
} else {
tag = "replace";
}
} else if (firstSize < matchingFirst) {
if (reverse === true) {
tag = "insert";
} else {
tag = "delete";
}
} else if (secondSize < matchingSecond) {
if (reverse === true) {
tag = "delete";
} else {
tag = "insert";
}
}
if (tag !== "") {
if (reverse === true) {
answer.push([tag, secondSize, matchingSecond, firstSize, matchingFirst]);
} else {
answer.push([tag, firstSize, matchingFirst, secondSize, matchingSecond]);
}
}
firstSize = matchingFirst + matchingSize;
secondSize = matchingSecond + matchingSize;
if (matchingSize > 0) {
if (reverse === true) {
answer.push(["equal", matchingSecond, secondSize, matchingFirst, firstSize]);
} else {
answer.push(["equal", matchingFirst, firstSize, matchingSecond, secondSize]);
}
}
}
}());
}());
return answer;
}());
// after the opcodes generate the other two core pieces of logic are
// quaranteened into an anonymous function.
return (function diffview__report() {
var a = 0,
i = 0,
node = ["<div class='diff'>"],
data = (options.diffcli === true)
? [
[],
[],
[],
[],
[],
[]
]
: [
[], [], [], []
],
baseStart = 0,
baseEnd = 0,
newStart = 0,
newEnd = 0,
rowcnt = 0,
rowItem = -1,
rcount = 0,
foldcount = 0,
foldstart = -1,
jump = 0,
finaldoc = "",
tabFix = (tab === "")
? ""
: new RegExp("^((" + tab.replace(/\\/g, "\\") + ")+)"),
noTab = function diffview__report_noTab(str) {
var b = 0,
strLen = str.length,
output = [];
for (b = 0; b < strLen; b += 1) {
output.push(str[b].replace(tabFix, ""));
}
return output;
},
baseTab = (tab === "")
? []
: noTab(baseTextArray),
newTab = (tab === "")
? []
: noTab(newTextArray),
opcodesLength = opcodes.length,
change = "",
btest = false,
ntest = false,
repeat = false,
ctest = true,
code = [],
charcompOutput = [],
// this is the character comparison logic that performs the 'largest common
// subsequence' between two lines of code
charcomp = function diffview__report_charcomp(lineA, lineB) {
var b = 0,
dataA = [],
dataB = [],
cleanedA = (options.diffcli === true)
? lineA
: lineA
.replace(/ /g, " ")
.replace(/ /g, " ")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/\$#lt;/g, "<")
.replace(/\$#gt;/g, ">")
.replace(/&/g, "&"),
cleanedB = (options.diffcli === true)
? lineB
: lineB
.replace(/ /g, " ")
.replace(/ /g, " ")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/\$#lt;/g, "<")
.replace(/\$#gt;/g, ">")
.replace(/&/g, "&"),
dataMinLength = 0,
currentdiff = [],
regStart = (/_pdiffdiff\u005f/g),
regEnd = (/_epdiffdiff\u005f/g),
strStart = "_pdiffdiff\u005f",
strEnd = "_epdiffdiff\u005f",
tabdiff = (function diffview__report_charcomp_tabdiff() {
var tabMatchA = "",
tabMatchB = "",
splitA = "",
splitB = "",
analysis = [],
matchListA = cleanedA.match(tabFix),
matchListB = cleanedB.match(tabFix);
if (matchListA === null || matchListB === null || (matchListA[0] === "" && matchListA.length === 1) || (matchListB[0] === "" && matchListB.length === 1)) {
return ["", "", cleanedA, cleanedB];
}
tabMatchA = matchListA[0];
tabMatchB = matchListB[0];
splitA = cleanedA.split(tabMatchA)[1];
splitB = cleanedB.split(tabMatchB)[1];
if (tabMatchA.length > tabMatchB.length) {
analysis = tabMatchA.split(tabMatchB);
tabMatchA = tabMatchB + strStart + analysis[1] + strEnd;
tabMatchB = tabMatchB + strStart + strEnd;
} else {
analysis = tabMatchB.split(tabMatchA);
tabMatchB = tabMatchA + strStart + analysis[1] + strEnd;
tabMatchA = tabMatchA + strStart + strEnd;
}
return [tabMatchA, tabMatchB, splitA, splitB];
}()),
//compare is the fuzzy string comparison algorithm
compare = function diffview__report_charcomp_compare(start) {
var x = 0,
y = 0,
max = Math.max(dataA.length, dataB.length),
store = [],
sorta = function diffview__report_charcomp_compare_sorta(a, b) {
if (a[1] - a[0] < b[1] - b[0]) {
return 1;
}
return -1;
},
sortb = function diffview__report_charcomp_compare_sortb(a, b) {
if (a[0] + a[1] > b[0] + b[1]) {
return 1;
}
return -1;
},
whitetest = (/^(\s+)$/),
whitespace = false,
wordtest = false;
//first gather a list of all matching indexes into an array
for (x = start; x < dataMinLength; x += 1) {
for (y = start; y < max; y += 1) {
if (dataA[x] === dataB[y] || dataB[x] === dataA[y]) {
store.push([x, y]);
if (dataA[y] === dataB[x] && dataA[y + 1] === dataB[x + 1] && whitetest.test(dataB[x - 1]) === true) {
wordtest = true;
store = [
[x, y]
];
}
if (dataA[x] === dataB[y] && dataA[x + 1] === dataB[y + 1] && whitetest.test(dataB[y - 1]) === true) {
wordtest = true;
store = [
[x, y]
];
}
break;
}
}
if (wordtest === true) {
break;
}
}
//if there are no character matches then quit out
if (store.length === 0) {
return [dataMinLength, max, 0, whitespace];
}
// take the list of matches and sort it first sort by size of change with
// shortest up front second sort by sum of change start and end the second sort
// results in the smallest change from the earliest point
store.sort(sorta);
if (dataMinLength - start < 5000) {
store.sort(sortb);
}
//x should always be the shorter index (change start)
if (store[0][0] < store[0][1]) {
x = store[0][0];
y = store[0][1];
} else {
y = store[0][0];
x = store[0][1];
}
if (options.diffspaceignore === true) {
if (whitetest.test(dataA.join("").slice(b, x)) === true || whitetest.test(dataB.join("").slice(b, x)) === true) {
whitespace = true;
}
}
//package the output
if (dataA[y] === dataB[x]) {
if (dataA[y - 1] === dataB[x - 1] && x !== start) {
x -= 1;
y -= 1;
}
return [x, y, 0, whitespace];
}
if (dataA[x] === dataB[y]) {
if (dataA[x - 1] === dataB[y - 1] && x !== start) {
x -= 1;
y -= 1;
}
return [x, y, 1, whitespace];
}
};
//if same after accounting for character entities then exit
if (cleanedA === cleanedB) {
return [lineA, lineB];
}
//prevent extra error counting that occurred before entering this function
errorout -= 1;
//diff for tabs
if (tabFix !== "" && cleanedA.length !== cleanedB.length && cleanedA.replace(tabFix, "") === cleanedB.replace(tabFix, "") && options.diffspaceignore === false) {
errorout += 1;
if (options.diffcli === true) {
tabdiff[0] = tabdiff[0] + tabdiff[2];
tabdiff[0] = tabdiff[0]
.replace(regStart, "<pd>")
.replace(regEnd, "</pd>");
tabdiff[1] = tabdiff[1] + tabdiff[3];
tabdiff[1] = tabdiff[1]
.replace(regStart, "<pd>")
.replace(regEnd, "</pd>");
return [tabdiff[0], tabdiff[1]];
}
tabdiff[0] = tabdiff[0] + tabdiff[2];
tabdiff[0] = tabdiff[0]
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(regStart, "<em>")
.replace(regEnd, "</em>");
tabdiff[1] = tabdiff[1] + tabdiff[3];
tabdiff[1] = tabdiff[1]
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(regStart, "<em>")
.replace(regEnd, "</em>");
return [tabdiff[0], tabdiff[1]];
}
//turn the pruned input into arrays
dataA = cleanedA.split("");
dataB = cleanedB.split("");
//the length of the shortest array
dataMinLength = Math.min(dataA.length, dataB.length);
for (b = 0; b < dataMinLength; b += 1) {
//if undefined break the loop
if (dataA[b] === undefined || dataB[b] === undefined) {
break;
}
//iterate until the arrays are not the same
if (dataA[b] !== dataB[b]) {
// fuzzy string comparison returns an array with these indexes 0 - shorter
// ending index of difference 1 - longer ending index of difference 2 - 0 if
// index 2 is for dataA or 1 for dataB 3 - whether the difference is only
// whitespace
currentdiff = compare(b);
//supply the difference start indicator
if (currentdiff[3] === false) {
//count each difference
errorout += 1;
if (b > 0) {
dataA[b - 1] = dataA[b - 1] + strStart;
dataB[b - 1] = dataB[b - 1] + strStart;
} else {
dataA[b] = strStart + dataA[b];
dataB[b] = strStart + dataB[b];
}
//complex decision tree on how to supply difference end indicator
if (currentdiff[2] === 1) {
if (currentdiff[0] === 0) {
dataA[0] = dataA[0].replace(regStart, strStart + strEnd);
} else if (currentdiff[0] === dataMinLength) {
if (dataB.length === dataMinLength) {
dataA[dataA.length - 1] = dataA[dataA.length - 1] + strEnd;
} else {
dataA[currentdiff[0] - 1] = dataA[currentdiff[0] - 1] + strEnd;
}
} else {
if (dataA[currentdiff[0]].indexOf(strStart) > -1) {
dataA[currentdiff[0]] = dataA[currentdiff[0]] + strEnd;
} else if (currentdiff[1] - currentdiff[0] === currentdiff[0]) {
dataA[b] = strEnd + dataA[b];
} else {
dataA[currentdiff[0]] = strEnd + dataA[currentdiff[0]];
}
}
if (currentdiff[1] > dataB.length - 1 || currentdiff[0] === dataMinLength) {
dataB[dataB.length - 1] = dataB[dataB.length - 1] + strEnd;
} else if (currentdiff[1] - currentdiff[0] === currentdiff[0]) {
dataB[b + (currentdiff[1] - currentdiff[0])] = strEnd + dataB[b + (currentdiff[1] - currentdiff[0])];
} else {
dataB[currentdiff[1]] = strEnd + dataB[currentdiff[1]];
}
} else {
if (currentdiff[0] === 0) {
dataB[0] = dataB[0].replace(regStart, strStart + strEnd);
} else if (currentdiff[0] === dataMinLength) {
if (dataA.length === dataMinLength) {
dataB[dataB.length - 1] = dataB[dataB.length - 1] + strEnd;
} else {
dataB[currentdiff[0] - 1] = dataB[currentdiff[0] - 1] + strEnd;
}
} else {
if (dataB[currentdiff[0]].indexOf(strStart) > -1) {
dataB[currentdiff[0]] = dataB[currentdiff[0]] + strEnd;
} else if (currentdiff[0] - currentdiff[1] === currentdiff[1]) {
dataB[b] = strEnd + dataB[b];
} else {
dataB[currentdiff[0]] = strEnd + dataB[currentdiff[0]];
}
}
if (currentdiff[1] > dataA.length - 1 || currentdiff[0] === dataMinLength) {
dataA[dataA.length - 1] = dataA[dataA.length - 1] + strEnd;
} else if (currentdiff[0] - currentdiff[1] === currentdiff[1]) {
dataA[b + (currentdiff[0] - currentdiff[1])] = strEnd + dataA[b + (currentdiff[0] - currentdiff[1])];
} else {
dataA[currentdiff[1]] = strEnd + dataA[currentdiff[1]];
}
}
}
// we must rebase the array with the shorter difference so that the end of the
// current difference is on the same index. This provides a common baseline by
// which to find the next unmatching index
if (currentdiff[1] > currentdiff[0] && currentdiff[1] - currentdiff[0] < 1000) {
if (currentdiff[2] === 1) {
do {
dataA.unshift("");
currentdiff[0] += 1;
} while (currentdiff[1] > currentdiff[0]);
} else {
do {
dataB.unshift("");
currentdiff[0] += 1;
} while (currentdiff[1] > currentdiff[0]);
}
}
// since the previous logic will grow the shorter array we have to redefine the
// shortest length
dataMinLength = Math.min(dataA.length, dataB.length);
//assign the incrementer to the end of the longer difference
b = currentdiff[1];
}
}
// if one array is longer than the other and not identified as different then
// identify this difference in length
if (dataA.length > dataB.length && dataB[dataB.length - 1] !== undefined && dataB[dataB.length - 1].indexOf(strEnd) < 1) {
dataB.push(strStart + strEnd);
dataA[dataB.length - 1] = strStart + dataA[dataB.length - 1];
dataA[dataA.length - 1] = dataA[dataA.length - 1] + strEnd;
errorout += 1;
}
if (dataB.length > dataA.length && dataA[dataA.length - 1] !== undefined && dataA[dataA.length - 1].indexOf(strEnd) < 1) {
dataA.push(strStart + strEnd);
dataB[dataA.length - 1] = strStart + dataB[dataA.length - 1];
dataB[dataB.length - 1] = dataB[dataB.length - 1] + strEnd;
errorout += 1;
}
// options.diffcli output doesn't need XML protected characters to be escaped
// because its output is the command line
if (options.diffcli === true) {
return [
dataA
.join("")
.replace(regStart, "<pd>")
.replace(regEnd, "</pd>"),
dataB
.join("")
.replace(regStart, "<pd>")
.replace(regEnd, "</pd>")
];
}
return [
dataA
.join("")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(regStart, "<em>")
.replace(regEnd, "</em>"),
dataB
.join("")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(regStart, "<em>")
.replace(regEnd, "</em>")
];
};
if (options.diffcli === false) {
if (options.diffview === "inline") {
node.push("<h3 class='texttitle'>");
node.push(options.sourcelabel);
node.push(" vs. ");
node.push(options.difflabel);
node.push("</h3><ol class='count'>");
} else {
data[0].push("<div class='diff-left'><h3 class='texttitle'>");
data[0].push(options.sourcelabel);
data[0].push("</h3><ol class='count'>");
data[2].push("<div class='diff-right'><h3 class='texttitle'>");
data[2].push(options.difflabel);
data[2].push("</h3><ol class='count' style='cursor:w-resize'>");
}
}
for (a = 0; a < opcodesLength; a += 1) {
code = opcodes[a];
change = code[0];
baseStart = code[1];
baseEnd = code[2];
newStart = code[3];
newEnd = code[4];
rowcnt = Math.max(baseEnd - baseStart, newEnd - newStart);
ctest = true;
for (i = 0; i < rowcnt; i += 1) {
//apply options.context collapsing for the output, if needed
if (options.context > -1 && opcodes.length > 1 && ((a > 0 && i === options.context) || (a === 0 && i === 0)) && change === "equal") {
ctest = false;
jump = rowcnt - ((a === 0
? 1
: 2) * options.context);
if (jump > 1) {
baseStart += jump;
newStart += jump;
i += jump - 1;
if (options.diffcli === true) {
data[5].push([baseStart, newStart]);
} else {
data[0].push("<li>...</li>");
if (options.diffview !== "inline") {
data[1].push("<li class=\"skip\"> </li>");
}
data[2].push("<li>...</li>");
data[3].push("<li class=\"skip\"> </li>");
}
if (a + 1 === opcodes.length) {
break;
}
}
} else if (change !== "equal") {
diffline += 1;
}
foldcount += 1;
if (baseTextArray[baseStart] !== newTextArray[newStart] && change === "equal") {
change = "replace";
}
// this is a check against false positives incurred by increasing or reducing of
// nesting. At this time it only checks one level deep.
if (tab !== "") {
if (btest === false && baseTextArray[baseEnd] !== newTextArray[newEnd] && typeof baseTextArray[baseStart + 1] === "string" && typeof newTextArray[newStart] === "string" && baseTab[baseStart + 1] === newTab[newStart] && baseTab[baseStart] !== newTab[newStart] && (typeof newTextArray[newStart - 1] !== "string" || baseTab[baseStart] !== newTab[newStart - 1])) {
btest = true;
} else if (ntest === false && baseTextArray[baseEnd] !== newTextArray[newEnd] && typeof newTextArray[newStart + 1] === "string" && typeof baseTextArray[baseStart] === "string" && newTab[newStart + 1] === baseTab[baseStart] &&