/*global ace, define, exports*/
/*jshint laxbreak: true*/
var diffview = function diffview_(options) {
"use strict";
(function diffview__options() {
options.context = ((/^([0-9]+)$/).test(options.context) === true)
? Number(options.context)
: -1;
options.crlf = (options.crlf === true || options.crlf === "true");
options.diff = (typeof options.diff === "string")
? options
.diff
.replace(/\u0000|\u0001|\u0002|\u0003|\u0004|\u0005|\u0006|\u0007|\u0008|\u000b|\u000c|\u000e|\u000f|\u0010|\u0011|\u0012|\u0013|\u0014|\u0015|\u0016|\u0017|\u0018|\u0019|\u001a|\u001b|\u001c|\u001d|\u001e|\u001f|\u007f|\u0080|\u0081|\u0082|\u0083|\u0084|\u0085|\u0086|\u0087|\u0088|\u0089|\u008a|\u008b|\u008c|\u008d|\u008e|\u008f|\u0090|\u0091|\u0092|\u0093|\u0094|\u0095|\u0096|\u0097|\u0098|\u0099|\u009a|\u009b|\u009c|\u009d|\u009e|\u009f/g, "")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n")
: "";
options.diffcli = (options.diffcli === true || options.diffcli === "true");
options.difflabel = (typeof options.difflabel === "string")
? options
.difflabel
.replace(/&/g, "&")
.replace(//g, ">")
: "New Source";
options.diffspaceignore = (options.diffspaceignore === true || options.diffspaceignore === "true");
options.diffview = (options.diffview === "inline")
? "inline"
: "sidebyside";
options.inline = (options.inline === true || options.inline === "true" || options.inline === "inline" || options.diffview === "inline");
options.inchar = (typeof options.inchar === "string")
? options.inchar
: " ";
options.insize = ((/^([0-9]+)$/).test(options.insize))
? Number(options.insize)
: 4;
options.source = (typeof options.source === "string")
? options
.source
.replace(/\u0000|\u0001|\u0002|\u0003|\u0004|\u0005|\u0006|\u0007|\u0008|\u000b|\u000c|\u000e|\u000f|\u0010|\u0011|\u0012|\u0013|\u0014|\u0015|\u0016|\u0017|\u0018|\u0019|\u001a|\u001b|\u001c|\u001d|\u001e|\u001f|\u007f|\u0080|\u0081|\u0082|\u0083|\u0084|\u0085|\u0086|\u0087|\u0088|\u0089|\u008a|\u008b|\u008c|\u008d|\u008e|\u008f|\u0090|\u0091|\u0092|\u0093|\u0094|\u0095|\u0096|\u0097|\u0098|\u0099|\u009a|\u009b|\u009c|\u009d|\u009e|\u009f/g, "")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n")
: "";
options.sourcelabel = (typeof options.sourcelabel === "string")
? options
.sourcelabel
.replace(/&/g, "&")
.replace(//g, ">")
: "Base Source";
//old non-standard API
if (typeof options.baseTextLines === "string") {
options.source = options
.baseTextLines
.replace(/\u0000|\u0001|\u0002|\u0003|\u0004|\u0005|\u0006|\u0007|\u0008|\u000b|\u000c|\u000e|\u000f|\u0010|\u0011|\u0012|\u0013|\u0014|\u0015|\u0016|\u0017|\u0018|\u0019|\u001a|\u001b|\u001c|\u001d|\u001e|\u001f|\u007f|\u0080|\u0081|\u0082|\u0083|\u0084|\u0085|\u0086|\u0087|\u0088|\u0089|\u008a|\u008b|\u008c|\u008d|\u008e|\u008f|\u0090|\u0091|\u0092|\u0093|\u0094|\u0095|\u0096|\u0097|\u0098|\u0099|\u009a|\u009b|\u009c|\u009d|\u009e|\u009f/g, "")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n");
}
if (typeof options.baseTextName === "string") {
options.sourcelabel = options
.baseTextName
.replace(/&/g, "&")
.replace(//g, ">");
}
if (typeof options.newTextLines === "string") {
options.diff = options
.newTextLines
.replace(/\u0000|\u0001|\u0002|\u0003|\u0004|\u0005|\u0006|\u0007|\u0008|\u000b|\u000c|\u000e|\u000f|\u0010|\u0011|\u0012|\u0013|\u0014|\u0015|\u0016|\u0017|\u0018|\u0019|\u001a|\u001b|\u001c|\u001d|\u001e|\u001f|\u007f|\u0080|\u0081|\u0082|\u0083|\u0084|\u0085|\u0086|\u0087|\u0088|\u0089|\u008a|\u008b|\u008c|\u008d|\u008e|\u008f|\u0090|\u0091|\u0092|\u0093|\u0094|\u0095|\u0096|\u0097|\u0098|\u0099|\u009a|\u009b|\u009c|\u009d|\u009e|\u009f/g, "")
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n");
}
if (typeof options.newTextName === "string") {
options.difflabel = options
.newTextName
.replace(/&/g, "&")
.replace(//g, ">");
}
if ((/^([0-9]+)$/).test(options.contextSize) === true) {
options.context = Number(options.contextSize);
}
if (typeof options.tchar === "string") {
options.inchar = options.tchar;
}
if ((/^([0-9]+)$/).test(options.tsize) === true) {
options.insize = Number(options.tsize);
}
}());
//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,
lf = (options.crlf === true)
? "\r\n"
: "\n",
//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("");
}()),
//array representation of base source
linesBase = (options.diffcli === true)
? options.source.split(lf)
: options.source
.replace(/&/g, "&")
.replace(/lt;/g, "$#lt;")
.replace(/gt;/g, "$#gt;")
.replace(//g, "$#gt;")
.split(lf),
//array representation of new source
linesNew = (options.diffcli === true)
? options.diff.split(lf)
: options.diff
.replace(/&/g, "&")
.replace(/lt;/g, "$#lt;")
.replace(/gt;/g, "$#gt;")
.replace(//g, "$#gt;")
.split(lf),
//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 a = 0,
codes = [],
lb = 0,
ln = 0,
aa = 0,
bb = 0,
match = 0,
end = 0,
same = function diffview__opcodes_initSame() {
return;
},
diff = function diffview__opcodes_initDiff() {
return;
},
diffeval = function diffview__opcodes_diffeval(type) {
if (type === "delete") {
codes.push(["delete", lb, (lb + (bb - a)), ln, ln]);
lb += (bb - a);
aa = bb - a;
do {
linesNew.unshift("");
aa -= 1;
} while (aa > 0);
a = bb;
if (bb === end) {
return;
}
return same();
}
if (type === "insert") {
codes.push(["insert", lb, lb, ln, (ln + (bb - a))]);
ln += (bb - a);
aa = bb - a;
do {
linesBase.unshift("");
aa -= 1;
} while (aa > 0);
a = bb;
return;
if (bb === end) {
return;
}
return same();
}
if (type === "replace") {
codes.push(["replace", lb, (lb + (bb - aa)), ln, (ln + (bb - aa))]);
lb += (bb - aa);
ln += (bb - aa);
a = bb;
if (bb === end) {
return;
}
return same();
}
};
same = function diffview__opcodes_sames() {
var xx = 0;
end = Math.min(linesBase.length, linesNew.length);
for (xx = a + 1; xx < end; xx += 1) {
if (linesBase[xx] !== linesNew[xx]) {
codes.push(["equal", lb, (lb + (xx - a)), ln, (ln + (xx - a))]);
lb += (xx - a);
ln += (xx - a);
a = xx;
return diff();
}
}
codes.push(["equal", lb, (lb + (aa - a)), ln, (ln + (aa - a))]);
};
diff = function diffview__opcodes_diff() {
match = 0;
for (aa = a; aa < end; aa += 1) {
for (bb = aa + 1; bb < end; bb += 1) {
if (linesBase[bb] === linesNew[aa]) {
return diffeval("delete");
}
if (linesBase[bb] === linesNew[aa]) {
if (match === 0) {
match = bb - aa;
} else {
if (match <= bb - aa) {
if (match === bb - aa) {
bb = aa + match - 1;
} else {
bb = aa + match;
}
return diffeval("delete");
}
return diffeval("insert");
}
} else if (linesBase[aa] === linesNew[bb]) {
if (match === 0) {
match = bb - aa;
} else {
if (match <= bb - aa) {
if (match === bb - aa) {
bb = aa + match - 1;
} else {
bb = aa + match;
}
return diffeval("insert");
}
return diffeval("delete");
}
} else if (linesBase[bb] === linesNew[bb] && match > 0 && match > bb - aa) {
return diffeval("replace");
}
}
}
};
if (linesBase[0] === linesNew[0]) {
same();
} else {
Math.min(linesBase.length, linesNew.length);
diff();
}
if (linesBase[0] === "") {
a = 0;
do {
a += 1;
} while (linesBase[a] === "");
linesBase = linesBase.slice(a);
}
if (linesNew[0] === "") {
a = 0;
do {
a += 1;
} while (linesNew[a] === "");
linesNew = linesNew.slice(a);
}
return codes;
}());
//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 = ["
"],
data = (options.diffcli === true)
? [
[],
[],
[],
[],
[],
[]
]
: [
[], [], [], []
],
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(linesBase),
newTab = (tab === "")
? []
: noTab(linesNew),
opcodesLength = opcodes.length,
btest = false,
ntest = false,
repeat = false,
ctest = true,
bs = 0,
be = 0,
ns = 0,
ne = 0,
change = "",
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, "
")
.replace(regEnd, "");
tabdiff[1] = tabdiff[1] + tabdiff[3];
tabdiff[1] = tabdiff[1]
.replace(regStart, "
")
.replace(regEnd, "");
return [tabdiff[0], tabdiff[1]];
}
tabdiff[0] = tabdiff[0] + tabdiff[2];
tabdiff[0] = tabdiff[0]
.replace(/&/g, "&")
.replace(//g, ">")
.replace(regStart, "
")
.replace(regEnd, "");
tabdiff[1] = tabdiff[1] + tabdiff[3];
tabdiff[1] = tabdiff[1]
.replace(/&/g, "&")
.replace(//g, ">")
.replace(regStart, "
")
.replace(regEnd, "");
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 {
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 {
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 {
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 {
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) < 0) {
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) < 0) {
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, "
")
.replace(regEnd, ""),
dataB
.join("")
.replace(regStart, "
")
.replace(regEnd, "")
];
}
return [
dataA
.join("")
.replace(/&/g, "&")
.replace(//g, ">")
.replace(regStart, "
")
.replace(regEnd, ""),
dataB
.join("")
.replace(/&/g, "&")
.replace(//g, ">")
.replace(regStart, "
")
.replace(regEnd, "")
];
};
if (options.diffcli === false) {
if (options.inline === true) {
node.push("
");
node.push(options.sourcelabel);
node.push(" vs. ");
node.push(options.difflabel);
node.push("
");
} else {
data[0].push("");
data[0].push(options.sourcelabel);
data[0].push("
");
data[2].push("");
data[2].push(options.difflabel);
data[2].push("
");
}
}
for (a = 0; a < opcodesLength; a += 1) {
change = opcodes[a][0];
bs = opcodes[a][1];
be = opcodes[a][2];
ns = opcodes[a][3];
ne = opcodes[a][4];
rowcnt = Math.max(be - bs, ne - ns);
ctest = true;
for (i = 0; i < rowcnt; i += 1) {
//apply options.context collapsing for the output, if needed
if (options.context > -1 && opcodesLength > 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) {
bs += jump;
ns += jump;
i += jump - 1;
if (options.diffcli === true) {
data[5].push([bs, ns]);
} else {
data[0].push("- ...
");
if (options.inline === false) {
data[1].push("-
");
}
data[2].push("- ...
");
data[3].push("-
");
}
if (a + 1 === opcodes.length) {
break;
}
}
} else if (change !== "equal") {
diffline += 1;
}
foldcount += 1;
if (linesBase[bs] !== linesNew[ns] && 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 && linesBase[be] !== linesNew[ne] && typeof linesBase[bs + 1] === "string" && typeof linesNew[ns] === "string" && baseTab[bs + 1] === newTab[ns] && baseTab[bs] !== newTab[ns] && (typeof linesNew[ns - 1] !== "string" || baseTab[bs] !== newTab[ns - 1])) {
btest = true;
} else if (ntest === false && linesBase[be] !== linesNew[ne] && typeof linesNew[ns + 1] === "string" && typeof linesBase[bs] === "string" && newTab[ns + 1] === baseTab[bs] && newTab[ns] !== baseTab[bs] && (typeof linesBase[bs - 1] !== "string" || newTab[ns] !== baseTab[bs - 1])) {
ntest = true;
}
}
if (options.diffcli === true) {
if (options.diffspaceignore === true && change === "replace" && linesBase[bs].replace(/\s+/g, "") === linesNew[ns].replace(/\s+/g, "")) {
change = "equal";
errorout -= 1;
} else {
//data array schema:
//0 - base line number
//1 - base code line
//2 - new line number
//3 - new code line
//4 - [0] (change)
//5 - index of options.context (not parallel)
if (ntest === true || change === "insert") {
if (options.diffspaceignore === false || (/^(\s+)$/g).test(linesNew[ns]) === false) {
data[0].push(0);
data[1].push("");
data[2].push(ns + 1);
data[3].push(linesNew[ns]);
data[4].push("insert");
errorout += 1;
}
} else if (btest === true || change === "delete") {
if (options.diffspaceignore === false || (/^(\s+)$/g).test(linesBase[bs]) === false) {
data[0].push(bs + 1);
data[1].push(linesBase[bs]);
data[2].push(0);
data[3].push("");
data[4].push("delete");
errorout += 1;
}
} else if (change === "replace") {
if (linesBase[bs] !== linesNew[ns]) {
if (linesBase[bs] === "") {
charcompOutput = ["", linesNew[ns]];
} else if (linesNew[ns] === "") {
charcompOutput = [linesBase[bs], ""];
} else if (bs < be && ns < ne) {
charcompOutput = charcomp(linesBase[bs], linesNew[ns]);
errorout += 1;
}
}
if (bs < be) {
data[0].push(bs + 1);
if (ns < ne) {
data[1].push(charcompOutput[0]);
} else {
data[1].push(linesBase[bs]);
}
data[2].push(0);
data[3].push("");
data[4].push("delete");
}
if (ns < ne) {
data[0].push(0);
data[1].push("");
data[2].push(ns + 1);
if (bs < be) {
data[3].push(charcompOutput[1]);
} else {
data[3].push(linesNew[ns]);
}
data[4].push("insert");
}
} else if (bs < be || ns < ne) {
if (options.diffspaceignore === false || linesBase[bs].replace(/\s+/g, "") !== linesNew[ns].replace(/\s+/g, "")) {
data[0].push(bs + 1);
data[1].push(linesBase[bs]);
data[2].push(ns + 1);
data[3].push(linesNew[ns]);
data[4].push(change);
if (change !== "equal") {
errorout += 1;
}
}
}
if (btest === true) {
bs += 1;
btest = false;
} else if (ntest === true) {
ns += 1;
ntest = false;
} else {
bs += 1;
ns += 1;
}
}
//this is the final of the three primary components
//this is where the output is built
} else if (options.inline === true) {
if (options.diffspaceignore === true && change === "replace" && linesBase[bs].replace(/\s+/g, "") === linesNew[ns].replace(/\s+/g, "")) {
change = "equal";
errorout -= 1;
}
if (options.context < 0 && rowItem < a) {
rowItem = a;
if (foldstart > -1) {
if (data[0][foldstart + 1] === foldcount - 1) {
data[0][foldstart] = "- " + data[0][foldstart].slice(data[0][foldstart].indexOf("line xxx'>- ") + 12);
} else {
data[0][foldstart] = data[0][foldstart].replace("xxx", (foldcount - 1 + rcount));
}
}
if (change !== "replace") {
data[0].push("
- - ");
foldstart = data[0].length - 1;
if (ntest === true || change === "insert") {
data[0].push("
");
} else {
data[0].push(bs + 1);
}
data[0].push("
");
} else {
rcount += 1;
}
} else if (change !== "replace") {
data[0].push("- ");
if (ntest === true || change === "insert") {
data[0].push("
");
} else {
data[0].push(bs + 1);
}
data[0].push("
");
} else if (change === "replace") {
rcount += 1;
}
if (ntest === true || change === "insert") {
data[2].push("- ");
data[2].push(ns + 1);
data[2].push("
");
if (options.diffspaceignore === true && linesNew[ns].replace(/\s+/g, "") === "") {
data[3].push("- ");
diffline -= 1;
} else {
data[3].push("
- ");
}
data[3].push(linesNew[ns]);
data[3].push("
");
} else if (btest === true || change === "delete") {
data[2].push("-
");
if (options.diffspaceignore === true && linesBase[bs].replace(/\s+/g, "") === "") {
data[3].push("- ");
diffline -= 1;
} else {
data[3].push("
- ");
}
data[3].push(linesBase[bs]);
data[3].push("
");
} else if (change === "replace") {
if (linesBase[bs] !== linesNew[ns]) {
if (linesBase[bs] === "") {
charcompOutput = ["", linesNew[ns]];
} else if (linesNew[ns] === "") {
charcompOutput = [linesBase[bs], ""];
} else if (bs < be && ns < ne) {
charcompOutput = charcomp(linesBase[bs], linesNew[ns]);
}
}
if (bs < be) {
data[0].push("- "+ (bs + 1) + "
");
data[2].push("-
");
if (options.diffspaceignore === true && linesBase[bs].replace(/\s+/g, "") === "") {
data[3].push("- ");
diffline -= 1;
} else {
data[3].push("
- ");
}
if (ns < ne) {
data[3].push(charcompOutput[0]);
} else {
data[3].push(linesBase[bs]);
}
data[3].push("
");
}
if (ns < ne) {
data[0].push("-
");
data[2].push("- ");
data[2].push(ns + 1);
data[2].push("
");
if (options.diffspaceignore === true && linesNew[ns].replace(/\s+/g, "") === "") {
data[3].push("- ");
diffline -= 1;
} else {
data[3].push("
- ");
}
if (bs < be) {
data[3].push(charcompOutput[1]);
} else {
data[3].push(linesNew[ns]);
}
data[3].push("
");
}
} else if (bs < be || ns < ne) {
data[2].push("- ");
data[2].push(ns + 1);
data[2].push("
");
data[3].push("- ");
data[3].push(linesBase[bs]);
data[3].push("
");
}
if (btest === true) {
bs += 1;
btest = false;
} else if (ntest === true) {
ns += 1;
ntest = false;
} else {
bs += 1;
ns += 1;
}
} else {
if (btest === false && ntest === false && typeof linesBase[bs] === "string" && typeof linesNew[ns] === "string") {
if (linesBase[bs] === "" && linesNew[ns] !== "") {
change = "insert";
}
if (linesNew[ns] === "" && linesBase[bs] !== "") {
change = "delete";
}
if (change === "replace" && bs < be && ns < ne && linesBase[bs] !== linesNew[ns]) {
if (options.diffspaceignore === true && linesBase[bs].replace(/\s+/g, "") === linesNew[ns].replace(/\s+/g, "")) {
change = "equal";
charcompOutput = [linesBase[bs], linesNew[ns]];
} else {
charcompOutput = charcomp(linesBase[bs], linesNew[ns]);
}
} else {
charcompOutput = [linesBase[bs], linesNew[ns]];
}
if (bs === Number(data[0][data[0].length - 1].substring(data[0][data[0].length - 1].indexOf(">") + 1, data[0][data[0].length - 1].lastIndexOf("<"))) - 1 || ns === Number(data[2][data[2].length - 1].substring(data[2][data[2].length - 1].indexOf(">") + 1, data[2][data[2].length - 1].lastIndexOf("<"))) - 1) {
repeat = true;
}
if (repeat === false) {
if (bs < be) {
if (options.context < 0 && rowItem < a) {
rowItem = a;
if (foldstart > -1) {
data[0][foldstart] = data[0][foldstart].replace("xxx", (foldcount - 1));
}
data[0].push("- - " + (bs + 1) + "
");
foldstart = data[0].length - 1;
} else {
data[0].push("- " + (bs + 1) + "
");
}
data[1].push("- ");
data[1].push(charcompOutput[0]);
data[1].push("
");
} else if (ctest === true) {
if (options.context < 0 && rowItem < a) {
rowItem = a;
if (foldstart > -1) {
data[0][foldstart] = data[0][foldstart].replace("xxx", (foldcount - 1));
}
data[0].push("- -
");
foldstart = data[0].length - 1;
} else {
data[0].push("-
");
}
data[1].push("");
}
if (ns < ne) {
data[2].push("- " + (ns + 1) + "
");
data[3].push("- ");
data[3].push(charcompOutput[1]);
data[3].push("
");
} else if (ctest === true) {
data[2].push("-
");
data[3].push("");
}
} else {
repeat = false;
}
if (bs < be) {
bs += 1;
}
if (ns < ne) {
ns += 1;
}
} else if (btest === true || (typeof linesBase[bs] === "string" && typeof linesNew[ns] !== "string")) {
if (bs !== Number(data[0][data[0].length - 1].substring(data[0][data[0].length - 1].indexOf(">") + 1, data[0][data[0].length - 1].lastIndexOf("<"))) - 1) {
data[0].push("- " + (bs + 1) + "
");
data[1].push("- ");
data[1].push(linesBase[bs]);
data[1].push("
");
data[2].push("-
");
data[3].push("");
}
btest = false;
bs += 1;
} else if (ntest === true || (typeof linesBase[bs] !== "string" && typeof linesNew[ns] === "string")) {
if (ns !== Number(data[2][data[2].length - 1].substring(data[2][data[2].length - 1].indexOf(">") + 1, data[2][data[2].length - 1].lastIndexOf("<"))) - 1) {
data[0].push("-
");
data[1].push("");
data[2].push("- " + (ns + 1) + "
");
data[3].push("- ");
data[3].push(linesNew[ns]);
data[3].push("
");
}
ntest = false;
ns += 1;
}
}
}
}
if (foldstart > -1) {
data[0][foldstart] = data[0][foldstart].replace("xxx", foldcount + rcount);
}
if (options.diffcli === true) {
data.push(errorout);
return data;
}
node.push(data[0].join(""));
node.push("
");
} else {
node.push("'data'>");
node.push(data[1].join(""));
node.push("
");
}
node.push(data[2].join(""));
node.push("
");
node.push(data[3].join(""));
if (options.inline === true) {
node.push("
");
} else {
node.push("
");
}
node.push("Diff view written by Pretty D" +
"iff.
");
finaldoc = node.join("");
return [
finaldoc
.replace(/li\ class='equal'><\/li/g, "li class='equal'>