--- /dev/null
+/***
+This is part of jsdifflib v1.0. <http://snowtide.com/jsdifflib>
+
+Copyright (c) 2007, Snowtide Informatics Systems, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Snowtide Informatics Systems nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+***/
+/* Author: Chas Emerick <cemerick@snowtide.com> */
+__whitespace = {" ":true, "\t":true, "\n":true, "\f":true, "\r":true};
+
+difflib = {
+ defaultJunkFunction: function (c) {
+ return c in __whitespace;
+ },
+
+ stripLinebreaks: function (str) { return str.replace(/^[\n\r]*|[\n\r]*$/g, ""); },
+
+ stringAsLines: function (str) {
+ var lfpos = str.indexOf("\n");
+ var crpos = str.indexOf("\r");
+ var linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r";
+
+ var lines = str.split(linebreak);
+ for (var i = 0; i < lines.length; i++) {
+ lines[i] = difflib.stripLinebreaks(lines[i]);
+ }
+
+ return lines;
+ },
+
+ // iteration-based reduce implementation
+ __reduce: function (func, list, initial) {
+ if (initial != null) {
+ var value = initial;
+ var idx = 0;
+ } else if (list) {
+ var value = list[0];
+ var idx = 1;
+ } else {
+ return null;
+ }
+
+ for (; idx < list.length; idx++) {
+ value = func(value, list[idx]);
+ }
+
+ return value;
+ },
+
+ // comparison function for sorting lists of numeric tuples
+ __ntuplecomp: function (a, b) {
+ var mlen = Math.max(a.length, b.length);
+ for (var i = 0; i < mlen; i++) {
+ if (a[i] < b[i]) return -1;
+ if (a[i] > b[i]) return 1;
+ }
+
+ return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1);
+ },
+
+ __calculate_ratio: function (matches, length) {
+ return length ? 2.0 * matches / length : 1.0;
+ },
+
+ // returns a function that returns true if a key passed to the returned function
+ // is in the dict (js object) provided to this function; replaces being able to
+ // carry around dict.has_key in python...
+ __isindict: function (dict) {
+ return function (key) { return key in dict; };
+ },
+
+ // replacement for python's dict.get function -- need easy default values
+ __dictget: function (dict, key, defaultValue) {
+ return key in dict ? dict[key] : defaultValue;
+ },
+
+ SequenceMatcher: function (a, b, isjunk) {
+ this.set_seqs = function (a, b) {
+ this.set_seq1(a);
+ this.set_seq2(b);
+ }
+
+ this.set_seq1 = function (a) {
+ if (a == this.a) return;
+ this.a = a;
+ this.matching_blocks = this.opcodes = null;
+ }
+
+ this.set_seq2 = function (b) {
+ if (b == this.b) return;
+ this.b = b;
+ this.matching_blocks = this.opcodes = this.fullbcount = null;
+ this.__chain_b();
+ }
+
+ this.__chain_b = function () {
+ var b = this.b;
+ var n = b.length;
+ var b2j = this.b2j = {};
+ var populardict = {};
+ for (var i = 0; i < b.length; i++) {
+ var elt = b[i];
+ if (elt in b2j) {
+ var indices = b2j[elt];
+ if (n >= 200 && indices.length * 100 > n) {
+ populardict[elt] = 1;
+ delete b2j[elt];
+ } else {
+ indices.push(i);
+ }
+ } else {
+ b2j[elt] = [i];
+ }
+ }
+
+ for (var elt in populardict)
+ delete b2j[elt];
+
+ var isjunk = this.isjunk;
+ var junkdict = {};
+ if (isjunk) {
+ for (var elt in populardict) {
+ if (isjunk(elt)) {
+ junkdict[elt] = 1;
+ delete populardict[elt];
+ }
+ }
+ for (var elt in b2j) {
+ if (isjunk(elt)) {
+ junkdict[elt] = 1;
+ delete b2j[elt];
+ }
+ }
+ }
+
+ this.isbjunk = difflib.__isindict(junkdict);
+ this.isbpopular = difflib.__isindict(populardict);
+ }
+
+ this.find_longest_match = function (alo, ahi, blo, bhi) {
+ var a = this.a;
+ var b = this.b;
+ var b2j = this.b2j;
+ var isbjunk = this.isbjunk;
+ var besti = alo;
+ var bestj = blo;
+ var bestsize = 0;
+ var j = null;
+
+ var j2len = {};
+ var nothing = [];
+ for (var i = alo; i < ahi; i++) {
+ var newj2len = {};
+ var jdict = difflib.__dictget(b2j, a[i], nothing);
+ for (var jkey in jdict) {
+ j = jdict[jkey];
+ if (j < blo) continue;
+ if (j >= bhi) break;
+ newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1;
+ if (k > bestsize) {
+ besti = i - k + 1;
+ bestj = j - k + 1;
+ bestsize = k;
+ }
+ }
+ j2len = newj2len;
+ }
+
+ while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
+ besti--;
+ bestj--;
+ bestsize++;
+ }
+
+ while (besti + bestsize < ahi && bestj + bestsize < bhi &&
+ !isbjunk(b[bestj + bestsize]) &&
+ a[besti + bestsize] == b[bestj + bestsize]) {
+ bestsize++;
+ }
+
+ while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
+ besti--;
+ bestj--;
+ bestsize++;
+ }
+
+ while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) &&
+ a[besti + bestsize] == b[bestj + bestsize]) {
+ bestsize++;
+ }
+
+ return [besti, bestj, bestsize];
+ }
+
+ this.get_matching_blocks = function () {
+ if (this.matching_blocks != null) return this.matching_blocks;
+ var la = this.a.length;
+ var lb = this.b.length;
+
+ var queue = [[0, la, 0, lb]];
+ var matching_blocks = [];
+ var alo, ahi, blo, bhi, qi, i, j, k, x;
+ while (queue.length) {
+ qi = queue.pop();
+ alo = qi[0];
+ ahi = qi[1];
+ blo = qi[2];
+ bhi = qi[3];
+ x = this.find_longest_match(alo, ahi, blo, bhi);
+ i = x[0];
+ j = x[1];
+ k = x[2];
+
+ if (k) {
+ matching_blocks.push(x);
+ if (alo < i && blo < j)
+ queue.push([alo, i, blo, j]);
+ if (i+k < ahi && j+k < bhi)
+ queue.push([i + k, ahi, j + k, bhi]);
+ }
+ }
+
+ matching_blocks.sort(difflib.__ntuplecomp);
+
+ var i1 = j1 = k1 = block = 0;
+ var non_adjacent = [];
+ for (var idx in matching_blocks) {
+ block = matching_blocks[idx];
+ i2 = block[0];
+ j2 = block[1];
+ k2 = block[2];
+ if (i1 + k1 == i2 && j1 + k1 == j2) {
+ k1 += k2;
+ } else {
+ if (k1) non_adjacent.push([i1, j1, k1]);
+ i1 = i2;
+ j1 = j2;
+ k1 = k2;
+ }
+ }
+
+ if (k1) non_adjacent.push([i1, j1, k1]);
+
+ non_adjacent.push([la, lb, 0]);
+ this.matching_blocks = non_adjacent;
+ return this.matching_blocks;
+ }
+
+ this.get_opcodes = function () {
+ if (this.opcodes != null) return this.opcodes;
+ var i = 0;
+ var j = 0;
+ var answer = [];
+ this.opcodes = answer;
+ var block, ai, bj, size, tag;
+ var blocks = this.get_matching_blocks();
+ for (var idx in blocks) {
+ block = blocks[idx];
+ ai = block[0];
+ bj = block[1];
+ size = block[2];
+ tag = '';
+ if (i < ai && j < bj) {
+ tag = 'replace';
+ } else if (i < ai) {
+ tag = 'delete';
+ } else if (j < bj) {
+ tag = 'insert';
+ }
+ if (tag) answer.push([tag, i, ai, j, bj]);
+ i = ai + size;
+ j = bj + size;
+
+ if (size) answer.push(['equal', ai, i, bj, j]);
+ }
+
+ return answer;
+ }
+
+ // this is a generator function in the python lib, which of course is not supported in javascript
+ // the reimplementation builds up the grouped opcodes into a list in their entirety and returns that.
+ this.get_grouped_opcodes = function (n) {
+ if (!n) n = 3;
+ var codes = this.get_opcodes();
+ if (!codes) codes = [["equal", 0, 1, 0, 1]];
+ var code, tag, i1, i2, j1, j2;
+ if (codes[0][0] == 'equal') {
+ code = codes[0];
+ tag = code[0];
+ i1 = code[1];
+ i2 = code[2];
+ j1 = code[3];
+ j2 = code[4];
+ codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2];
+ }
+ if (codes[codes.length - 1][0] == 'equal') {
+ code = codes[codes.length - 1];
+ tag = code[0];
+ i1 = code[1];
+ i2 = code[2];
+ j1 = code[3];
+ j2 = code[4];
+ codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)];
+ }
+
+ var nn = n + n;
+ var groups = [];
+ for (var idx in codes) {
+ code = codes[idx];
+ tag = code[0];
+ i1 = code[1];
+ i2 = code[2];
+ j1 = code[3];
+ j2 = code[4];
+ if (tag == 'equal' && i2 - i1 > nn) {
+ groups.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]);
+ i1 = Math.max(i1, i2-n);
+ j1 = Math.max(j1, j2-n);
+ }
+
+ groups.push([tag, i1, i2, j1, j2]);
+ }
+
+ if (groups && groups[groups.length - 1][0] == 'equal') groups.pop();
+
+ return groups;
+ }
+
+ this.ratio = function () {
+ matches = difflib.__reduce(
+ function (sum, triple) { return sum + triple[triple.length - 1]; },
+ this.get_matching_blocks(), 0);
+ return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
+ }
+
+ this.quick_ratio = function () {
+ var fullbcount, elt;
+ if (this.fullbcount == null) {
+ this.fullbcount = fullbcount = {};
+ for (var i = 0; i < this.b.length; i++) {
+ elt = this.b[i];
+ fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1;
+ }
+ }
+ fullbcount = this.fullbcount;
+
+ var avail = {};
+ var availhas = difflib.__isindict(avail);
+ var matches = numb = 0;
+ for (var i = 0; i < this.a.length; i++) {
+ elt = this.a[i];
+ if (availhas(elt)) {
+ numb = avail[elt];
+ } else {
+ numb = difflib.__dictget(fullbcount, elt, 0);
+ }
+ avail[elt] = numb - 1;
+ if (numb > 0) matches++;
+ }
+
+ return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
+ }
+
+ this.real_quick_ratio = function () {
+ var la = this.a.length;
+ var lb = this.b.length;
+ return _calculate_ratio(Math.min(la, lb), la + lb);
+ }
+
+ this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction;
+ this.a = this.b = null;
+ this.set_seqs(a, b);
+ }
+}
--- /dev/null
+/***
+This is part of jsdifflib v1.0. <http://snowtide.com/jsdifflib>
+
+Copyright (c) 2007, Snowtide Informatics Systems, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Snowtide Informatics Systems nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+***/
+/* Author: Chas Emerick <cemerick@snowtide.com> */
+table.diff {
+ border-collapse:collapse;
+ border:1px solid darkgray
+}
+table.diff tbody {
+ font-family:Courier, monospace
+}
+table.diff tbody th {
+ font-family:verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
+ background:#EED;
+ font-size:11px;
+ font-weight:normal;
+ border:1px solid #BBC;
+ color:#886;
+ padding:.3em .5em .1em 2em;
+ text-align:right;
+ vertical-align:top
+}
+table.diff thead {
+ border-bottom:1px solid #BBC;
+ background:#EFEFEF;
+ font-family:Verdana
+}
+table.diff thead th.texttitle {
+ text-align:left
+}
+table.diff tbody td {
+ padding:0px .4em;
+ padding-top:.4em;
+ vertical-align:top;
+}
+table.diff .empty {
+ background-color:#DDD;
+}
+table.diff .replace {
+ background-color:#FD8
+}
+table.diff .delete {
+ background-color:#E99;
+}
+table.diff .skip {
+ background-color:#EFEFEF;
+ border:1px solid #AAA;
+ border-right:1px solid #BBC;
+}
+table.diff .insert {
+ background-color:#9E9
+}
+table.diff th.author {
+ text-align:right;
+ border-top:1px solid #BBC;
+ background:#EFEFEF
+}
\ No newline at end of file
--- /dev/null
+/***
+This is part of jsdifflib v1.0. <http://snowtide.com/jsdifflib>
+
+Copyright (c) 2007, Snowtide Informatics Systems, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the Snowtide Informatics Systems nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+***/
+/* Author: Chas Emerick <cemerick@snowtide.com> */
+diffview = {
+ /**
+ * Builds and returns a visual diff view. The single parameter, `params', should contain
+ * the following values:
+ *
+ * - 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
+ * - opcodes: the array of arrays returned by SequenceMatcher.get_opcodes()
+ * - 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
+ * - viewType: if 0, a side-by-side diff view is generated (default); if 1, an inline diff view is
+ * generated
+ */
+ buildView: function (params) {
+ var baseTextLines = params.baseTextLines;
+ var newTextLines = params.newTextLines;
+ var opcodes = params.opcodes;
+ var baseTextName = params.baseTextName ? params.baseTextName : "Base Text";
+ var newTextName = params.newTextName ? params.newTextName : "New Text";
+ var contextSize = params.contextSize;
+ var inline = (params.viewType == 0 || params.viewType == 1) ? params.viewType : 0;
+
+ if (baseTextLines == null)
+ throw "Cannot build diff view; baseTextLines is not defined.";
+ if (newTextLines == null)
+ throw "Cannot build diff view; newTextLines is not defined.";
+ if (!opcodes)
+ throw "Canno build diff view; opcodes is not defined.";
+
+ function celt (name, clazz) {
+ var e = document.createElement(name);
+ e.className = clazz;
+ return e;
+ }
+
+ function telt (name, text) {
+ var e = document.createElement(name);
+ e.appendChild(document.createTextNode(text));
+ return e;
+ }
+
+ function ctelt (name, clazz, text) {
+ var e = document.createElement(name);
+ e.className = clazz;
+ e.appendChild(document.createTextNode(text));
+ return e;
+ }
+
+ var tdata = document.createElement("thead");
+ var node = document.createElement("tr");
+ tdata.appendChild(node);
+ if (inline) {
+ node.appendChild(document.createElement("th"));
+ node.appendChild(document.createElement("th"));
+ node.appendChild(ctelt("th", "texttitle", baseTextName + " vs. " + newTextName));
+ } else {
+ node.appendChild(document.createElement("th"));
+ node.appendChild(ctelt("th", "texttitle", baseTextName));
+ node.appendChild(document.createElement("th"));
+ node.appendChild(ctelt("th", "texttitle", newTextName));
+ }
+ tdata = [tdata];
+
+ var rows = [];
+ var node2;
+
+ /**
+ * Adds two cells to the given row; if the given row corresponds to a real
+ * line number (based on the line index tidx and the endpoint of the
+ * range in question tend), then the cells will contain the line number
+ * and the line of text from textLines at position tidx (with the class of
+ * the second cell set to the name of the change represented), and tidx + 1 will
+ * be returned. Otherwise, tidx is returned, and two empty cells are added
+ * to the given row.
+ */
+ function addCells (row, tidx, tend, textLines, change) {
+ if (tidx < tend) {
+ row.appendChild(telt("th", (tidx + 1).toString()));
+ row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
+ return tidx + 1;
+ } else {
+ row.appendChild(document.createElement("th"));
+ row.appendChild(celt("td", "empty"));
+ return tidx;
+ }
+ }
+
+ function addCellsInline (row, tidx, tidx2, textLines, change) {
+ row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString()));
+ row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString()));
+ row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
+ }
+
+ for (var idx = 0; idx < opcodes.length; idx++) {
+ code = opcodes[idx];
+ change = code[0];
+ var b = code[1];
+ var be = code[2];
+ var n = code[3];
+ var ne = code[4];
+ var rowcnt = Math.max(be - b, ne - n);
+ var toprows = [];
+ var botrows = [];
+ for (var i = 0; i < rowcnt; i++) {
+ // jump ahead if we've alredy provided leading context or if this is the first range
+ if (contextSize && opcodes.length > 1 && ((idx > 0 && i == contextSize) || (idx == 0 && i == 0)) && change=="equal") {
+ var jump = rowcnt - ((idx == 0 ? 1 : 2) * contextSize);
+ if (jump > 1) {
+ toprows.push(node = document.createElement("tr"));
+
+ b += jump;
+ n += jump;
+ i += jump - 1;
+ node.appendChild(telt("th", "..."));
+ if (!inline) node.appendChild(ctelt("td", "skip", ""));
+ node.appendChild(telt("th", "..."));
+ node.appendChild(ctelt("td", "skip", ""));
+
+ // skip last lines if they're all equal
+ if (idx + 1 == opcodes.length) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ toprows.push(node = document.createElement("tr"));
+ if (inline) {
+ if (change == "insert") {
+ addCellsInline(node, null, n++, newTextLines, change);
+ } else if (change == "replace") {
+ botrows.push(node2 = document.createElement("tr"));
+ if (b < be) addCellsInline(node, b++, null, baseTextLines, "delete");
+ if (n < ne) addCellsInline(node2, null, n++, newTextLines, "insert");
+ } else if (change == "delete") {
+ addCellsInline(node, b++, null, baseTextLines, change);
+ } else {
+ // equal
+ addCellsInline(node, b++, n++, baseTextLines, change);
+ }
+ } else {
+ b = addCells(node, b, be, baseTextLines, change);
+ n = addCells(node, n, ne, newTextLines, change);
+ }
+ }
+
+ for (var i = 0; i < toprows.length; i++) rows.push(toprows[i]);
+ for (var i = 0; i < botrows.length; i++) rows.push(botrows[i]);
+ }
+
+ rows.push(node = ctelt("th", "author", "diff view generated by "));
+ node.setAttribute("colspan", inline ? 3 : 4);
+ node.appendChild(node2 = telt("a", "jsdifflib"));
+ node2.setAttribute("href", "http://snowtide.com/jsdifflib");
+
+ tdata.push(node = document.createElement("tbody"));
+ for (var idx in rows) node.appendChild(rows[idx]);
+
+ node = celt("table", "diff" + (inline ? " inlinediff" : ""));
+ for (var idx in tdata) node.appendChild(tdata[idx]);
+ return node;
+ }
+}
\ No newline at end of file