From ea2263ff29d96280ee6dc9139b8b4260ce9b8fad Mon Sep 17 00:00:00 2001 From: Jamie Peabody Date: Sun, 12 May 2013 19:52:47 +0100 Subject: [PATCH] New inner-line diff algorithm. Added method 'unmarkup' to clear markup. Added option: vpcolor. Fixed autoresize and resize. --- doc/index.html | 7 +- editor/editor.css | 4 +- editor/editor.js | 21 +- examples/{example2.html => ajax.html} | 0 examples/example3.html | 56 --- examples/lhs_long.txt | 1 - examples/rhs_long.txt | 1 - examples/{example1.html => simple.html} | 0 examples/{example4.html => size.html} | 18 +- lib/mergely.css | 2 + lib/mergely.js | 464 +++++++++++++----------- 11 files changed, 283 insertions(+), 291 deletions(-) rename examples/{example2.html => ajax.html} (100%) delete mode 100644 examples/example3.html delete mode 100644 examples/lhs_long.txt delete mode 100644 examples/rhs_long.txt rename examples/{example1.html => simple.html} (100%) rename examples/{example4.html => size.html} (88%) diff --git a/doc/index.html b/doc/index.html index 36d98e3..fe4c606 100644 --- a/doc/index.html +++ b/doc/index.html @@ -113,6 +113,8 @@ $(document).ready(function () {
bgcolor
The background color that mergely fills the margin canvas with. Defaults to '#eeeeee'
+
vwcolor
+
The margin/viewport indicator color. Defaults to 'rgba(0, 0, 200, 0.5)'
fadein
A jQuery fade-in value to enable the editor to fade in. Set to empty string to disable. Defaults to 'fast'
@@ -159,10 +161,13 @@ $(document).ready(function () {
$(selector).mergely('clear', side)
Clears the editor contents.
+
$(selector).mergely('unmarkup')
+
Clears the editor markup.
+
$(selector).mergely('search', side, text)
Search the editor for text. Repeating the call will find the next available token.
-
$(selector).resize()
+
$(selector).mergely('resize')
Resize the editor.
$(selector).mergely('update')
diff --git a/editor/editor.css b/editor/editor.css index e4a24e8..9df6030 100755 --- a/editor/editor.css +++ b/editor/editor.css @@ -6,10 +6,9 @@ body { margin: 0px; font-family: "trebuchet ms"; } #info h3, #info p { display: none; } #info button { visibility: hidden; font-size: 0.75em; } #about, #editor, #settings { float: right !important; margin: 6px 5px 0 5px; } -#settings { } #settings img { display: inline-block; height: 16px; vertical-align: middle; padding-bottom: 3px; } -.toolbar { display: none; margin: 0px; background: #e2e8ec url(../../images/toolbar_bg.png); border-bottom: 1px solid #A7BCC8; line-height: 14px; } +.toolbar { display: none; margin: 0px; background: #e2e8ec url(../../images/toolbar_bg.png); line-height: 15px; } .title { display: none; font-weight: bold; } .toolbar .ui-button { width: 1em; } .toolbar input[type=text] { float: left; border: 1px solid #888; font-size: 12px; font-weight: bold; font-family: tahoma; letter-spacing: 1px; width: 150px; color: #444; margin-right: 2px; } @@ -29,3 +28,4 @@ body { margin: 0px; font-family: "trebuchet ms"; } #dialog-settings input[type=text] { width: 70px; border: 1px solid #444; float:right; } #dialog-settings label.checkbox { width: auto; } #dialog-settings fieldset { -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; border: 1px solid #ccc; width: 222px; padding-right: 15px; } +#mergely-resizer { position: fixed; bottom: 0; top: 73px; width: 100%;} diff --git a/editor/editor.js b/editor/editor.js index d2d2692..4f33d2d 100755 --- a/editor/editor.js +++ b/editor/editor.js @@ -93,26 +93,26 @@ $(document).ready(function () { ignorews: parameters.get('ws', false), lcs: parameters.get('lcs', true), sidebar: parameters.get('sb', true), - height: function(h) { - return h - 100; - }, loaded: function() { - $('.toolbar, .title').fadeIn('fast'); + $('.toolbar').fadeIn('fast'); $('button').css({'visibility':'visible'}); }, resized: function() { - var lhsx = $('#compare-editor-lhs .CodeMirror-gutter').offset().left + $('#compare-editor-lhs .CodeMirror-gutter').width() + 1; - var rhsx = $('#compare-editor-rhs .CodeMirror-gutter').offset().left + $('#compare-editor-rhs .CodeMirror-gutter').width() + 1 - $('#lhs-toolbar').width(); + var lhsx = $('.mergely-margin:first-child').width(); + var rhsx = lhsx + $('#compare-editor-rhs .CodeMirror').width() + 25 - $('#lhs-toolbar').width(); $('#lhs-toolbar, #title-lhs').css({'position':'relative', 'left':lhsx}); $('#rhs-toolbar, #title-rhs').css({'position':'relative', 'left':rhsx}); $('#title-rhs').css({'left':rhsx}); }, + height: 'auto', + width: 'auto', cmsettings: { mode: 'text/plain', lineWrapping: parameters.get('wrap') || false, readOnly: (key == '4qsmsDyb') || parameters.get('ro') } }); + if (key.length == 8) { $.when( $.ajax({ @@ -293,6 +293,7 @@ $(document).ready(function () { var readOnly = $('#compare').mergely('cm', 'lhs').getOption('readOnly') || $('#compare').mergely('cm', 'rhs').getOption('readOnly'); var lcs = $('#compare').mergely('options').lcs; var sidebar = $('#compare').mergely('options').sidebar; + var ignoreunchanged = $('#compare').mergely('options').ignoreunchanged; $.each(conf, function(key, item){ $(item.id).val(item.getColor()); }); $('#ignore-ws').prop('checked', ignorews); @@ -300,6 +301,9 @@ $(document).ready(function () { $('#readonly').prop('checked', readOnly); $('#lcs').prop('checked', lcs); $('#sidebar').prop('checked', sidebar); + /* + $('#ignoreunchanged').prop('checked', ignoreunchanged); + */ $('#settings').click(function(){ dlg.dialog({ @@ -317,6 +321,9 @@ $(document).ready(function () { var readonly = $('#readonly').prop('checked'); var lcs = $('#lcs').prop('checked'); var sidebar = $('#sidebar').prop('checked'); + /* + var ignoreunchanged = $('#ignoreunchanged').prop('checked'); + */ var text = '.mergely.a.rhs.start { border-top: 1px solid ' + aborder + '; }\n\ .mergely.a.lhs.start.end,\n\ @@ -337,7 +344,7 @@ $(document).ready(function () { .mergely.ch.d.lhs { background-color: ' + dbg + '; text-decoration: line-through; color: #888; }'; $('').appendTo('head'); - $('#compare').mergely('options', {ignorews: ignorews, lcs: lcs, sidebar: sidebar, fgcolor:{a:aborder,c:cborder,d:dborder}}); + $('#compare').mergely('options', {ignorews: ignorews, lcs: lcs, sidebar: sidebar, ignoreunchanged: ignoreunchanged, fgcolor:{a:aborder,c:cborder,d:dborder}}); $('#compare').mergely('cm', 'lhs').setOption('lineWrapping', wraplines); $('#compare').mergely('cm', 'rhs').setOption('lineWrapping', wraplines); $('#compare').mergely('cm', 'lhs').setOption('readOnly', readonly); diff --git a/examples/example2.html b/examples/ajax.html similarity index 100% rename from examples/example2.html rename to examples/ajax.html diff --git a/examples/example3.html b/examples/example3.html deleted file mode 100644 index ba9f012..0000000 --- a/examples/example3.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - Mergely - Simple Example - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - diff --git a/examples/lhs_long.txt b/examples/lhs_long.txt deleted file mode 100644 index d155599..0000000 --- a/examples/lhs_long.txt +++ /dev/null @@ -1 +0,0 @@ -the quick red fox jumped over the hairy dog on a cold, wet, and windy day in January diff --git a/examples/rhs_long.txt b/examples/rhs_long.txt deleted file mode 100644 index 2c3f250..0000000 --- a/examples/rhs_long.txt +++ /dev/null @@ -1 +0,0 @@ -the quick brown fox jumped over the lazy dog on a cold, wet, and windy day in December diff --git a/examples/example1.html b/examples/simple.html similarity index 100% rename from examples/example1.html rename to examples/simple.html diff --git a/examples/example4.html b/examples/size.html similarity index 88% rename from examples/example4.html rename to examples/size.html index c8dc82e..4110515 100755 --- a/examples/example4.html +++ b/examples/size.html @@ -57,24 +57,36 @@ This example demonstrates how to enable line wrapping lineWrapping: true, } }); + $('#compare5').mergely({ + width: 'auto', + height: 200, + autoresize: false, + cmsettings: { + readOnly: false, + lineWrapping: true, + } + }); + $.ajax({ type: 'GET', async: true, dataType: 'text', - url: 'lhs_long.txt', + url: 'lhs.txt', success: function (response) { $('#compare1').mergely('lhs', response); $('#compare2').mergely('lhs', response); $('#compare3').mergely('lhs', response); $('#compare4').mergely('lhs', response); + $('#compare5').mergely('lhs', response); } }); $.ajax({ type: 'GET', async: true, dataType: 'text', - url: 'rhs_long.txt', + url: 'rhs.txt', success: function (response) { $('#compare1').mergely('rhs', response); $('#compare2').mergely('rhs', response); $('#compare3').mergely('rhs', response); $('#compare4').mergely('rhs', response); + $('#compare5').mergely('rhs', response); } }); }); @@ -91,6 +103,8 @@ This example demonstrates how to enable line wrapping
+ +
diff --git a/lib/mergely.css b/lib/mergely.css index 47ea4e7..5053d0b 100644 --- a/lib/mergely.css +++ b/lib/mergely.css @@ -1,3 +1,4 @@ + /* required */ .mergely-column textarea { width: 80px; height: 200px; } .mergely-column { float: left; } @@ -15,6 +16,7 @@ .mergely.a.lhs.start.end, .mergely.a.rhs.end { border-bottom: 1px solid #a3d1ff; } .mergely.a.rhs { background-color: #ddeeff; } +.mergely.a.lhs.start.end.first { border-bottom: 0; border-top: 1px solid #a3d1ff; } .mergely.d.lhs { background-color: #edc0c0; } .mergely.d.lhs.end, diff --git a/lib/mergely.js b/lib/mergely.js index 64e4675..6f6a2bc 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -12,6 +12,38 @@ Mgly.Timer = function(){ self.start(); } +Mgly.ChangeExpression = new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/); + +Mgly.DiffParser = function(diff) { + var changes = []; + var change_id = 0; + // parse diff + var diff_lines = diff.split(/\n/); + for (var i = 0; i < diff_lines.length; ++i) { + if (diff_lines[i].length == 0) continue; + var change = {}; + var test = Mgly.ChangeExpression.exec(diff_lines[i]); + if (test == null) continue; + // lines are zero-based + var fr = test[1].split(','); + change['lhs-line-from'] = fr[0] - 1; + if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1; + else change['lhs-line-to'] = fr[1] - 1; + var to = test[3].split(','); + change['rhs-line-from'] = to[0] - 1; + if (to.length == 1) change['rhs-line-to'] = to[0] - 1; + else change['rhs-line-to'] = to[1] - 1; + // TODO: optimize for changes that are adds/removes + if (change['lhs-line-from'] < 0) change['lhs-line-from'] = 0; + if (change['lhs-line-to'] < 0) change['lhs-line-to'] = 0; + if (change['rhs-line-from'] < 0) change['rhs-line-from'] = 0; + if (change['rhs-line-to'] < 0) change['rhs-line-to'] = 0; + change['op'] = test[2]; + changes[change_id++] = change; + } + return changes; +} + Mgly.sizeOf = function(obj) { var size = 0, key; for (key in obj) { @@ -21,87 +53,50 @@ Mgly.sizeOf = function(obj) { } Mgly.LCS = function(x, y) { - //http://en.wikipedia.org/wiki/Longest_common_subsequence_problem - this.length = this._lcs(x, y); - var C = []; - x = x.split(''); - y = y.split(''); - x.unshift('');//add an empty element to the start [1..m] - y.unshift('');//add an empty element to the start [1..n] - this.C = C; - this.x = x; - this.y = y; - var i = 0; - var j = 0; - for (i = 0; i < x.length + 1; ++i) { - C[i] = []; - for (j = 0; j < y.length + 1; ++j) C[i][j] = 0; - } - for (i = 1; i < x.length + 1; ++i) { - for (j = 1; j < y.length + 1; ++j) { - if (x[i - 1] == y[j - 1]) C[i][j] = C[i - 1][j - 1] + 1; - else C[i][j] = Math.max( C[i][j - 1], C[i - 1][j] ); - } - } - this.ready = 1; + this.x = x.replace(/ /g, '\n'); + this.y = y.replace(/ /g, '\n'); } jQuery.extend(Mgly.LCS.prototype, { clear: function() { this.ready = 0; }, diff: function(added, removed) { - this._diff(this.x.length - 1, this.y.length - 1, added, removed); - }, - _diff: function(i, j, added, removed){ - var x = this.x; - var y = this.y; - var C = this.C; - if (this.ready && i > 0 && j > 0 && (x[i] == y[j])) { - this._diff(i - 1, j - 1, added, removed); + var d = new Mgly.diff(this.x, this.y, retain_lines = true, ignore_ws = false); + var changes = Mgly.DiffParser(d.normal_form()); + var li = 0, ri = 0, lj = 0, rj = 0, r0 = 0, l0 = 0; + for (var i = 0; i < changes.length; ++i) { + var change = changes[i]; + + // find the starting index of the line + li += d.lhs_lines.slice(lj, change['lhs-line-from']).join(' ').length; + // if the starting index is more than last time, add 1 (space) + if (li > l0) li += 1; + // get the index of the the span of the change + lj = change['lhs-line-to'] + 1; + // get the changed text + var lchange = d.lhs_lines.slice(change['lhs-line-from'], lj).join(' '); + // output the changed index and text + removed(li, li + lchange.length); + // increment the starting index beyond the last change + li += lchange.length + 1; + // remember last index + l0 = li; + + // find the starting index of the line + ri += d.rhs_lines.slice(rj, change['rhs-line-from']).join(' ').length; + // if the starting index is more than last time, add 1 (space) + if (ri > r0) ri += 1; + // get the index of the the span of the change + rj = change['rhs-line-to'] + 1; + // get the changed text + var rchange = d.rhs_lines.slice(change['rhs-line-from'], rj).join(' '); + // output the changed index and text + added(ri, ri + rchange.length); + // increment the starting index beyond the last change + ri += rchange.length + 1; + // remember last index + r0 = ri; } - else { - if (j > 0 && (i == 0 || (C[i][j - 1] >= C[i-1][j]))) { - this._diff(i, j - 1, added, removed); - if (added) added(j - 1, y[j]); - } - else if (i > 0 && (j == 0 || (C[i][j - 1] < C[i - 1][j]))) { - this._diff(i - 1, j, added, removed); - if (removed) removed(i - 1, x[i]); - } - } - }, - _lcs: function(string1, string2) { - // init max value - var longest = 0; - // init 2D array with 0 - var table = Array(string1.length); - for(a = 0; a <= string1.length; a++){ - table[a] = Array(string2.length); - for(b = 0; b <= string2.length; b++){ - table[a][b] = 0; - } - } - // fill table - for(var i = 0; i < string1.length; i++) { - for(var j = 0; j < string2.length; j++) { - if(string1[i]==string2[j]) { - if(table[i][j] == 0){ - table[i+1][j+1] = 1; - } - else { - table[i+1][j+1] = table[i][j] + 1; - } - if(table[i+1][j+1] > longest){ - longest = table[i+1][j+1]; - } - } - else { - table[i+1][j+1] = 0; - } - } - } - return longest; } }); - Mgly.diff = function(lhs, rhs, retain_lines, ignore_ws) { this.diff_codes = {}; this.max_code = 0; @@ -365,11 +360,12 @@ jQuery.extend(Mgly.mergely.prototype, { resize_timeout: 500, change_timeout: 150, fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f'}, - _bgcolor: '#eee', + bgcolor: '#eee', + vpcolor: 'rgba(0, 0, 200, 0.5)', lhs: function(setValue) { }, rhs: function(setValue) { }, loaded: function() { }, - _auto_height: function(h) { return h - 20; }, + //_auto_height: function(h) { return h - 20; }, _auto_width: function(w) { return w; }, resize: function() { var w = jQuery(el).parent().width(); @@ -382,7 +378,8 @@ jQuery.extend(Mgly.mergely.prototype, { this.editor_width = w; } if (this.height == 'auto') { - h = this._auto_height(h); + //h = this._auto_height(h); + h = jQuery(el).parent().height(); } else { h = this.height; @@ -403,7 +400,7 @@ jQuery.extend(Mgly.mergely.prototype, { } if (this.resized) this.resized(); }, - _debug: '', //scroll,draw,calc,diff,markup,change + _debug: 'change', //scroll,draw,calc,diff,markup,change resized: function() { } }; var cmsettings = { @@ -455,21 +452,23 @@ jQuery.extend(Mgly.mergely.prototype, { this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); }); // resize - var sz_timeout1 = null; - var sz = function() { - //self.em_height = null; //recalculate - if (self.settings.resize) self.settings.resize(); - self.editor[self.id + '-lhs'].refresh(); - self.editor[self.id + '-rhs'].refresh(); - self._changing(self.id + '-lhs', self.id + '-rhs'); - } - jQuery(window).resize( - function () { - if (sz_timeout1) clearTimeout(sz_timeout1); - sz_timeout1 = setTimeout(sz, self.settings.resize_timeout); + if (this.settings.autoresize) { + var sz_timeout1 = null; + var sz = function() { + //self.em_height = null; //recalculate + if (self.settings.resize) self.settings.resize(); + self.editor[self.id + '-lhs'].refresh(); + self.editor[self.id + '-rhs'].refresh(); + self._changing(self.id + '-lhs', self.id + '-rhs'); } - ); - sz(); + jQuery(window).resize( + function () { + if (sz_timeout1) clearTimeout(sz_timeout1); + sz_timeout1 = setTimeout(sz, self.settings.resize_timeout); + } + ); + sz(); + } }, unbind: function() { if (this.changed_timeout != null) clearTimeout(this.changed_timeout); @@ -492,6 +491,10 @@ jQuery.extend(Mgly.mergely.prototype, { update: function() { this._changing(this.id + '-lhs', this.id + '-rhs'); }, + unmarkup: function() { + console.log('UNMARKUP'); + this._clear(); + }, scrollTo: function(side, num) { var le = this.editor[this.id + '-lhs']; var re = this.editor[this.id + '-rhs']; @@ -507,7 +510,9 @@ jQuery.extend(Mgly.mergely.prototype, { options: function(opts) { if (opts) { jQuery.extend(this.settings, opts); - this.resize(); + if (this.settings.autoresize) { + this.resize(); + } } else { return this.settings; @@ -575,7 +580,9 @@ jQuery.extend(Mgly.mergely.prototype, { var height = this.settings.editor_height; var width = this.settings.editor_width; this.changed_timeout = null; - this.change_funcs = []; + this.chfns = {}; + this.chfns[this.id + '-lhs'] = []; + this.chfns[this.id + '-rhs'] = []; this.prev_query = []; this.cursor = []; this._skipscroll = {}; @@ -720,22 +727,27 @@ jQuery.extend(Mgly.mergely.prototype, { }, this.settings.change_timeout); }, _changed: function(editor_name1, editor_name2) { + this._clear(); + this._diff(editor_name1, editor_name2); + }, + _clear: function() { var self = this; for (var name in this.editor) { if (!this.editor.hasOwnProperty(name)) continue; var editor = this.editor[name]; + var fns = self.chfns[name]; // clear editor changes editor.operation(function() { var timer = new Mgly.Timer(); for (var i = 0, l = editor.lineCount(); i < l; ++i) { editor.removeLineClass(i, 'background'); } - for (var i = 0; i < self.change_funcs.length; ++i) { - var edid = editor.getDoc().id; - var change = self.change_funcs[i]; - if (change.doc.id != edid) continue; + for (var i = 0; i < fns.length; ++i) { + //var edid = editor.getDoc().id; + var change = fns[i]; + //if (change.doc.id != edid) continue; if (change.lines.length) { - self.trace('change', 'clear text', edid, change.lines[0].text); + self.trace('change', 'clear text', change.lines[0].text); } change.clear(); } @@ -743,8 +755,28 @@ jQuery.extend(Mgly.mergely.prototype, { self.trace('change', 'clear time', timer.stop()); }); } - self.change_funcs = []; - this._diff(editor_name1, editor_name2); + self.chfns[name] = []; + + var ex = this._draw_info(this.id + '-lhs', this.id + '-rhs'); + var ctx_lhs = ex.clhs.get(0).getContext('2d'); + var ctx_rhs = ex.crhs.get(0).getContext('2d'); + var ctx = ex.dcanvas.getContext('2d'); + + ctx_lhs.beginPath(); + ctx_lhs.fillStyle = this.settings.bgcolor; + ctx_lhs.strokeStyle = '#888'; + ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height); + ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height); + + ctx_rhs.beginPath(); + ctx_rhs.fillStyle = this.settings.bgcolor; + ctx_rhs.strokeStyle = '#888'; + ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height); + ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height); + + ctx.beginPath(); + ctx.fillStyle = '#fff'; + ctx.fillRect(0, 0, this.draw_mid_width, ex.visible_page_height); }, _diff: function(editor_name1, editor_name2) { var lhs = this.editor[editor_name1].getValue(); @@ -752,7 +784,7 @@ jQuery.extend(Mgly.mergely.prototype, { var timer = new Mgly.Timer(); var d = new Mgly.diff(lhs, rhs, false, this.settings.ignorews); this.trace('change', 'diff time', timer.stop()); - this.changes = this._parse_diff(editor_name1, editor_name2, d.normal_form()); + this.changes = Mgly.DiffParser(d.normal_form()); this.trace('change', 'parse time', timer.stop()); this._calculate_offsets(editor_name1, editor_name2, this.changes); this.trace('change', 'offsets time', timer.stop()); @@ -828,7 +860,8 @@ jQuery.extend(Mgly.mergely.prototype, { console.error('canvas width is 0'); return; } - this.draw_rhs_max = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas').width() - 0.5; //24.5; + this.draw_mid_width = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas').width(); + this.draw_rhs_max = this.draw_mid_width - 0.5; //24.5; this.draw_lhs_width = 5; this.draw_rhs_width = 5; this.trace('calc', 'change offsets calculated', {top_offset: top_offset, lhs_min: this.draw_lhs_min, rhs_max: this.draw_rhs_max, lhs_width: this.draw_lhs_width, rhs_width: this.draw_rhs_width}); @@ -1052,62 +1085,14 @@ jQuery.extend(Mgly.mergely.prototype, { var rhs_stop = { line: -1, ch: -1 }; var lcs = new Mgly.LCS(lhs_line, rhs_line); - var max = Math.max(lhs_line.length, rhs_line.length); - if (max == 0) max = 1; - var percent = ((1.0)*lcs.length / max) * 100; - if (percent < 10) lcs.clear(); lcs.diff( - added = function (index, c) { - if (rhs_start.ch < 0) { - rhs_start.line = k; - rhs_start.ch = index; - rhs_stop.line = k; - rhs_stop.ch = index; - } - else if (index == rhs_stop.ch + 1) { - rhs_stop.ch = index; - } - else { - if ((rhs_start.ch >= 0) && (rhs_stop.ch >= rhs_start.ch)) { - rhs_stop.ch += 1; - marktext.push([red, {line:rhs_start.line, ch:rhs_start.ch}, {line:rhs_stop.line, ch:rhs_stop.ch}, {className: 'mergely ch a rhs'}]); - } - //reset - rhs_start.ch = -1; - rhs_stop.ch = -1; - if (c != '\n') this.added(index, c);//call again - } + function (from, to) {//added + marktext.push([red, {line:k, ch:from}, {line:k, ch:to}, {className: 'mergely ch a rhs'}]); }, - removed = function (index, c) { - if (lhs_start.ch < 0) { - lhs_start.line = j; - lhs_start.ch = index; - lhs_stop.line = j; - lhs_stop.ch = index; - } - else if (index == lhs_stop.ch + 1) { - lhs_stop.ch = index; - } - else { - if ((lhs_start.ch >= 0) && (lhs_stop.ch >= lhs_start.ch)) { - lhs_stop.ch += 1; - marktext.push([led, {line:lhs_start.line, ch:lhs_start.ch}, {line:lhs_stop.line, ch:lhs_stop.ch}, {className: 'mergely ch d lhs'}]); - } - //reset - lhs_start.ch = -1; - lhs_stop.ch = -1; - if (c != '\n') this.removed(index, c);//call again - } + removed = function (from, to) {//removed + marktext.push([led, {line:j, ch:from}, {line:j, ch:to}, {className: 'mergely ch d lhs'}]); } ); - if ((rhs_start.ch >= 0) && (rhs_stop.ch >= rhs_start.ch)) { - rhs_stop.ch += 1; - marktext.push([red, {line:rhs_start.line, ch:rhs_start.ch}, {line:rhs_stop.line, ch:rhs_stop.ch}, {className: 'mergely ch a rhs'}]); - } - if ((lhs_start.ch >= 0) && (lhs_stop.ch >= lhs_start.ch)) { - lhs_stop.ch += 1; - marktext.push([led, {line:lhs_start.line, ch:lhs_start.ch}, {line:lhs_stop.line, ch:lhs_stop.ch}, {className: 'mergely ch d lhs'}]); - } } } } @@ -1119,7 +1104,7 @@ jQuery.extend(Mgly.mergely.prototype, { for (var i = 0; i < marktext.length; ++i) { var m = marktext[i]; if (m[0].doc.id != led.getDoc().id) continue; - self.change_funcs.push(m[0].markText(m[1], m[2], m[3])); + self.chfns[self.id + '-lhs'].push(m[0].markText(m[1], m[2], m[3])); } }); red.operation(function() { @@ -1127,8 +1112,7 @@ jQuery.extend(Mgly.mergely.prototype, { for (var i = 0; i < marktext.length; ++i) { var m = marktext[i]; if (m[0].doc.id != red.getDoc().id) continue; - var mark = m[0].markText(m[1], m[2], m[3]); - self.change_funcs.push(m[0].markText(m[1], m[2], m[3])); + self.chfns[self.id + '-rhs'].push(m[0].markText(m[1], m[2], m[3])); } }); this.trace('change', 'LCS markup time', timer.stop()); @@ -1165,16 +1149,44 @@ jQuery.extend(Mgly.mergely.prototype, { { line: change[side + '-line-from'], ch: 0 }, { line: change[side + '-line-to'], ch: line[side].text.length }); - if (change['op'] == 'c') { - ed[oside].replaceRange( text, - { line: change[oside + '-line-from'], ch: 0 }, - { line: change[oside + '-line-to'], ch: line[oside].text.length }); + if (side == 'rhs') { + if (change['op'] == 'c') { + ed[oside].replaceRange( text, + { line: change[oside + '-line-from'], ch: 0 }, + { line: change[oside + '-line-to'], ch: line[oside].text.length }); + } + else if (change['op'] == 'a') { + ed[oside].replaceRange( text + '\n', + { line: change[oside + '-line-from'], ch: 0 }, + { line: change[oside + '-line-to'], ch: 0 }); + } + else {// 'd' + var from = parseInt(change[oside + '-line-from']); + var to = parseInt(change[oside + '-line-to']); + for (var i = to; i >= from; --i) { + ed[oside].removeLine(i); + } + } } - else {// 'a' or 'd' - var from = parseInt(change[oside + '-line-from']); - var to = parseInt(change[oside + '-line-to']); - for (var i = to; i >= from; --i) { - ed[oside].removeLine(i); + else { // lhs + if (change['op'] == 'c') { + ed[oside].replaceRange( text, + { line: change[oside + '-line-from'], ch: 0 }, + { line: change[oside + '-line-to'], ch: line[oside].text.length }); + } + else if (change['op'] == 'a') { + var from = parseInt(change[oside + '-line-from']); + var to = parseInt(change[oside + '-line-to']); + for (var i = to; i >= from; --i) { + ed[oside].removeLine(i); + } + } + else {// 'd' + var from = parseInt(change[oside + '-line-from']); + var to = parseInt(change[oside + '-line-to']); + ed[oside].replaceRange( '\n' + text, + { line: change[oside + '-line-from'], ch: line[oside].text.length }, + { line: change[oside + '-line-to'], ch: line[oside].text.length }); } } //reset @@ -1184,51 +1196,61 @@ jQuery.extend(Mgly.mergely.prototype, { }); this.trace('change', 'markup buttons time', timer.stop()); }, - _draw_diff: function(editor_name1, editor_name2, changes) { + _draw_info: function(editor_name1, editor_name2) { var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height(); var gutter_height = jQuery(this.editor[editor_name1].getScrollerElement()).children(':first-child').height(); - var visible_page_ratio = (visible_page_height / gutter_height); - var margin_ratio = (visible_page_height / gutter_height); - var lhs_scroller = jQuery(this.editor[editor_name1].getScrollerElement()); - var rhs_scroller = jQuery(this.editor[editor_name2].getScrollerElement()); - var lhs_lines = this.editor[editor_name1].lineCount(); - var rhs_lines = this.editor[editor_name2].lineCount(); - - this.trace('draw', 'visible_page_height', visible_page_height); - this.trace('draw', 'gutter_height', gutter_height); - this.trace('draw', 'visible_page_ratio', visible_page_ratio); - this.trace('draw', 'lhs-scroller-top', lhs_scroller.scrollTop()); - this.trace('draw', 'rhs-scroller-top', rhs_scroller.scrollTop()); - var dcanvas = document.getElementById(editor_name1 + '-' + editor_name2 + '-canvas'); if (dcanvas == undefined) throw 'Failed to find: ' + editor_name1 + '-' + editor_name2 + '-canvas'; - jQuery.each(jQuery.find('#' + this.id + ' canvas'), function () { - jQuery(this).get(0).height = visible_page_height; - }); var clhs = jQuery('#' + this.id + '-lhs-margin'); var crhs = jQuery('#' + this.id + '-rhs-margin'); - clhs.unbind('click'); - crhs.unbind('click'); - var mcanvas_lhs = clhs.get(0); - var mcanvas_rhs = crhs.get(0); - var lhs_xyoffset = jQuery(clhs).offset(); - var rhs_xyoffset = jQuery(crhs).offset(); - - var ctx = dcanvas.getContext('2d'); + return { + visible_page_height: visible_page_height, + gutter_height: gutter_height, + visible_page_ratio: (visible_page_height / gutter_height), + margin_ratio: (visible_page_height / gutter_height), + lhs_scroller: jQuery(this.editor[editor_name1].getScrollerElement()), + rhs_scroller: jQuery(this.editor[editor_name2].getScrollerElement()), + lhs_lines: this.editor[editor_name1].lineCount(), + rhs_lines: this.editor[editor_name2].lineCount(), + dcanvas: dcanvas, + clhs: clhs, + crhs: crhs, + lhs_xyoffset: jQuery(clhs).offset(), + rhs_xyoffset: jQuery(crhs).offset() + }; + }, + _draw_diff: function(editor_name1, editor_name2, changes) { + var ex = this._draw_info(editor_name1, editor_name2); + var mcanvas_lhs = ex.clhs.get(0); + var mcanvas_rhs = ex.crhs.get(0); + var ctx = ex.dcanvas.getContext('2d'); var ctx_lhs = mcanvas_lhs.getContext('2d'); var ctx_rhs = mcanvas_rhs.getContext('2d'); + this.trace('draw', 'visible_page_height', ex.visible_page_height); + this.trace('draw', 'gutter_height', ex.gutter_height); + this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio); + this.trace('draw', 'lhs-scroller-top', ex.lhs_scroller.scrollTop()); + this.trace('draw', 'rhs-scroller-top', ex.rhs_scroller.scrollTop()); + + jQuery.each(jQuery.find('#' + this.id + ' canvas'), function () { + jQuery(this).get(0).height = ex.visible_page_height; + }); + + ex.clhs.unbind('click'); + ex.crhs.unbind('click'); + ctx_lhs.beginPath(); - ctx_lhs.fillStyle = this.settings._bgcolor; + ctx_lhs.fillStyle = this.settings.bgcolor; ctx_lhs.strokeStyle = '#888'; - ctx_lhs.fillRect(0, 0, 6.5, visible_page_height); - ctx_lhs.strokeRect(0, 0, 6.5, visible_page_height); + ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height); + ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height); ctx_rhs.beginPath(); - ctx_rhs.fillStyle = this.settings._bgcolor; + ctx_rhs.fillStyle = this.settings.bgcolor; ctx_rhs.strokeStyle = '#888'; - ctx_rhs.fillRect(0, 0, 6.5, visible_page_height); - ctx_rhs.strokeRect(0, 0, 6.5, visible_page_height); + ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height); + ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height); var vp = this._get_viewport(editor_name1, editor_name2); for (var i = 0; i < changes.length; ++i) { @@ -1236,10 +1258,10 @@ jQuery.extend(Mgly.mergely.prototype, { this.trace('draw', change); // margin indicators - lhs_y_start = ((change['lhs-y-start'] + lhs_scroller.scrollTop()) * visible_page_ratio); - lhs_y_end = ((change['lhs-y-end'] + lhs_scroller.scrollTop()) * visible_page_ratio) + 1; - rhs_y_start = ((change['rhs-y-start'] + rhs_scroller.scrollTop()) * visible_page_ratio); - rhs_y_end = ((change['rhs-y-end'] + rhs_scroller.scrollTop()) * visible_page_ratio) + 1; + var lhs_y_start = ((change['lhs-y-start'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio); + var lhs_y_end = ((change['lhs-y-end'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1; + var rhs_y_start = ((change['rhs-y-start'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio); + var rhs_y_end = ((change['rhs-y-end'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1; this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end); ctx_lhs.beginPath(); @@ -1260,10 +1282,10 @@ jQuery.extend(Mgly.mergely.prototype, { continue; } - var lhs_y_start = change['lhs-y-start']; - var lhs_y_end = change['lhs-y-end']; - var rhs_y_start = change['rhs-y-start']; - var rhs_y_end = change['rhs-y-end']; + lhs_y_start = change['lhs-y-start']; + lhs_y_end = change['lhs-y-end']; + rhs_y_start = change['rhs-y-start']; + rhs_y_end = change['rhs-y-end']; var radius = 3; @@ -1343,34 +1365,34 @@ jQuery.extend(Mgly.mergely.prototype, { } // visible window feedback - ctx_lhs.fillStyle = 'rgba(0, 0, 200, 0.5)'; - ctx_rhs.fillStyle = 'rgba(0, 0, 200, 0.5)'; + ctx_lhs.fillStyle = this.settings.vpcolor; + ctx_rhs.fillStyle = this.settings.vpcolor; - var to = clhs.height() * visible_page_ratio; - var from = (lhs_scroller.scrollTop() / gutter_height) * clhs.height(); - this.trace('draw', 'cls.height', clhs.height()); - this.trace('draw', 'lhs_scroller.scrollTop()', lhs_scroller.scrollTop()); - this.trace('draw', 'gutter_height', gutter_height); - this.trace('draw', 'visible_page_ratio', visible_page_ratio); + var to = ex.clhs.height() * ex.visible_page_ratio; + var from = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height(); + this.trace('draw', 'cls.height', ex.clhs.height()); + this.trace('draw', 'lhs_scroller.scrollTop()', ex.lhs_scroller.scrollTop()); + this.trace('draw', 'gutter_height', ex.gutter_height); + this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio); this.trace('draw', 'from', from, 'to', to); ctx_lhs.fillRect(1.5, from, 4.5, to); ctx_rhs.fillRect(1.5, from, 4.5, to); - clhs.click(function (ev) { + ex.clhs.click(function (ev) { var y = ev.pageY - lhs_xyoffset.top - (to / 2); - var sto = Math.max(0, (y / mcanvas_lhs.height) * lhs_scroller.get(0).scrollHeight); - lhs_scroller.scrollTop(sto); + var sto = Math.max(0, (y / ex.mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight); + ex.lhs_scroller.scrollTop(sto); }); - crhs.click(function (ev) { + ex.crhs.click(function (ev) { var y = ev.pageY - rhs_xyoffset.top - (to / 2); - var sto = Math.max(0, (y / mcanvas_rhs.height) * rhs_scroller.get(0).scrollHeight); - rhs_scroller.scrollTop(sto); + var sto = Math.max(0, (y / ex.mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight); + ex.rhs_scroller.scrollTop(sto); }); }, trace: function(name) { if(this.settings._debug.indexOf(name) >= 0) { - arguments[0] = name+':'; + arguments[0] = name+':'; console.log([].slice.apply(arguments)); } }