User:A930913/vada/diffview.js

/***
 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 <[email protected]> */
/* 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
 *
 * Arguments:
 * - baseTextLines: the array of strings that was used as the base
 *       text input to SequenceMatcher
 * - newTextLines: the array of strings that was used as the new
 *       text input to SequenceMatcher
 * - baseTextName: the title to be displayed above the base text
 *       listing in the diff view; defaults to "Base Text"
 * - newTextName: the title to be displayed above the new text
 *       listing in the diff view; defaults to "New Text"
 * - contextSize: the number of lines of context to show around
 *       differences; by default, all lines are shown
 * - inline: if not true, a side-by-side diff view is generated
 *       (default); if true, an inline diff view is generated
 * - tchar: the character(s) comprising a code indentation; this
 *          defaults to an empty string
 * - tsize: the number of tchar characters to comprise a single code
 *       indentation; this defaults to 1 if tchar is not an empty string
 */
var diffview = function diffview(args) {
        "use strict";
        //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,
            baseTextLines = (typeof args.baseTextLines === "string") ? args.baseTextLines : "",
            newTextLines  = (typeof args.newTextLines === "string") ? args.newTextLines : "",
            baseTextName  = (typeof args.baseTextName === "string") ? args.baseTextName : "Base Source",
            newTextName   = (typeof args.newTextName === "string") ? args.newTextName : "New Source",
            context       = ((/^([0-9]+)$/).test(args.contextSize)) ? Number(args.contextSize) : -1,
            tsize         = ((/^([0-9]+)$/).test(args.tsize)) ? Number(args.tsize) : 4,
            tchar         = (typeof args.tchar === "string") ? args.tchar : " ",
            inline        = (args.inline === true) ? true : false,
            //tab is a construct of a standard indentation for code
            tab           = (function diffview__tab() {
                var a      = 0,
                    output = [];
                if (tchar === "") {
                    return "";
                }
                for (a = 0; a < tsize; a += 1) {
                    output.push(tchar);
                }
                return output.join("");
            }()),
            //translates source code from a string to an array by
            //splitting on line breaks
            stringAsLines = function diffview__stringAsLines(str) {
                var lfpos     = str.indexOf("\n"),
                    crpos     = str.indexOf("\r"),
                    linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r",
                    lines     = "";
                if (linebreak === "\n") {
                    str = str.replace(/\r/g, "");
                } else {
                    str = str.replace(/\n/g, "");
                }
                lines = str.replace(/\&/g, "&amp;").replace(/\&#lt;/g, "$#l" + "t;").replace(/\&#gt;/g, "$#g" + "t;").replace(/</g, "$#l" + "t;").replace(/>/g, "$#g" + "t;");
                return lines.split(linebreak);
            },
            //array representation of base source
            baseTextArray = stringAsLines(baseTextLines),
            //array representation of new source
            newTextArray  = stringAsLines(newTextLines),
            //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] && (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 (baseTextLines === "" || newTextLines === "") {
                    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           = [
                    [], [], [], []
                ],
                baseStart      = 0,
                baseEnd        = 0,
                newStart       = 0,
                newEnd         = 0,
                rowcnt         = 0,
                jump           = 0,
                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,
                        c             = 0,
                        d             = 0,
                        e             = 0,
                        dataA         = [],
                        dataB         = [],
                        cleanedA      = lineA.replace(/\&#160;/g, " ").replace(/\&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt\;/g, "<").replace(/&gt\;/g, ">").replace(/\$#lt\;/g, "<").replace(/\$#gt\;/g, ">"),
                        cleanedB      = lineB.replace(/\&#160;/g, " ").replace(/\&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt\;/g, "<").replace(/&gt\;/g, ">").replace(/\$#lt\;/g, "<").replace(/\$#gt\;/g, ">"),
                        dataMinLength = 0,
                        matchNextB    = [],
                        mBTest        = false,
                        matchNextA    = [],
                        mATest        = false,
                        lenComp       = [],
                        regStart      = (/_pdiffdiff\_/g),
                        regEnd        = (/_epdiffdiff\_/g),
                        strStart      = "_pdiff" + "diff_",
                        strEnd        = "_epdiff" + "diff_",
                        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
                            ];
                        }());
                    if (cleanedA === cleanedB) {
                        return [
                            lineA, lineB
                        ];
                    }
                    errorout -= 1;
                    if (tabFix !== "" && cleanedA.length !== cleanedB.length && cleanedA.replace(tabFix, "") === cleanedB.replace(tabFix, "")) {
                        errorout += 1;
                        return [
                            (tabdiff[0] + tabdiff[2]).replace(/&/g, "&amp;").replace(/</g, "&l" + "t;").replace(/>/g, "&g" + "t;").replace(regStart, "<em>").replace(regEnd, "</em>"), (tabdiff[1] + tabdiff[3]).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(regStart, "<em>").replace(regEnd, "</em>")
                        ];
                    }
                    dataA         = cleanedA.split("");
                    dataB         = cleanedB.split("");
                    dataMinLength = Math.min(dataA.length, dataB.length);
                    for (b = 0; b < dataMinLength; b += 1) {
                        if (dataA[b] === undefined || dataB[b] === undefined) {
                            break;
                        }
                        if (dataA[b] !== dataB[b]) {
                            mBTest = false;
                            mATest = false;
                            lenComp.push(dataMinLength);
                            lenComp.push(dataMinLength);
                            dataA[b] = strStart + dataA[b];
                            dataB[b] = strStart + dataB[b];
                            errorout += 1;
                            for (c = b; c < dataMinLength; c += 1) {
                                if (mBTest === false) {
                                    for (d = c; d < dataMinLength; d += 1) {
                                        if (dataA[c] === dataB[d]) {
                                            if (c === b) {
                                                c -= 1;
                                            }
                                            matchNextB.push(c - 1);
                                            matchNextB.push(d - 1);
                                            mBTest = true;
                                            break;
                                        }
                                    }
                                    if (c === dataMinLength - 1 && d === dataMinLength) {
                                        mBTest = true;
                                    }
                                }
                                if (mATest === false) {
                                    for (e = c; e < dataMinLength; e += 1) {
                                        if (dataB[c] === dataA[e]) {
                                            if (c === b) {
                                                c -= 1;
                                            }
                                            matchNextA.push(e - 1);
                                            matchNextA.push(c - 1);
                                            mATest = true;
                                            break;
                                        }
                                    }
                                    if (c === dataMinLength - 1 && e === dataMinLength) {
                                        mATest = true;
                                    }
                                }
                                if (mBTest === true && mATest === true) {
                                    if (e < d) {
                                        lenComp.pop();
                                        lenComp.pop();
                                        lenComp.push(matchNextA[0]);
                                        lenComp.push(matchNextA[1]);
                                    } else if (d < e) {
                                        lenComp.pop();
                                        lenComp.pop();
                                        lenComp.push(matchNextB[0]);
                                        lenComp.push(matchNextB[1]);
                                    } else if (d === e && d < dataMinLength) {
                                        lenComp.pop();
                                        lenComp.pop();
                                        lenComp.push(matchNextB[0]);
                                        lenComp.push(matchNextB[1]);
                                    }
                                    break;
                                }
                            }
                            if (lenComp[0] === dataMinLength || lenComp[1] === dataMinLength) {
                                if (dataA[b].replace(regStart, "") === dataB[dataB.length - 1]) {
                                    dataA[b]                = strStart + strEnd + dataA[b].replace(regStart, "");
                                    dataB[dataB.length - 1] = strEnd + dataB[dataB.length - 1];
                                } else if (dataB[b].replace(regStart, "") === dataA[dataA.length - 1]) {
                                    dataB[b]                = strStart + strEnd + dataB[b].replace(regStart, "");
                                    dataA[dataA.length - 1] = strEnd + dataA[dataA.length - 1];
                                } else {
                                    dataA.push(strEnd);
                                    dataB.push(strEnd);
                                }
                                break;
                            }
                            if (dataA[lenComp[0]] === dataB[b].substring(strStart.length)) {
                                if (dataA[lenComp[0]] === dataB[b].substring(strStart.length)) {
                                    dataA[lenComp[0]] = strEnd + dataA[lenComp[0]];
                                } else {
                                    dataA[lenComp[0]] = dataA[lenComp[0]] + strEnd;
                                }
                                if (lenComp[1] === b) {
                                    dataB[lenComp[1]] = strStart + strEnd + dataB[lenComp[1]].replace(regStart, "");
                                } else {
                                    dataB[lenComp[1]] = strEnd + dataB[lenComp[1]];
                                }
                            } else if (dataB[lenComp[1]] === dataA[b].substring(strStart.length)) {
                                if (dataB[lenComp[1]] === dataA[b].substring(strStart.length)) {
                                    dataB[lenComp[1]] = strEnd + dataB[lenComp[1]];
                                } else {
                                    dataB[lenComp[1]] = dataB[lenComp[1]] + strEnd;
                                }
                                if (lenComp[0] === b) {
                                    dataA[lenComp[0]] = strStart + strEnd + dataA[lenComp[0]].replace(regStart, "");
                                } else {
                                    dataA[lenComp[0]] = strEnd + dataA[lenComp[0]];
                                }
                            } else {
                                if (lenComp[1] > lenComp[0] && dataA[lenComp[1] + 1] === dataB[lenComp[1] + 1]) {
                                    if (dataA[lenComp[1]] === dataB[lenComp[1]].replace(regEnd, "")) {
                                        dataB[lenComp[1]] = dataB[lenComp[1]].replace(regEnd, "");
                                        do {
                                            lenComp[1] -= 1;
                                        } while (dataA[lenComp[1]] === dataB[lenComp[1]]);
                                        dataB[lenComp[1]] = dataB[lenComp[1]] + strEnd;
                                    }
                                    dataA[lenComp[1]] = dataA[lenComp[1]] + strEnd;
                                    lenComp[0]        = lenComp[1];
                                } else if (regEnd.test(dataA[lenComp[0]]) === false) {
                                    dataA[lenComp[0]] = dataA[lenComp[0]] + strEnd;
                                }
                                if (lenComp[0] > lenComp[1] && dataB[lenComp[0] + 1] === dataA[lenComp[0] + 1]) {
                                    if (dataB[lenComp[0]] === dataA[lenComp[0]].replace(regEnd, "")) {
                                        dataA[lenComp[0]] = dataA[lenComp[0]].replace(regEnd, "");
                                        do {
                                            lenComp[0] -= 1;
                                        } while (dataB[lenComp[0]] === dataA[lenComp[0]]);
                                        dataA[lenComp[0]] = dataA[lenComp[0]] + strEnd;
                                    }
                                    dataB[lenComp[0]] = dataB[lenComp[0]] + strEnd;
                                    lenComp[1]        = lenComp[0];
                                } else if (regEnd.test(dataB[lenComp[1]]) === false) {
                                    dataB[lenComp[1]] = dataB[lenComp[1]] + strEnd;
                                }
                            }
                            if (lenComp[1] - lenComp[0] > 0) {
                                for (c = (lenComp[1] - lenComp[0]) + b; c > b; c -= 1) {
                                    dataA.splice(0, 0, "");
                                }
                            }
                            if (lenComp[0] - lenComp[1] > 0) {
                                for (c = (lenComp[0] - lenComp[1]) + b; c > b; c -= 1) {
                                    dataB.splice(0, 0, "");
                                }
                            }
                            b             = Math.max(lenComp[0], lenComp[1]);
                            dataMinLength = Math.min(dataA.length, dataB.length);
                            matchNextB.pop();
                            matchNextB.pop();
                            matchNextA.pop();
                            matchNextA.pop();
                            lenComp.pop();
                            lenComp.pop();
                        }
                    }
                    if (b < Math.max(dataA.length, dataB.length) && regEnd.test(dataA[dataA.length - 1]) === false && regEnd.test(dataB[dataB.length - 1]) === false) {
                        errorout += 1;
                        if (dataA.length < dataB.length) {
                            dataA.push(strStart);
                            dataA.push(strEnd);
                            dataB[b] = strStart + dataB[b];
                            dataB.push(strEnd);
                        } else {
                            dataB.push(strStart);
                            dataB.push(strEnd);
                            dataA[b] = strStart + dataA[b];
                            dataA.push(strEnd);
                        }
                    }
                    return [
                        dataA.join("").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(regStart, "<em>").replace(regEnd, "</em>"), dataB.join("").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(regStart, "<em>").replace(regEnd, "</em>")
                    ];
                };
            if (inline === true) {
                node.push("<h3 class='texttitle'>");
                node.push(baseTextName);
                node.push(" vs. ");
                node.push(newTextName);
                node.push("</h3><ol class='count'>");
            } else {
                data[0].push("<div class='diff-left'><h3 class='texttitle'>");
                data[0].push(baseTextName);
                data[0].push("</h3><ol class='count'>");
                data[2].push("<div class='diff-right'><h3 class='texttitle'>");
                data[2].push(newTextName);
                data[2].push("</h3><ol class='count' onmousedown='pd.colSliderGrab(this);' 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 context collapsing for the output, if needed
                    if (isNaN(context) === false && context > -1 && opcodes.length > 1 && ((a > 0 && i === context) || (a === 0 && i === 0)) && change === "equal") {
                        ctest = false;
                        jump  = rowcnt - ((a === 0 ? 1 : 2) * context);
                        if (jump > 1) {
                            data[0].push("<li>...</li>");
                            if (inline === false) {
                                data[1].push("<li class='skip'>&#10;</li>");
                            }
                            data[2].push("<li>...</li>");
                            data[3].push("<li class='skip'>&#10;</li>");
                            baseStart += jump;
                            newStart  += jump;
                            i         += jump - 1;
                            if (a + 1 === opcodes.length) {
                                break;
                            }
                        }
                    } else if (change !== "equal") {
                        diffline += 1;
                    }
                    if (baseTextArray[baseStart] === newTextArray[newStart]) {
                        change = "equal";
                    } else if (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] && newTab[newStart] !== baseTab[baseStart] && (typeof baseTextArray[baseStart - 1] !== "string" || newTab[newStart] !== baseTab[baseStart - 1])) {
                            ntest = true;
                        }
                    }
                    //this is the final of the three primary components
                    //this is where the output is built
                    if (inline === true) {
                        if (ntest === true || change === "insert") {
                            data[0].push("<li class='empty'>&#8203;&#10;</li>");
                            data[2].push("<li>");
                            data[2].push(newStart + 1);
                            data[2].push("&#10;</li>");
                            data[3].push("<li class='insert'>");
                            data[3].push(newTextArray[newStart]);
                            data[3].push("&#10;</li>");
                        } else if (btest === true || change === "delete") {
                            data[0].push("<li>");
                            data[0].push(baseStart + 1);
                            data[0].push("</li>");
                            data[2].push("<li class='empty'>&#8203;&#10;</li>");
                            data[3].push("<li class='delete'>");
                            data[3].push(baseTextArray[baseStart]);
                            data[3].push("&#10;</li>");
                        } else if (change === "replace") {
                            if (baseTextArray[baseStart] !== newTextArray[newStart]) {
                                if (baseTextArray[baseStart] === "") {
                                    charcompOutput = [
                                        "", newTextArray[newStart]
                                    ];
                                } else if (newTextArray[newStart] === "") {
                                    charcompOutput = [
                                        baseTextArray[baseStart], ""
                                    ];
                                } else if (baseStart < baseEnd && newStart < newEnd) {
                                    charcompOutput = charcomp(baseTextArray[baseStart], newTextArray[newStart]);
                                }
                            }
                            if (baseStart < baseEnd) {
                                data[0].push("<li>");
                                data[0].push(baseStart + 1);
                                data[0].push("</li>");
                                data[2].push("<li class='empty'>&#8203;&#10;</li>");
                                data[3].push("<li class='delete'>");
                                if (newStart < newEnd) {
                                    data[3].push(charcompOutput[0]);
                                } else {
                                    data[3].push(baseTextArray[baseStart]);
                                }
                                data[3].push("&#10;</li>");
                            }
                            if (newStart < newEnd) {
                                data[0].push("<li class='empty'>&#8203;&#10;</li>");
                                data[2].push("<li>");
                                data[2].push(newStart + 1);
                                data[2].push("</li>");
                                data[3].push("<li class='insert'>");
                                if (baseStart < baseEnd) {
                                    data[3].push(charcompOutput[1]);
                                } else {
                                    data[3].push(newTextArray[newStart]);
                                }
                                data[3].push("&#10;</li>");
                            }
                        } else if (baseStart < baseEnd || newStart < newEnd) {
                            data[0].push("<li>");
                            data[0].push(baseStart + 1);
                            data[0].push("</li>");
                            data[2].push("<li>");
                            data[2].push(newStart + 1);
                            data[2].push("</li>");
                            data[3].push("<li class='");
                            data[3].push(change);
                            data[3].push("'>");
                            data[3].push(baseTextArray[baseStart]);
                            data[3].push("&#10;</li>");
                        }
                        if (btest === true) {
                            baseStart += 1;
                            btest     = false;
                        } else if (ntest === true) {
                            newStart += 1;
                            ntest    = false;
                        } else {
                            baseStart += 1;
                            newStart  += 1;
                        }
                    } else {
                        if (btest === false && ntest === false && typeof baseTextArray[baseStart] === "string" && typeof newTextArray[newStart] === "string") {
                            if (baseTextArray[baseStart] === "" && newTextArray[newStart] !== "") {
                                change = "insert";
                            }
                            if (newTextArray[newStart] === "" && baseTextArray[baseStart] !== "") {
                                change = "delete";
                            }
                            if (change === "replace" && baseStart < baseEnd && newStart < newEnd && baseTextArray[baseStart] !== newTextArray[newStart]) {
                                charcompOutput = charcomp(baseTextArray[baseStart], newTextArray[newStart]);
                            } else {
                                charcompOutput = [];
                            }
                            if (baseStart === data[0][data[0].length - 2] - 1 || newStart === data[2][data[2].length - 2] - 1) {
                                repeat = true;
                            }
                            if (repeat === false) {
                                if (baseStart < baseEnd) {
                                    if (baseTextArray[baseStart] === "") {
                                        data[0].push("<li class='empty'>&#10;");
                                    } else {
                                        data[0].push("<li>" + (baseStart + 1));
                                    }
                                    data[0].push("</li>");
                                    data[1].push("<li class='");
                                    if (newStart >= newEnd) {
                                        data[1].push("delete");
                                    } else if (baseTextArray[baseStart] === "" && newTextArray[newStart] !== "") {
                                        data[1].push("empty");
                                    } else {
                                        data[1].push(change);
                                    }
                                    data[1].push("'>");
                                    if (charcompOutput.length === 2) {
                                        data[1].push(charcompOutput[0]);
                                    } else {
                                        data[1].push(baseTextArray[baseStart]);
                                    }
                                    data[1].push("&#10;</li>");
                                } else if (ctest === true) {
                                    data[0].push("<li class='empty'>&#8203;&#10;</li>");
                                    data[1].push("<li class='empty'>&#8203;</li>");
                                }
                                if (newStart < newEnd) {
                                    if (newTextArray[newStart] === "") {
                                        data[2].push("<li class='empty'>&#10;");
                                    } else {
                                        data[2].push("<li>" + (newStart + 1));
                                    }
                                    data[2].push("</li>");
                                    data[3].push("<li class='");
                                    if (baseStart >= baseEnd) {
                                        data[3].push("insert");
                                    } else if (newTextArray[newStart] === "" && baseTextArray[baseStart] !== "") {
                                        data[3].push("empty");
                                    } else {
                                        data[3].push(change);
                                    }
                                    data[3].push("'>");
                                    if (charcompOutput.length === 2) {
                                        data[3].push(charcompOutput[1]);
                                    } else {
                                        data[3].push(newTextArray[newStart]);
                                    }
                                    data[3].push("&#10;</li>");
                                } else if (ctest === true) {
                                    data[2].push("<li class='empty'>&#8203;&#10;</li>");
                                    data[3].push("<li class='empty'>&#8203;</li>");
                                }
                            } else {
                                repeat = false;
                            }
                            if (baseStart < baseEnd) {
                                baseStart += 1;
                            }
                            if (newStart < newEnd) {
                                newStart += 1;
                            }
                        } else if (btest === true || (typeof baseTextArray[baseStart] === "string" && typeof newTextArray[newStart] !== "string")) {
                            if (baseStart !== data[0][data[0].length - 2] - 1) {
                                data[0].push("<li>");
                                data[0].push(baseStart + 1);
                                data[0].push("</li>");
                                data[1].push("<li class='delete'>");
                                data[1].push(baseTextArray[baseStart]);
                                data[1].push("&#10;</li>");
                                data[2].push("<li class='empty'>&#8203;&#10;</li>");
                                data[3].push("<li class='empty'>&#8203;</li>");
                            }
                            btest     = false;
                            baseStart += 1;
                        } else if (ntest === true || (typeof baseTextArray[baseStart] !== "string" && typeof newTextArray[newStart] === "string")) {
                            if (newStart !== data[2][data[2].length - 2] - 1) {
                                data[0].push("<li class='empty'>&#8203;&#10;</li>");
                                data[1].push("<li class='empty'>&#8203;</li>");
                                data[2].push("<li>");
                                data[2].push(newStart + 1);
                                data[2].push("</li>");
                                data[3].push("<li class='insert'>");
                                data[3].push(newTextArray[newStart]);
                                data[3].push("&#10;</li>");
                            }
                            ntest    = false;
                            newStart += 1;
                        }
                    }
                }
            }
            node.push(data[0].join(""));
            node.push("</ol><ol class=");
            if (inline === true) {
                node.push("'count'>");
            } else {
                node.push("'data'>");
                node.push(data[1].join(""));
                node.push("</ol></div>");
            }
            node.push(data[2].join(""));
            node.push("</ol><ol class='data'>");
            node.push(data[3].join(""));
            if (inline === true) {
                node.push("</ol>");
            } else {
                node.push("</ol></div>");
            }
            node.push("<p class='author'>Diff view written by <a href='http://prettydiff.com/'>Pretty Diff</a>.</p></div>");
            return [
                node.join("").replace(/li class='equal'><\/li/g, "li class='equal'>&#10;</li").replace(/\$#gt;/g, "&gt;").replace(/\$#lt;/g, "&lt;").replace(/\%#lt;/g, "$#lt;").replace(/\%#gt;/g, "$#gt;"), errorout, diffline
            ];
        }());
    };

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.