prettydiff2
Version:
Latest version of Pretty Diff for use in Atom Beautify to field test it in the wild before moving on to Pretty Diff 3.
909 lines (900 loc) • 79.8 kB
JavaScript
/*global ace, define, global, module*/
/*jshint laxbreak: true*/
/*
Written by Austin Cheney on 1 Mar 2017
Please see the license.txt file associated with the Pretty Diff
application for license information.
* 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() {
if (typeof options.diff === "string") {
if (options.functions !== undefined) {
options.diff = options
.diff
.replace(options.functions.binaryCheck, "");
}
if (options.semicolon === true) {
options.diff = options
.diff
.replace(/;\n/g, "\n")
.replace(/;$/, "");
}
}
if (typeof options.source === "string") {
if (options.functions !== undefined) {
options.source = options
.source
.replace(options.functions.binaryCheck, "");
}
if (options.semicolon === true) {
options.source = options
.source
.replace(/;\n/g, "\n")
.replace(/;$/, "");
}
}
options.diffview = (options.diffview === "inline")
? "inline"
: "sidebyside";
options.diffcomments = (
options.diffcomments === true || options.diffcomments === "true"
);
options.diffspaceignore = (
options.diffspaceignore === true || options.diffspaceignore === "true"
);
options.quote = (options.quote === true || options.quote === "true");
options.semicolon = (
options.semicolon === true || options.semicolon === "true"
);
options.content = (options.content === true || options.content === "true");
options.diffcli = (options.diffcli === true || options.diffcli === "true");
options.context = (isNaN(options.context) === false)
? Number(options.context)
: -1;
if (options.diffcli === true && options.context < 0) {
options.context = 2;
}
}());
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 = 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 = (typeof options.source === "string")
? stringAsLines(options.source)
: options.source,
//array representation of new source
newTextArray = (typeof options.diff === "string")
? stringAsLines(options.diff)
: options.diff,
opcodes = [],
codeBuild = function diffview__opcodes() {
var table = {},
one = (typeof options.source === "string")
? options.source.split("\n")
: options.source,
two = (typeof options.diff === "string")
? options.diff.split("\n")
: options.diff,
lena = one.length,
lenb = two.length,
a = 0,
b = 0,
c = 0,
d = 0,
codes = [],
fix = function diffview__opcodes_fix(code) {
var prior = codes[codes.length - 1];
if (prior !== undefined) {
if (prior[0] === code[0]) {
if (code[0] === "replace" || code[0] === "equal") {
prior[2] = code[2];
prior[4] = code[4];
} else if (code[0] === "delete") {
prior[2] = code[2];
} else if (code[0] === "insert") {
prior[4] = code[4];
}
return;
}
if (prior[0] === "insert" && prior[4] - prior[3] === 1) {
if (code[2] - code[1] === 1) {
if (code[0] === "replace") {
prior[0] = "replace";
prior[1] = code[1];
prior[2] = code[2];
code[0] = "insert";
code[1] = -1;
code[2] = -1;
} else if (code[0] === "delete") {
code[0] = "replace";
code[3] = prior[3];
code[4] = prior[4];
codes.pop();
prior = codes[codes.length - 1];
if (prior[0] === "replace") {
prior[2] = code[2];
prior[4] = code[4];
return;
}
}
} else if (code[0] === "delete") {
prior[0] = "replace";
prior[1] = code[1];
prior[2] = code[1] + 1;
code[1] = code[1] + 1;
} else if (code[0] === "replace") {
prior[0] = "replace";
prior[1] = code[1];
prior[2] = code[1] + 1;
c = prior[2];
d = prior[4];
return;
}
} else if (prior[0] === "insert" && code[0] === "delete" && code[2] - code[1] === 1) {
prior[4] = prior[4] - 1;
code[0] = "replace";
code[3] = prior[4];
code[4] = prior[4] + 1;
} else if (prior[0] === "delete" && prior[2] - prior[1] === 1) {
if (code[4] - code[3] === 1) {
if (code[0] === "replace") {
prior[0] = "replace";
prior[3] = code[3];
prior[4] = code[4];
code[0] = "delete";
code[3] = -1;
code[4] = -1;
} else if (code[0] === "insert") {
code[0] = "replace";
code[1] = prior[1];
code[2] = prior[2];
codes.pop();
prior = codes[codes.length - 1];
if (prior[0] === "replace") {
prior[2] = code[2];
prior[4] = code[4];
return;
}
}
} else if (code[0] === "insert") {
prior[0] = "replace";
prior[3] = code[3];
prior[4] = code[3] + 1;
code[3] = code[3] + 1;
} else if (code[0] === "replace") {
prior[0] = "replace";
prior[3] = code[3];
prior[4] = code[4] + 1;
c = prior[2];
d = prior[4];
return;
}
} else if (prior[0] === "delete" && code[0] === "insert" && code[4] - code[3] === 1) {
prior[2] = prior[2] - 1;
code[0] = "replace";
code[1] = prior[2];
code[2] = prior[2] + 1;
} else if (prior[0] === "replace") {
if (code[0] === "delete") {
if (one[code[2] - 1] === two[prior[4] - 1]) {
if (prior[2] - prior[1] > 1) {
prior[4] = prior[4] - 1;
}
c = c - 1;
d = d - 1;
return;
}
if (one[code[2]] === two[prior[4] - 1]) {
if (prior[2] - prior[1] > 1) {
prior[2] = prior[2] - 1;
prior[4] = prior[4] - 11;
table[one[c - 1]][0] = table[one[c - 1]][0] - 1;
}
}
} else if (code[0] === "insert") {
if (one[prior[2] - 1] === two[code[4] - 1]) {
if (prior[2] - prior[1] > 1) {
prior[2] = prior[2] - 1;
}
c = c - 1;
d = d - 1;
return;
}
if (one[code[2] - 1] === two[prior[4]]) {
if (prior[4] - prior[3] > 1) {
prior[2] = prior[2] - 1;
prior[4] = prior[4] - 1;
table[two[d - 1]][1] = table[two[d - 1]][1] - 1;
}
}
}
}
}
codes.push(code);
},
equality = function diffview__opcodes_equality() {
do {
table[one[c]][0] = table[one[c]][0] - 1;
table[one[c]][1] = table[one[c]][1] - 1;
c = c + 1;
d = d + 1;
} while (c < lena && d < lenb && one[c] === two[d]);
fix(["equal", a, c, b, d]);
b = d - 1;
a = c - 1;
},
deletion = function diffview__opcodes_deletion() {
do {
table[one[c]][0] = table[one[c]][0] - 1;
c = c + 1;
} while (c < lena && table[one[c]][1] < 1);
fix(["delete", a, c, -1, -1]);
a = c - 1;
b = d - 1;
},
deletionStatic = function diffview__opcodes_deletionStatic() {
table[one[a]][0] = table[one[a]][0] - 1;
fix([
"delete", a, a + 1,
-1,
-1
]);
a = c;
b = d - 1;
},
insertion = function diffview__opcodes_insertion() {
do {
table[two[d]][1] = table[two[d]][1] - 1;
d = d + 1;
} while (d < lenb && table[two[d]][0] < 1);
fix(["insert", -1, -1, b, d]);
a = c - 1;
b = d - 1;
},
insertionStatic = function diffview__opcodes_insertionStatic() {
table[two[b]][1] = table[two[b]][1] - 1;
fix([
"insert", -1, -1, b, b + 1
]);
a = c - 1;
b = d;
},
replacement = function diffview__opcodes_replacement() {
do {
table[one[c]][0] = table[one[c]][0] - 1;
table[two[d]][1] = table[two[d]][1] - 1;
c = c + 1;
d = d + 1;
} while (c < lena && d < lenb && table[one[c]][1] > 0 && table[two[d]][0] > 0);
fix(["replace", a, c, b, d]);
a = c - 1;
b = d - 1;
},
replaceUniques = function diffview__opcodes_replaceUniques() {
do {
table[one[c]][0] = table[one[c]][0] - 1;
c = c + 1;
d = d + 1;
} while (c < lena && d < lenb && table[one[c]][1] < 1 && table[two[d]][0] < 1);
fix(["replace", a, c, b, d]);
a = c - 1;
b = d - 1;
};
// * First Pass, account for lines from first file
// * build the table from the second file
do {
if (options.diffspaceignore === true) {
two[b] = two[b].replace(/\s+/g, "");
}
if (table[two[b]] === undefined) {
table[two[b]] = [0, 1];
} else {
table[two[b]][1] = table[two[b]][1] + 1;
}
b = b + 1;
} while (b < lenb);
// * Second Pass, account for lines from second file
// * build the table from the first file
lena = one.length;
a = 0;
do {
if (options.diffspaceignore === true) {
one[a] = one[a].replace(/\s+/g, "");
}
if (table[one[a]] === undefined) {
table[one[a]] = [1, 0];
} else {
table[one[a]][0] = table[one[a]][0] + 1;
}
a = a + 1;
} while (a < lena);
a = 0;
b = 0;
// find all equality... differences are what's left over solve only for the
// second set test removing reverse test removing undefined checks for table
// refs
do {
c = a;
d = b;
if (one[a] === two[b]) {
equality();
} else if (table[one[a]][1] < 1 && table[two[b]][0] < 1) {
replaceUniques();
} else if (table[one[a]][1] < 1 && one[a + 1] !== two[b + 2]) {
deletion();
} else if (table[two[b]][0] < 1 && one[a + 2] !== two[b + 1]) {
insertion();
} else if (table[one[a]][0] - table[one[a]][1] === 1 && one[a + 1] !== two[b + 2]) {
deletionStatic();
} else if (table[two[b]][1] - table[two[b]][0] === 1 && one[a + 2] !== two[b + 1]) {
insertionStatic();
} else if (one[a + 1] === two[b]) {
deletion();
} else if (one[a] === two[b + 1]) {
insertion();
} else {
replacement();
}
a = a + 1;
b = b + 1;
} while (a < lena && b < lenb);
if (lena - a === lenb - b) {
if (one[a] === two[b]) {
fix(["equal", a, lena, b, lenb]);
} else {
fix(["replace", a, lena, b, lenb]);
}
} else if (a < lena) {
fix(["delete", a, lena, -1, -1]);
} else if (b < lenb) {
fix(["insert", -1, -1, b, lenb]);
}
return codes;
};
if (Array.isArray(options.source) === false && typeof options.source !== "string") {
return "Error: source value is not a string or array!";
}
if (Array.isArray(options.diff) === false && typeof options.diff !== "string") {
return "Error: diff value is not a string or array!";
}
opcodes = codeBuild();
//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 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 = b + 1) {
output.push(str[b].replace(tabFix, ""));
}
return output;
},
htmlfix = function diffview__report_htmlfix(item) {
return item.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
},
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];
}()),
whiteout = function diffview__report_charcomp_whiteout(whitediff) {
var spacetest = (/<((em)|(pd))>\u0020+<\/((em)|(pd))>/),
crtest = (/<((em)|(pd))>\r+<\/((em)|(pd))>/);
if (spacetest.test(whitediff) === true) {
return whitediff;
}
if (crtest.test(whitediff) === true) {
return whitediff.replace(/\s+/, "(carriage return)");
}
return whitediff.replace(/\s+/, "(white space differences)");
},
//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 = x + 1) {
for (y = start; y < max; y = 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];
}
//package the output
if (dataA[y] === dataB[x]) {
if (dataA[y - 1] === dataB[x - 1] && x !== start) {
x = x - 1;
y = y - 1;
}
if (options.diffspaceignore === true && ((whitetest.test(dataA[y - 1]) === true && y - start > 0) || (whitetest.test(dataB[x - 1]) === true && x - start > 0))) {
whitespace = true;
}
return [x, y, 0, whitespace];
}
if (dataA[x] === dataB[y]) {
if (dataA[x - 1] === dataB[y - 1] && x !== start) {
x = x - 1;
y = y - 1;
}
if (options.diffspaceignore === true && ((whitetest.test(dataA[x - 1]) === true && x - start > 0) || (whitetest.test(dataB[y - 1]) === true && y - start > 0))) {
whitespace = true;
}
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 = errorout - 1;
//diff for tabs
if (tabFix !== "" && cleanedA.length !== cleanedB.length && cleanedA.replace(tabFix, "") === cleanedB.replace(tabFix, "") && options.diffspaceignore === false) {
errorout = 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 = 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 = 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] = currentdiff[0] + 1;
} while (currentdiff[1] > currentdiff[0]);
} else {
do {
dataB.unshift("");
currentdiff[0] = 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 = 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 = 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>")
.replace(/<pd>\s+<\/pd>/g, whiteout),
dataB
.join("")
.replace(regStart, "<pd>")
.replace(regEnd, "</pd>")
.replace(/<pd>\s+<\/pd>/g, whiteout)
];
}
return [
dataA
.join("")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(regStart, "<em>")
.replace(regEnd, "</em>")
.replace(/<em>\s+<\/em>/g, whiteout),
dataB
.join("")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(regStart, "<em>")
.replace(regEnd, "</em>")
.replace(/<em>\s+<\/em>/g, whiteout)
];
};
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'>");
}
} else {
foldstart = 0;
}
for (a = 0; a < opcodesLength; a = 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;
if (foldstart > -1 && options.diffcli === false) {
data[0][foldstart] = data[0][foldstart].replace("xxx", foldcount);
}
if (options.diffcli === true) {
if (foldstart > 49 && change === "equal") {
break;
}
if (options.diffspaceignore === true && change === "replace" && baseTextArray[baseStart] !== undefined && newTextArray[newStart] !== undefined && baseTextArray[baseStart].replace(/\s+/g, "") === newTextArray[newStart].replace(/\s+/g, "")) {
change = "equal";
} else if (change !== "equal") {
if (a > 0 && opcodes[a - 1][0] === "equal") {
foldcount = options.context;
if ((ntest === true || change === "insert") && (options.diffspaceignore === false || (/^(\s+)$/g).test(newTextArray[newStart]) === false)) {
foldstart = foldstart + 1;
if (options.api === "dom") {
data.push("</li><li><h3>Line: ");
data.push(opcodes[a - 1][2] + 1);
data.push("</h3>");
} else {
data.push("");
data.push("\u001b[36mLine: " + (opcodes[a - 1][2] + 1) + "\u001b[39m");
}
if (foldcount > 0) {
do {
if (newStart - foldcount > -1) {
if (options.api === "dom") {
data.push("<p>");
data.push(htmlfix(newTextArray[newStart - foldcount]));
data.push("</p>");
} else {
data.push(newTextArray[newStart - foldcount]);
}
}
foldcount = foldcount - 1;
} while (foldcount > 0);
}
} else {
foldstart = foldstart + 1;
if (options.api === "dom") {
data.push("</li><li><h3>Line: ");
data.push(baseStart + 1);
data.push("</h3>");
} else {
data.push("");
data.push("\u001b[36mLine: " + (baseStart + 1) + "\u001b[39m");
}
if (foldcount > 0) {
do {
if (baseStart - foldcount > -1) {
if (options.api === "dom") {
data.push("<p>");
data.push(htmlfix(newTextArray[newStart - foldcount]));
data.push("</p>");
} else {
data.push(baseTextArray[baseStart - foldcount]);
}
}
foldcount = foldcount - 1;
} while (foldcount > 0);
}
}
} else if (a < 1) {
if (options.api === "dom") {
data.push("</li><li><h3>Line: 1</h3>");
} else {
data.push("");
data.push("\u001b[36mLine: 1\u001b[39m");
}
foldstart = foldstart + 1;
}
foldcount = 0;
if ((ntest === true || change === "insert") && (options.diffspaceignore === false || (/^(\s+)$/g).test(newTextArray[newStart]) === false)) {
do {
if (options.api === "dom") {
data.push("<ins>");
data.push(htmlfix(newTextArray[newStart