From f1782d193379f01d0841e73e5a984f840bbc2e7f Mon Sep 17 00:00:00 2001 From: Kljh Date: Sat, 16 Aug 2014 15:55:47 +0100 Subject: [PATCH 1/8] =?UTF-8?q?Added=20keyboard=20shortcuts=20=C3=A0=20la?= =?UTF-8?q?=20WinMerge:=20Alt=20+=20Up=20or=20Down=20arrow=20:=20go=20to?= =?UTF-8?q?=20previous=20or=20next=20difference=20Alt=20+=20Left=20or=20Ri?= =?UTF-8?q?ght=20arrow=20:=20merge=20difference=20using=20right=20or=20lef?= =?UTF-8?q?t=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shift, Ctr, Alt or Meta keys can be used indifferntly (to cope with browser's idiosyncratics). Event handler is now attached to text editor. This is clearly to review (does not capture all page events and too intrusive). New state "current_diff" has been added. Need to scroll the editor to center it on currently active state (scrollToChange function to add). --- lib/mergely.js | 239 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 160 insertions(+), 79 deletions(-) diff --git a/lib/mergely.js b/lib/mergely.js index 1fe2c6d..d0f66f7 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -5,7 +5,7 @@ Mgly.Timer = function(){ self.start = function() { self.t0 = new Date().getTime(); } self.stop = function() { var t1 = new Date().getTime(); - var d = t1 - self.t0; + var d = t1 - self.t0; self.t0 = t1; return d; } @@ -172,11 +172,11 @@ jQuery.extend(Mgly.diff.prototype, { var change = 'c'; if (item.lhs_deleted_count == 0 && item.rhs_inserted_count > 0) change = 'a'; else if (item.lhs_deleted_count > 0 && item.rhs_inserted_count == 0) change = 'd'; - + if (item.lhs_deleted_count == 1) lhs_str = item.lhs_start + 1; else if (item.lhs_deleted_count == 0) lhs_str = item.lhs_start; else lhs_str = (item.lhs_start + 1) + ',' + (item.lhs_start + item.lhs_deleted_count); - + if (item.rhs_inserted_count == 1) rhs_str = item.rhs_start + 1; else if (item.rhs_inserted_count == 0) rhs_str = item.rhs_start; else rhs_str = (item.rhs_start + 1) + ',' + (item.rhs_start + item.rhs_inserted_count); @@ -369,7 +369,7 @@ jQuery.extend(Mgly.mergely.prototype, { Mgly.CodeMirrorDiffView = function(el, options) { CodeMirror.defineExtension('centerOnCursor', function() { var coords = this.cursorCoords(null, 'local'); - this.scrollTo(null, + this.scrollTo(null, (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2)); }); this.init(el, options); @@ -384,13 +384,15 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { lcs: true, sidebar: true, viewport: false, - ignorews: false, + ignorews: true, fadein: 'fast', - editor_width: '400px', + editor_width: '650px', editor_height: '400px', resize_timeout: 500, change_timeout: 150, - fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f'}, + current_diff: 0, // currently active difference, in 0 .. changes.length range. + fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color) + ca:'#4b73ff',cc:'#737373',cd:'#ff4f4f'}, // color for currently active difference (bright color) bgcolor: '#eee', vpcolor: 'rgba(0, 0, 200, 0.5)', lhs: function(setValue) { }, @@ -443,15 +445,15 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } this.lhs_cmsettings = {}; this.rhs_cmsettings = {}; - + // save this element for faster queries this.element = jQuery(el); - + // save options if there are any if (options && options.cmsettings) jQuery.extend(this.lhs_cmsettings, cmsettings, options.cmsettings, options.lhs_cmsettings); if (options && options.cmsettings) jQuery.extend(this.rhs_cmsettings, cmsettings, options.cmsettings, options.rhs_cmsettings); if (options) jQuery.extend(this.settings, options); - + // bind if the element is destroyed this.element.bind('destroyed', jQuery.proxy(this.teardown, this)); @@ -565,7 +567,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.prev_query[side] = query; } var cursor = this.cursor[this.id]; - + if (cursor[direction]()) { editor.setSelection(cursor.from(), cursor.to()); } @@ -612,7 +614,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } this.merge_rhs_button = jQuery(merge_rhs_button); this.merge_lhs_button = jQuery(merge_lhs_button); - + // create the textarea and canvas elements jQuery(this.element).append(jQuery('
')); jQuery(this.element).append(jQuery('
')); @@ -677,7 +679,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { sz(true); } //bind - + if (this.settings.lhs) { var setv = this.editor[this.id + '-lhs'].getDoc().setValue; this.settings.lhs(setv.bind(this.editor[this.id + '-lhs'].getDoc())); @@ -686,8 +688,77 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var setv = this.editor[this.id + '-rhs'].getDoc().setValue; this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc())); } + + // register WinMerge style key bindings + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key + var self = this; + el.addEventListener("keydown", function(evt) { + if (evt.defaultPrevented) + ;//return; // Should do nothing if the key event was already consumed. + + if (!evt.shiftKey && !evt.altKey && !evt.ctrlKey && !evt.metaKey) + return; // if none of Shift, Alt, Ctrl or Meta key is used then do nothing + + var shift_key = 16, alt_key = 17, ctrlKey = 18, metaKey = 91; // 13, 27 ? + var up_arrow = 38, down_arrow = 40, left_arrow = 37, right_arrow = 39; + if (!evt.key) { + // use keyCode as key + evt.key = evt.which; + } + + switch (evt.key) { + case "ArrowDown": + case down_arrow: + self.settings.current_diff++; + self._changed(self.id + '-lhs', self.id + '-rhs'); + break; + case "ArrowUp": + case up_arrow: + self.settings.current_diff--; + self.settings.current_diff = Math.max(self.settings.current_diff, 0); + self._changed(self.id + '-lhs', self.id + '-rhs'); + break; + case "ArrowLeft": + case left_arrow: + // !! recalculating the changes !! + var editor_name1 = self.id + '-lhs'; + var editor_name2 = self.id + '-rhs'; + var lhs = self.editor[editor_name1].getValue(); + var rhs = self.editor[editor_name2].getValue(); + var d = new Mgly.diff(lhs, rhs, self.settings); + var changes = Mgly.DiffParser(d.normal_form()); + + var change = changes[self.settings.current_diff]; + self._merge_change(change, "rhs", "lhs"); + break; + case "ArrowRight": + case right_arrow: + // !! recalculating the changes !! + var editor_name1 = self.id + '-lhs'; + var editor_name2 = self.id + '-rhs'; + var lhs = self.editor[editor_name1].getValue(); + var rhs = self.editor[editor_name2].getValue(); + var d = new Mgly.diff(lhs, rhs, self.settings); + var changes = Mgly.DiffParser(d.normal_form()); + + var change = changes[self.settings.current_diff]; + self._merge_change(change, "lhs", "rhs"); + break; + case "Enter": + // Do something for "enter" or "return" key press. + break; + case "Escape": + // Do something for "esc" key press. + break; + default: + return; // Quit when this doesn't handle the key event. + } + + // Consume the event for suppressing "double action". + //event.preventDefault(); + }); }, - + _scrolling: function(editor_name) { if (this._skipscroll[editor_name] === true) { // scrolling one side causes the other to event - ignore it @@ -702,23 +773,23 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var midline = this.editor[editor_name].coordsChar({left:0, top:this.midway}); var top_to = scroller.scrollTop(); var left_to = scroller.scrollLeft(); - + this.trace('scroll', 'side', editor_name); this.trace('scroll', 'midway', this.midway); this.trace('scroll', 'midline', midline); this.trace('scroll', 'top_to', top_to); this.trace('scroll', 'left_to', left_to); - + var editor_name1 = this.id + '-lhs'; var editor_name2 = this.id + '-rhs'; - + for (var name in this.editor) { if (!this.editor.hasOwnProperty(name)) continue; if (editor_name == name) continue; //same editor var this_side = editor_name.replace(this.id + '-', ''); var other_side = name.replace(this.id + '-', ''); var top_adjust = 0; - + // find the last change that is less than or within the midway point // do not move the rhs until the lhs end point is >= the rhs end point. var last_change = null; @@ -736,14 +807,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { force_scroll = true; } else { - top_adjust += - (change[this_side+'-y-end'] - change[this_side+'-y-start']) - + top_adjust += + (change[this_side+'-y-end'] - change[this_side+'-y-start']) - (change[other_side+'-y-end'] - change[other_side+'-y-start']); } } } } - + var vp = this.editor[name].getViewport(); var scroll = true; if (last_change) { @@ -761,7 +832,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { scroller.scrollTop(top_to - top_adjust).scrollLeft(left_to); } else this.trace('scroll', 'not scrolling other side'); - + if (this.settings.autoupdate) { var timer = new Mgly.Timer(); this._calculate_offsets(editor_name1, editor_name2, this.changes); @@ -814,12 +885,12 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); } 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'; @@ -831,7 +902,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { 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); @@ -903,16 +974,16 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var saveY = this.editor[editor_name1].getScrollInfo().top; // temporarily scroll to top this.editor[editor_name1].scrollTo(null, 0); - - // this is the distance from the top of the screen to the top of the + + // this is the distance from the top of the screen to the top of the // content of the first codemirror editor var topnode = jQuery('#' + this.id + ' .CodeMirror-measure').first(); var top_offset = topnode.offset().top - 4; if(!top_offset) return false; - + // restore editor's scroll position this.editor[editor_name1].scrollTo(null, saveY); - + this.draw_top_offset = 0.5 - top_offset; return true; }, @@ -942,10 +1013,10 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var lhschc = this.editor[editor_name1].charCoords({line: 0}); var rhschc = this.editor[editor_name2].charCoords({line: 0}); var vp = this._get_viewport(editor_name1, editor_name2); - + for (var i = 0; i < changes.length; ++i) { var change = changes[i]; - + if (!this.settings.sidebar && !this._is_change_in_view(vp, change)) { // if the change is outside the viewport, skip delete change['lhs-y-start']; @@ -958,7 +1029,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + var ls, le, rs, re; if (this.editor[editor_name1].getOption('lineWrapping') || this.editor[editor_name1].getOption('lineWrapping')) { // If using line-wrapping, we must get the height of the line @@ -969,7 +1040,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var tle = this.editor[editor_name1].cursorCoords({line: llt, ch: 0}, 'page'); var lhseh = this.editor[editor_name1].getLineHandle(llt); le = { top: tle.top, bottom: tle.top + lhseh.height }; - + var tls = this.editor[editor_name2].cursorCoords({line: rlf, ch: 0}, 'page'); var rhssh = this.editor[editor_name2].getLineHandle(rlf); rs = { top: tls.top, bottom: tls.top + rhssh.height }; @@ -980,24 +1051,24 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } else { // If not using line-wrapping, we can calculate the line position - ls = { - top: lhschc.top + llf * this.em_height, + ls = { + top: lhschc.top + llf * this.em_height, bottom: lhschc.bottom + llf * this.em_height + 2 }; le = { - top: lhschc.top + llt * this.em_height, + top: lhschc.top + llt * this.em_height, bottom: lhschc.bottom + llt * this.em_height + 2 }; rs = { - top: rhschc.top + rlf * this.em_height, + top: rhschc.top + rlf * this.em_height, bottom: rhschc.bottom + rlf * this.em_height + 2 }; re = { - top: rhschc.top + rlt * this.em_height, + top: rhschc.top + rlt * this.em_height, bottom: rhschc.bottom + rlt * this.em_height + 2 }; } - + if (change['op'] == 'a') { // adds (right), normally start from the end of the lhs, // except for the case when the start of the rhs is 0 @@ -1036,7 +1107,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }, _markup_changes: function (editor_name1, editor_name2, changes) { jQuery('.merge-button').remove(); // clear - + var self = this; var led = this.editor[editor_name1]; var red = this.editor[editor_name2]; @@ -1049,11 +1120,11 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + var clazz = ['mergely', 'lhs', change['op'], 'cid-' + i]; led.addLineClass(llf, 'background', 'start'); led.addLineClass(llt, 'background', 'end'); - + if (llf == 0 && llt == 0 && rlf == 0) { led.addLineClass(llf, 'background', clazz.join(' ')); led.addLineClass(llf, 'background', 'first'); @@ -1065,7 +1136,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { led.addLineClass(j, 'background', clazz.join(' ')); } } - + if (!red.getOption('readOnly')) { // add widgets to lhs, if rhs is not read only var rhs_button = self.merge_rhs_button.clone(); @@ -1081,7 +1152,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); var vp = this._get_viewport(editor_name1, editor_name2); - + this.trace('change', 'markup lhs-editor time', timer.stop()); red.operation(function() { for (var i = 0; i < changes.length; ++i) { @@ -1090,16 +1161,16 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + if (!self._is_change_in_view(vp, change)) { // if the change is outside the viewport, skip continue; } - + var clazz = ['mergely', 'rhs', change['op'], 'cid-' + i]; red.addLineClass(rlf, 'background', 'start'); red.addLineClass(rlt, 'background', 'end'); - + if (rlf == 0 && rlt == 0 && llf == 0) { red.addLineClass(rlf, 'background', clazz.join(' ')); red.addLineClass(rlf, 'background', 'first'); @@ -1126,7 +1197,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } }); this.trace('change', 'markup rhs-editor time', timer.stop()); - + // mark text deleted, LCS changes var marktext = []; for (var i = 0; this.settings.lcs && i < changes.length; ++i) { @@ -1135,7 +1206,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + if (!this._is_change_in_view(vp, change)) { // if the change is outside the viewport, skip continue; @@ -1151,7 +1222,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } else if (change['op'] == 'c') { // apply LCS changes to each line - for (var j = llf, k = rlf, p = 0; + for (var j = llf, k = rlf, p = 0; ((j >= 0) && (j <= llt)) || ((k >= 0) && (k <= rlt)); ++j, ++k) { if (k + p > rlt) { @@ -1172,7 +1243,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var lhs_stop = { line: -1, ch: -1 }; var rhs_start = { line: -1, ch: -1 }; var rhs_stop = { line: -1, ch: -1 }; - + var lcs = new Mgly.LCS(lhs_line, rhs_line); lcs.diff( function (from, to) {//added @@ -1186,7 +1257,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } } this.trace('change', 'LCS marktext time', timer.stop()); - + // mark changes outside closure led.operation(function() { // apply lhs markup @@ -1205,7 +1276,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } }); this.trace('change', 'LCS markup time', timer.stop()); - + // merge buttons var ed = {lhs:led, rhs:red}; jQuery('.merge-button').on('click', function(ev){ @@ -1230,12 +1301,24 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); var change = self.changes[cid]; - var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; - + // line below not used + //var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; + + self._merge_change(change, side, oside); + }); + this.trace('change', 'markup buttons time', timer.stop()); + }, + _merge_change : function(change, side, oside) { + var editor_name1 = this.id + '-lhs'; + var editor_name2 = this.id + '-rhs'; + var led = this.editor[editor_name1]; + var red = this.editor[editor_name2]; + var ed = {lhs:led, rhs:red}; + var text = ed[side].getRange( CodeMirror.Pos(change[side + '-line-from'], 0), CodeMirror.Pos(change[side + '-line-to'] + 1, 0)); - + if (change['op'] == 'c') { ed[oside].replaceRange(text, CodeMirror.Pos(change[oside + '-line-from'], 0), @@ -1272,8 +1355,6 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { ed['lhs'].setValue(ed['lhs'].getValue()); ed['rhs'].setValue(ed['rhs'].getValue()); return false; - }); - this.trace('change', 'markup buttons time', timer.stop()); }, _draw_info: function(editor_name1, editor_name2) { var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height(); @@ -1311,14 +1392,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { 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.strokeStyle = '#888'; @@ -1344,41 +1425,41 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end); ctx_lhs.beginPath(); - ctx_lhs.fillStyle = this.settings.fgcolor[change['op']]; + ctx_lhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; ctx_lhs.strokeStyle = '#000'; ctx_lhs.lineWidth = 0.5; ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_rhs.beginPath(); - ctx_rhs.fillStyle = this.settings.fgcolor[change['op']]; + ctx_rhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; ctx_rhs.strokeStyle = '#000'; ctx_rhs.lineWidth = 0.5; ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); ctx_rhs.strokeRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); - + if (!this._is_change_in_view(vp, change)) { continue; } - + 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; - + // draw left box ctx.beginPath(); - ctx.strokeStyle = this.settings.fgcolor[change['op']]; - ctx.lineWidth = 1; - + ctx.strokeStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; + ctx.lineWidth = (this.settings.current_diff==i) ? 1.5 : 1; + var rectWidth = this.draw_lhs_width; var rectHeight = lhs_y_end - lhs_y_start - 1; var rectX = this.draw_lhs_min; var rectY = lhs_y_start; // top and top top-right corner - + // draw left box ctx.moveTo(rectX, rectY); if (navigator.appName == 'Microsoft Internet Explorer') { @@ -1399,7 +1480,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { ctx.lineTo(rectX, rectY + rectHeight); } ctx.stroke(); - + rectWidth = this.draw_rhs_width; rectHeight = rhs_y_end - rhs_y_start - 1; rectX = this.draw_rhs_max; @@ -1423,7 +1504,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { ctx.lineTo(rectX, rectY + rectHeight); } ctx.stroke(); - + // connect boxes var cx = this.draw_lhs_min + this.draw_lhs_width; var cy = lhs_y_start + (lhs_y_end + 1 - lhs_y_start) / 2.0; @@ -1446,7 +1527,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { // visible window feedback ctx_lhs.fillStyle = this.settings.vpcolor; ctx_rhs.fillStyle = this.settings.vpcolor; - + var lto = ex.clhs.height() * ex.visible_page_ratio; var lfrom = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height(); var rto = ex.crhs.height() * ex.visible_page_ratio; @@ -1457,10 +1538,10 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio); this.trace('draw', 'lhs from', lfrom, 'lhs to', lto); this.trace('draw', 'rhs from', rfrom, 'rhs to', rto); - + ctx_lhs.fillRect(1.5, lfrom, 4.5, lto); ctx_rhs.fillRect(1.5, rfrom, 4.5, rto); - + ex.clhs.click(function (ev) { var y = ev.pageY - ex.lhs_xyoffset.top - (lto / 2); var sto = Math.max(0, (y / mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight); @@ -1468,7 +1549,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); ex.crhs.click(function (ev) { var y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2); - var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight); + var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight); ex.rhs_scroller.scrollTop(sto); }); }, @@ -1476,14 +1557,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { if(this.settings._debug.indexOf(name) >= 0) { arguments[0] = name+':'; console.log([].slice.apply(arguments)); - } + } } }); jQuery.pluginMaker = function(plugin) { // add the plugin function as a jQuery plugin jQuery.fn[plugin.prototype.name] = function(options) { - // get the arguments + // get the arguments var args = jQuery.makeArray(arguments), after = args.slice(1); var rc = undefined; From e4c36f35ef3b1197922f1f87077febfc4a6ba5c6 Mon Sep 17 00:00:00 2001 From: Kljh Date: Sat, 16 Aug 2014 16:38:32 +0100 Subject: [PATCH 2/8] Revert previous commit. Too many whitespace modification makes it painful to see the actual changes. This reverts commit f1782d193379f01d0841e73e5a984f840bbc2e7f. --- lib/mergely.js | 239 ++++++++++++++++--------------------------------- 1 file changed, 79 insertions(+), 160 deletions(-) diff --git a/lib/mergely.js b/lib/mergely.js index d0f66f7..1fe2c6d 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -5,7 +5,7 @@ Mgly.Timer = function(){ self.start = function() { self.t0 = new Date().getTime(); } self.stop = function() { var t1 = new Date().getTime(); - var d = t1 - self.t0; + var d = t1 - self.t0; self.t0 = t1; return d; } @@ -172,11 +172,11 @@ jQuery.extend(Mgly.diff.prototype, { var change = 'c'; if (item.lhs_deleted_count == 0 && item.rhs_inserted_count > 0) change = 'a'; else if (item.lhs_deleted_count > 0 && item.rhs_inserted_count == 0) change = 'd'; - + if (item.lhs_deleted_count == 1) lhs_str = item.lhs_start + 1; else if (item.lhs_deleted_count == 0) lhs_str = item.lhs_start; else lhs_str = (item.lhs_start + 1) + ',' + (item.lhs_start + item.lhs_deleted_count); - + if (item.rhs_inserted_count == 1) rhs_str = item.rhs_start + 1; else if (item.rhs_inserted_count == 0) rhs_str = item.rhs_start; else rhs_str = (item.rhs_start + 1) + ',' + (item.rhs_start + item.rhs_inserted_count); @@ -369,7 +369,7 @@ jQuery.extend(Mgly.mergely.prototype, { Mgly.CodeMirrorDiffView = function(el, options) { CodeMirror.defineExtension('centerOnCursor', function() { var coords = this.cursorCoords(null, 'local'); - this.scrollTo(null, + this.scrollTo(null, (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2)); }); this.init(el, options); @@ -384,15 +384,13 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { lcs: true, sidebar: true, viewport: false, - ignorews: true, + ignorews: false, fadein: 'fast', - editor_width: '650px', + editor_width: '400px', editor_height: '400px', resize_timeout: 500, change_timeout: 150, - current_diff: 0, // currently active difference, in 0 .. changes.length range. - fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color) - ca:'#4b73ff',cc:'#737373',cd:'#ff4f4f'}, // color for currently active difference (bright color) + fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f'}, bgcolor: '#eee', vpcolor: 'rgba(0, 0, 200, 0.5)', lhs: function(setValue) { }, @@ -445,15 +443,15 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } this.lhs_cmsettings = {}; this.rhs_cmsettings = {}; - + // save this element for faster queries this.element = jQuery(el); - + // save options if there are any if (options && options.cmsettings) jQuery.extend(this.lhs_cmsettings, cmsettings, options.cmsettings, options.lhs_cmsettings); if (options && options.cmsettings) jQuery.extend(this.rhs_cmsettings, cmsettings, options.cmsettings, options.rhs_cmsettings); if (options) jQuery.extend(this.settings, options); - + // bind if the element is destroyed this.element.bind('destroyed', jQuery.proxy(this.teardown, this)); @@ -567,7 +565,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.prev_query[side] = query; } var cursor = this.cursor[this.id]; - + if (cursor[direction]()) { editor.setSelection(cursor.from(), cursor.to()); } @@ -614,7 +612,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } this.merge_rhs_button = jQuery(merge_rhs_button); this.merge_lhs_button = jQuery(merge_lhs_button); - + // create the textarea and canvas elements jQuery(this.element).append(jQuery('
')); jQuery(this.element).append(jQuery('
')); @@ -679,7 +677,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { sz(true); } //bind - + if (this.settings.lhs) { var setv = this.editor[this.id + '-lhs'].getDoc().setValue; this.settings.lhs(setv.bind(this.editor[this.id + '-lhs'].getDoc())); @@ -688,77 +686,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var setv = this.editor[this.id + '-rhs'].getDoc().setValue; this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc())); } - - // register WinMerge style key bindings - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key - var self = this; - el.addEventListener("keydown", function(evt) { - if (evt.defaultPrevented) - ;//return; // Should do nothing if the key event was already consumed. - - if (!evt.shiftKey && !evt.altKey && !evt.ctrlKey && !evt.metaKey) - return; // if none of Shift, Alt, Ctrl or Meta key is used then do nothing - - var shift_key = 16, alt_key = 17, ctrlKey = 18, metaKey = 91; // 13, 27 ? - var up_arrow = 38, down_arrow = 40, left_arrow = 37, right_arrow = 39; - if (!evt.key) { - // use keyCode as key - evt.key = evt.which; - } - - switch (evt.key) { - case "ArrowDown": - case down_arrow: - self.settings.current_diff++; - self._changed(self.id + '-lhs', self.id + '-rhs'); - break; - case "ArrowUp": - case up_arrow: - self.settings.current_diff--; - self.settings.current_diff = Math.max(self.settings.current_diff, 0); - self._changed(self.id + '-lhs', self.id + '-rhs'); - break; - case "ArrowLeft": - case left_arrow: - // !! recalculating the changes !! - var editor_name1 = self.id + '-lhs'; - var editor_name2 = self.id + '-rhs'; - var lhs = self.editor[editor_name1].getValue(); - var rhs = self.editor[editor_name2].getValue(); - var d = new Mgly.diff(lhs, rhs, self.settings); - var changes = Mgly.DiffParser(d.normal_form()); - - var change = changes[self.settings.current_diff]; - self._merge_change(change, "rhs", "lhs"); - break; - case "ArrowRight": - case right_arrow: - // !! recalculating the changes !! - var editor_name1 = self.id + '-lhs'; - var editor_name2 = self.id + '-rhs'; - var lhs = self.editor[editor_name1].getValue(); - var rhs = self.editor[editor_name2].getValue(); - var d = new Mgly.diff(lhs, rhs, self.settings); - var changes = Mgly.DiffParser(d.normal_form()); - - var change = changes[self.settings.current_diff]; - self._merge_change(change, "lhs", "rhs"); - break; - case "Enter": - // Do something for "enter" or "return" key press. - break; - case "Escape": - // Do something for "esc" key press. - break; - default: - return; // Quit when this doesn't handle the key event. - } - - // Consume the event for suppressing "double action". - //event.preventDefault(); - }); }, - + _scrolling: function(editor_name) { if (this._skipscroll[editor_name] === true) { // scrolling one side causes the other to event - ignore it @@ -773,23 +702,23 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var midline = this.editor[editor_name].coordsChar({left:0, top:this.midway}); var top_to = scroller.scrollTop(); var left_to = scroller.scrollLeft(); - + this.trace('scroll', 'side', editor_name); this.trace('scroll', 'midway', this.midway); this.trace('scroll', 'midline', midline); this.trace('scroll', 'top_to', top_to); this.trace('scroll', 'left_to', left_to); - + var editor_name1 = this.id + '-lhs'; var editor_name2 = this.id + '-rhs'; - + for (var name in this.editor) { if (!this.editor.hasOwnProperty(name)) continue; if (editor_name == name) continue; //same editor var this_side = editor_name.replace(this.id + '-', ''); var other_side = name.replace(this.id + '-', ''); var top_adjust = 0; - + // find the last change that is less than or within the midway point // do not move the rhs until the lhs end point is >= the rhs end point. var last_change = null; @@ -807,14 +736,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { force_scroll = true; } else { - top_adjust += - (change[this_side+'-y-end'] - change[this_side+'-y-start']) - + top_adjust += + (change[this_side+'-y-end'] - change[this_side+'-y-start']) - (change[other_side+'-y-end'] - change[other_side+'-y-start']); } } } } - + var vp = this.editor[name].getViewport(); var scroll = true; if (last_change) { @@ -832,7 +761,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { scroller.scrollTop(top_to - top_adjust).scrollLeft(left_to); } else this.trace('scroll', 'not scrolling other side'); - + if (this.settings.autoupdate) { var timer = new Mgly.Timer(); this._calculate_offsets(editor_name1, editor_name2, this.changes); @@ -885,12 +814,12 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); } 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'; @@ -902,7 +831,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { 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); @@ -974,16 +903,16 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var saveY = this.editor[editor_name1].getScrollInfo().top; // temporarily scroll to top this.editor[editor_name1].scrollTo(null, 0); - - // this is the distance from the top of the screen to the top of the + + // this is the distance from the top of the screen to the top of the // content of the first codemirror editor var topnode = jQuery('#' + this.id + ' .CodeMirror-measure').first(); var top_offset = topnode.offset().top - 4; if(!top_offset) return false; - + // restore editor's scroll position this.editor[editor_name1].scrollTo(null, saveY); - + this.draw_top_offset = 0.5 - top_offset; return true; }, @@ -1013,10 +942,10 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var lhschc = this.editor[editor_name1].charCoords({line: 0}); var rhschc = this.editor[editor_name2].charCoords({line: 0}); var vp = this._get_viewport(editor_name1, editor_name2); - + for (var i = 0; i < changes.length; ++i) { var change = changes[i]; - + if (!this.settings.sidebar && !this._is_change_in_view(vp, change)) { // if the change is outside the viewport, skip delete change['lhs-y-start']; @@ -1029,7 +958,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + var ls, le, rs, re; if (this.editor[editor_name1].getOption('lineWrapping') || this.editor[editor_name1].getOption('lineWrapping')) { // If using line-wrapping, we must get the height of the line @@ -1040,7 +969,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var tle = this.editor[editor_name1].cursorCoords({line: llt, ch: 0}, 'page'); var lhseh = this.editor[editor_name1].getLineHandle(llt); le = { top: tle.top, bottom: tle.top + lhseh.height }; - + var tls = this.editor[editor_name2].cursorCoords({line: rlf, ch: 0}, 'page'); var rhssh = this.editor[editor_name2].getLineHandle(rlf); rs = { top: tls.top, bottom: tls.top + rhssh.height }; @@ -1051,24 +980,24 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } else { // If not using line-wrapping, we can calculate the line position - ls = { - top: lhschc.top + llf * this.em_height, + ls = { + top: lhschc.top + llf * this.em_height, bottom: lhschc.bottom + llf * this.em_height + 2 }; le = { - top: lhschc.top + llt * this.em_height, + top: lhschc.top + llt * this.em_height, bottom: lhschc.bottom + llt * this.em_height + 2 }; rs = { - top: rhschc.top + rlf * this.em_height, + top: rhschc.top + rlf * this.em_height, bottom: rhschc.bottom + rlf * this.em_height + 2 }; re = { - top: rhschc.top + rlt * this.em_height, + top: rhschc.top + rlt * this.em_height, bottom: rhschc.bottom + rlt * this.em_height + 2 }; } - + if (change['op'] == 'a') { // adds (right), normally start from the end of the lhs, // except for the case when the start of the rhs is 0 @@ -1107,7 +1036,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }, _markup_changes: function (editor_name1, editor_name2, changes) { jQuery('.merge-button').remove(); // clear - + var self = this; var led = this.editor[editor_name1]; var red = this.editor[editor_name2]; @@ -1120,11 +1049,11 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + var clazz = ['mergely', 'lhs', change['op'], 'cid-' + i]; led.addLineClass(llf, 'background', 'start'); led.addLineClass(llt, 'background', 'end'); - + if (llf == 0 && llt == 0 && rlf == 0) { led.addLineClass(llf, 'background', clazz.join(' ')); led.addLineClass(llf, 'background', 'first'); @@ -1136,7 +1065,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { led.addLineClass(j, 'background', clazz.join(' ')); } } - + if (!red.getOption('readOnly')) { // add widgets to lhs, if rhs is not read only var rhs_button = self.merge_rhs_button.clone(); @@ -1152,7 +1081,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); var vp = this._get_viewport(editor_name1, editor_name2); - + this.trace('change', 'markup lhs-editor time', timer.stop()); red.operation(function() { for (var i = 0; i < changes.length; ++i) { @@ -1161,16 +1090,16 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + if (!self._is_change_in_view(vp, change)) { // if the change is outside the viewport, skip continue; } - + var clazz = ['mergely', 'rhs', change['op'], 'cid-' + i]; red.addLineClass(rlf, 'background', 'start'); red.addLineClass(rlt, 'background', 'end'); - + if (rlf == 0 && rlt == 0 && llf == 0) { red.addLineClass(rlf, 'background', clazz.join(' ')); red.addLineClass(rlf, 'background', 'first'); @@ -1197,7 +1126,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } }); this.trace('change', 'markup rhs-editor time', timer.stop()); - + // mark text deleted, LCS changes var marktext = []; for (var i = 0; this.settings.lcs && i < changes.length; ++i) { @@ -1206,7 +1135,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; - + if (!this._is_change_in_view(vp, change)) { // if the change is outside the viewport, skip continue; @@ -1222,7 +1151,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } else if (change['op'] == 'c') { // apply LCS changes to each line - for (var j = llf, k = rlf, p = 0; + for (var j = llf, k = rlf, p = 0; ((j >= 0) && (j <= llt)) || ((k >= 0) && (k <= rlt)); ++j, ++k) { if (k + p > rlt) { @@ -1243,7 +1172,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var lhs_stop = { line: -1, ch: -1 }; var rhs_start = { line: -1, ch: -1 }; var rhs_stop = { line: -1, ch: -1 }; - + var lcs = new Mgly.LCS(lhs_line, rhs_line); lcs.diff( function (from, to) {//added @@ -1257,7 +1186,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } } this.trace('change', 'LCS marktext time', timer.stop()); - + // mark changes outside closure led.operation(function() { // apply lhs markup @@ -1276,7 +1205,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } }); this.trace('change', 'LCS markup time', timer.stop()); - + // merge buttons var ed = {lhs:led, rhs:red}; jQuery('.merge-button').on('click', function(ev){ @@ -1301,24 +1230,12 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); var change = self.changes[cid]; - // line below not used - //var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; - - self._merge_change(change, side, oside); - }); - this.trace('change', 'markup buttons time', timer.stop()); - }, - _merge_change : function(change, side, oside) { - var editor_name1 = this.id + '-lhs'; - var editor_name2 = this.id + '-rhs'; - var led = this.editor[editor_name1]; - var red = this.editor[editor_name2]; - var ed = {lhs:led, rhs:red}; - + var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; + var text = ed[side].getRange( CodeMirror.Pos(change[side + '-line-from'], 0), CodeMirror.Pos(change[side + '-line-to'] + 1, 0)); - + if (change['op'] == 'c') { ed[oside].replaceRange(text, CodeMirror.Pos(change[oside + '-line-from'], 0), @@ -1355,6 +1272,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { ed['lhs'].setValue(ed['lhs'].getValue()); ed['rhs'].setValue(ed['rhs'].getValue()); return false; + }); + this.trace('change', 'markup buttons time', timer.stop()); }, _draw_info: function(editor_name1, editor_name2) { var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height(); @@ -1392,14 +1311,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { 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.strokeStyle = '#888'; @@ -1425,41 +1344,41 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end); ctx_lhs.beginPath(); - ctx_lhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; + ctx_lhs.fillStyle = this.settings.fgcolor[change['op']]; ctx_lhs.strokeStyle = '#000'; ctx_lhs.lineWidth = 0.5; ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_rhs.beginPath(); - ctx_rhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; + ctx_rhs.fillStyle = this.settings.fgcolor[change['op']]; ctx_rhs.strokeStyle = '#000'; ctx_rhs.lineWidth = 0.5; ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); ctx_rhs.strokeRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); - + if (!this._is_change_in_view(vp, change)) { continue; } - + 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; - + // draw left box ctx.beginPath(); - ctx.strokeStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; - ctx.lineWidth = (this.settings.current_diff==i) ? 1.5 : 1; - + ctx.strokeStyle = this.settings.fgcolor[change['op']]; + ctx.lineWidth = 1; + var rectWidth = this.draw_lhs_width; var rectHeight = lhs_y_end - lhs_y_start - 1; var rectX = this.draw_lhs_min; var rectY = lhs_y_start; // top and top top-right corner - + // draw left box ctx.moveTo(rectX, rectY); if (navigator.appName == 'Microsoft Internet Explorer') { @@ -1480,7 +1399,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { ctx.lineTo(rectX, rectY + rectHeight); } ctx.stroke(); - + rectWidth = this.draw_rhs_width; rectHeight = rhs_y_end - rhs_y_start - 1; rectX = this.draw_rhs_max; @@ -1504,7 +1423,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { ctx.lineTo(rectX, rectY + rectHeight); } ctx.stroke(); - + // connect boxes var cx = this.draw_lhs_min + this.draw_lhs_width; var cy = lhs_y_start + (lhs_y_end + 1 - lhs_y_start) / 2.0; @@ -1527,7 +1446,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { // visible window feedback ctx_lhs.fillStyle = this.settings.vpcolor; ctx_rhs.fillStyle = this.settings.vpcolor; - + var lto = ex.clhs.height() * ex.visible_page_ratio; var lfrom = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height(); var rto = ex.crhs.height() * ex.visible_page_ratio; @@ -1538,10 +1457,10 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio); this.trace('draw', 'lhs from', lfrom, 'lhs to', lto); this.trace('draw', 'rhs from', rfrom, 'rhs to', rto); - + ctx_lhs.fillRect(1.5, lfrom, 4.5, lto); ctx_rhs.fillRect(1.5, rfrom, 4.5, rto); - + ex.clhs.click(function (ev) { var y = ev.pageY - ex.lhs_xyoffset.top - (lto / 2); var sto = Math.max(0, (y / mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight); @@ -1549,7 +1468,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); ex.crhs.click(function (ev) { var y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2); - var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight); + var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight); ex.rhs_scroller.scrollTop(sto); }); }, @@ -1557,14 +1476,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { if(this.settings._debug.indexOf(name) >= 0) { arguments[0] = name+':'; console.log([].slice.apply(arguments)); - } + } } }); jQuery.pluginMaker = function(plugin) { // add the plugin function as a jQuery plugin jQuery.fn[plugin.prototype.name] = function(options) { - // get the arguments + // get the arguments var args = jQuery.makeArray(arguments), after = args.slice(1); var rc = undefined; From 885fcd4e05ccaf38794685eb20915d4e09579cbe Mon Sep 17 00:00:00 2001 From: Kljh Date: Sat, 16 Aug 2014 17:31:15 +0100 Subject: [PATCH 3/8] =?UTF-8?q?Added=20keyboard=20shortcuts=20=C3=A0=20la?= =?UTF-8?q?=20WinMerge.=20Alt=20+=20Up=20or=20Down=20arrow=20:=20go=20to?= =?UTF-8?q?=20previous=20or=20next=20difference=20Alt=20+=20Left=20or=20Ri?= =?UTF-8?q?ght=20arrow=20:=20merge=20difference=20using=20right=20or=20lef?= =?UTF-8?q?t=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shift, Ctr, Alt or Meta keys can be used indifferntly (to cope with browser's idiosyncratics). Event handler is now attached to text editor. This is clearly to review (does not capture all page events and too intrusive). New state "current_diff" has been added. Need to scroll the editor to center it on currently active state (scrollToChange function to add). --- lib/mergely.js | 101 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/lib/mergely.js b/lib/mergely.js index 1fe2c6d..1c064c6 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -386,11 +386,13 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { viewport: false, ignorews: false, fadein: 'fast', - editor_width: '400px', + editor_width: '650px', editor_height: '400px', resize_timeout: 500, change_timeout: 150, - fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f'}, + current_diff: 0, // currently active difference, in 0 .. changes.length range. + fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color) + ca:'#4b73ff',cc:'#737373',cd:'#ff4f4f'}, // color for currently active difference (bright color) bgcolor: '#eee', vpcolor: 'rgba(0, 0, 200, 0.5)', lhs: function(setValue) { }, @@ -686,6 +688,74 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var setv = this.editor[this.id + '-rhs'].getDoc().setValue; this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc())); } + + // kljh: register WinMerge style key bindings + var self = this; + el.addEventListener("keydown", function(evt) { + if (evt.defaultPrevented) + ;//return; // Should do nothing if the key event was already consumed. + + if (!evt.shiftKey && !evt.altKey && !evt.ctrlKey && !evt.metaKey) + return; // if none of Shift, Alt, Ctrl or Meta key is used then do nothing + + var shift_key = 16, alt_key = 17, ctrlKey = 18, metaKey = 91; // 13, 27 ? + var up_arrow = 38, down_arrow = 40, left_arrow = 37, right_arrow = 39; + if (!evt.key) { + // use keyCode as key + evt.key = evt.which; + } + + switch (evt.key) { + case "ArrowDown": + case down_arrow: + self.settings.current_diff++; + self._changed(self.id + '-lhs', self.id + '-rhs'); + break; + case "ArrowUp": + case up_arrow: + self.settings.current_diff--; + self.settings.current_diff = Math.max(self.settings.current_diff, 0); + self._changed(self.id + '-lhs', self.id + '-rhs'); + break; + case "ArrowLeft": + case left_arrow: + // !! recalculating the changes !! + var editor_name1 = self.id + '-lhs'; + var editor_name2 = self.id + '-rhs'; + var lhs = self.editor[editor_name1].getValue(); + var rhs = self.editor[editor_name2].getValue(); + var d = new Mgly.diff(lhs, rhs, self.settings); + var changes = Mgly.DiffParser(d.normal_form()); + + var change = changes[self.settings.current_diff]; + self._merge_change(change, "rhs", "lhs"); + break; + case "ArrowRight": + case right_arrow: + // !! recalculating the changes !! + var editor_name1 = self.id + '-lhs'; + var editor_name2 = self.id + '-rhs'; + var lhs = self.editor[editor_name1].getValue(); + var rhs = self.editor[editor_name2].getValue(); + var d = new Mgly.diff(lhs, rhs, self.settings); + var changes = Mgly.DiffParser(d.normal_form()); + + var change = changes[self.settings.current_diff]; + self._merge_change(change, "lhs", "rhs"); + break; + case "Enter": + // Do something for "enter" or "return" key press. + break; + case "Escape": + // Do something for "esc" key press. + break; + default: + return; // Quit when this doesn't handle the key event. + } + + // Consume the event for suppressing "double action". + //event.preventDefault(); + }); }, _scrolling: function(editor_name) { @@ -1230,7 +1300,21 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { }); var change = self.changes[cid]; - var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; + // kljh: not used + //var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; + + self._merge_change(change, side, oside); + return false; + }); + this.trace('change', 'markup buttons time', timer.stop()); + }, + _merge_change : function(change, side, oside) { + var editor_name1 = this.id + '-lhs'; + var editor_name2 = this.id + '-rhs'; + var led = this.editor[editor_name1]; + var red = this.editor[editor_name2]; + var ed = {lhs:led, rhs:red}; + var text = ed[side].getRange( CodeMirror.Pos(change[side + '-line-from'], 0), @@ -1271,9 +1355,6 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { //reset ed['lhs'].setValue(ed['lhs'].getValue()); ed['rhs'].setValue(ed['rhs'].getValue()); - return false; - }); - this.trace('change', 'markup buttons time', timer.stop()); }, _draw_info: function(editor_name1, editor_name2) { var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height(); @@ -1344,14 +1425,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end); ctx_lhs.beginPath(); - ctx_lhs.fillStyle = this.settings.fgcolor[change['op']]; + ctx_lhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; ctx_lhs.strokeStyle = '#000'; ctx_lhs.lineWidth = 0.5; ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_rhs.beginPath(); - ctx_rhs.fillStyle = this.settings.fgcolor[change['op']]; + ctx_rhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; ctx_rhs.strokeStyle = '#000'; ctx_rhs.lineWidth = 0.5; ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); @@ -1370,8 +1451,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { // draw left box ctx.beginPath(); - ctx.strokeStyle = this.settings.fgcolor[change['op']]; - ctx.lineWidth = 1; + ctx.strokeStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; + ctx.lineWidth = (this.settings.current_diff==i) ? 1.5 : 1; var rectWidth = this.draw_lhs_width; var rectHeight = lhs_y_end - lhs_y_start - 1; From 6308a6926a52746a34c6f1cc94a502de33fb972a Mon Sep 17 00:00:00 2001 From: Kljh Date: Sat, 16 Aug 2014 20:39:03 +0100 Subject: [PATCH 4/8] Added auto scrolling. Current difference is now always in viewport. Added functions get_changes() and scroll_to_change(change) around line 767. Both new review. We shouldn't need to rerun the full diff but I couldn't find how to retrieve the diff results. Scrolling to a given change is quite tricky (musn't break the canvas graph, must have both pane nice). --- lib/mergely.js | 95 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 30 deletions(-) diff --git a/lib/mergely.js b/lib/mergely.js index 1c064c6..a2bc3cb 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -689,75 +689,110 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc())); } + // kljh: register WinMerge style key bindings var self = this; - el.addEventListener("keydown", function(evt) { - if (evt.defaultPrevented) - ;//return; // Should do nothing if the key event was already consumed. + document.body.addEventListener("keydown", function(evt) { + if (evt.defaultPrevented) { + //return; // Should do nothing if the key event was already consumed. + //console.warn("Should do nothing if the key event was already consumed."); + } if (!evt.shiftKey && !evt.altKey && !evt.ctrlKey && !evt.metaKey) return; // if none of Shift, Alt, Ctrl or Meta key is used then do nothing - var shift_key = 16, alt_key = 17, ctrlKey = 18, metaKey = 91; // 13, 27 ? - var up_arrow = 38, down_arrow = 40, left_arrow = 37, right_arrow = 39; + var shift_key=16, alt_key=17, ctrlKey=18, metaKey=91; // enterKey=13, escKey=27 + var up_arrow=38, down_arrow=40, left_arrow=37, right_arrow=39; + var key_h_left=72, key_j_down=74, key_k_up=75, key_h_right=76; if (!evt.key) { // use keyCode as key evt.key = evt.which; } + if (evt.shiftKey && evt.ctrlKey) { + // VI convention : Ctrl + Shif + K/L/J/H + if (evt.which==key_h_left) evt.key = left_arrow; + if (evt.which==key_j_down) evt.key = down_arrow; + if (evt.which==key_k_up) evt.key = up_arrow; + if (evt.which==key_h_right) evt.key = right_arrow; + } switch (evt.key) { + case "Down": case "ArrowDown": case down_arrow: + // !! recalculating the changes !! + var changes = get_changes(); + self.settings.current_diff++; + self.settings.current_diff = Math.min(self.settings.current_diff, changes.length-1); + scroll_to_change(changes[self.settings.current_diff]); + self._changed(self.id + '-lhs', self.id + '-rhs'); break; + case "Up": case "ArrowUp": case up_arrow: + // !! recalculating the changes !! + var changes = get_changes(); + self.settings.current_diff--; self.settings.current_diff = Math.max(self.settings.current_diff, 0); + scroll_to_change(changes[self.settings.current_diff]); + self._changed(self.id + '-lhs', self.id + '-rhs'); break; + case "Left": case "ArrowLeft": case left_arrow: // !! recalculating the changes !! - var editor_name1 = self.id + '-lhs'; - var editor_name2 = self.id + '-rhs'; - var lhs = self.editor[editor_name1].getValue(); - var rhs = self.editor[editor_name2].getValue(); - var d = new Mgly.diff(lhs, rhs, self.settings); - var changes = Mgly.DiffParser(d.normal_form()); + var changes = get_changes(); - var change = changes[self.settings.current_diff]; - self._merge_change(change, "rhs", "lhs"); + self._merge_change(changes[self.settings.current_diff], "rhs", "lhs"); break; + case "Right": case "ArrowRight": case right_arrow: // !! recalculating the changes !! - var editor_name1 = self.id + '-lhs'; - var editor_name2 = self.id + '-rhs'; - var lhs = self.editor[editor_name1].getValue(); - var rhs = self.editor[editor_name2].getValue(); - var d = new Mgly.diff(lhs, rhs, self.settings); - var changes = Mgly.DiffParser(d.normal_form()); + var changes = get_changes(); - var change = changes[self.settings.current_diff]; - self._merge_change(change, "lhs", "rhs"); - break; - case "Enter": - // Do something for "enter" or "return" key press. - break; - case "Escape": - // Do something for "esc" key press. + self._merge_change(changes[self.settings.current_diff], "lhs", "rhs"); break; default: - return; // Quit when this doesn't handle the key event. + return; } // Consume the event for suppressing "double action". - //event.preventDefault(); + event.preventDefault(); }); + + function get_changes() { + var lhs = self.editor[self.id+'-lhs'].getValue(); + var rhs = self.editor[self.id+'-rhs'].getValue(); + var d = new Mgly.diff(lhs, rhs, self.settings); + var changes = Mgly.DiffParser(d.normal_form()); + return changes; + } + + function scroll_to_change(change) { + var led = self.editor[self.id+'-lhs']; + var red = self.editor[self.id+'-rhs']; + + var yref = led.getScrollerElement().offsetHeight * 1/2; // center between 0 and 1/2 + + // using directly CodeMirror breaks canvas alignment + // var ly = led.charCoords({line: Math.max(change["lhs-line-from"],0), ch: 0}, "local").top; + + // calculate scroll offset for current change. Warning: returns relative y position so we scroll to 0 first. + led.scrollTo(null, 0); + red.scrollTo(null, 0); + self._calculate_offsets(self.id+'-lhs', self.id+'-rhs', [change]); + led.scrollTo(null, Math.max(change["lhs-y-start"]-yref, 0)); + red.scrollTo(null, Math.max(change["rhs-y-start"]-yref, 0)); + + // right pane should simply follows + } }, - + _scrolling: function(editor_name) { if (this._skipscroll[editor_name] === true) { // scrolling one side causes the other to event - ignore it From 4128796bfa12efd9b5354e3044f77a023a51f686 Mon Sep 17 00:00:00 2001 From: Kljh Date: Sun, 17 Aug 2014 00:17:39 +0100 Subject: [PATCH 5/8] Played with ajax.html. It's probably no longer simple enough as an example. On the other hand it has a nice drag and drop feature. I couldn't make the "ignorews" checkbos work. I had to update codemirror.css to have the texteditor height match the container. I tested using Opera browser and there seems to be no autosizing. --- examples/ajax.html | 88 ++++++++++++++++++++++++++++++++++++++++++---- lib/codemirror.css | 2 +- lib/mergely.js | 18 +++++++--- 3 files changed, 96 insertions(+), 12 deletions(-) diff --git a/examples/ajax.html b/examples/ajax.html index dbc1c89..16926f1 100644 --- a/examples/ajax.html +++ b/examples/ajax.html @@ -13,40 +13,116 @@ This example demonstrates how to set left and right editors using ajax. - + - + - + + + + + +
Drop files here
ignore witespaces
+
+ + + +
  save   save
diff --git a/lib/codemirror.css b/lib/codemirror.css index 8de0b19..a831faa 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -3,7 +3,7 @@ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; - height: 300px; + height: 400px; } .CodeMirror-scroll { /* Set scrolling behaviour here */ diff --git a/lib/mergely.js b/lib/mergely.js index a2bc3cb..dc6161a 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -725,7 +725,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { self.settings.current_diff++; self.settings.current_diff = Math.min(self.settings.current_diff, changes.length-1); - scroll_to_change(changes[self.settings.current_diff]); + self._scroll_to_change(changes[self.settings.current_diff]); self._changed(self.id + '-lhs', self.id + '-rhs'); break; @@ -737,7 +737,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { self.settings.current_diff--; self.settings.current_diff = Math.max(self.settings.current_diff, 0); - scroll_to_change(changes[self.settings.current_diff]); + self._scroll_to_change(changes[self.settings.current_diff]); self._changed(self.id + '-lhs', self.id + '-rhs'); break; @@ -773,11 +773,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { return changes; } - function scroll_to_change(change) { + }, + + _scroll_to_change : function(change) { + var self = this; var led = self.editor[self.id+'-lhs']; var red = self.editor[self.id+'-rhs']; - var yref = led.getScrollerElement().offsetHeight * 1/2; // center between 0 and 1/2 + var yref = led.getScrollerElement().offsetHeight * 1/2; // center between >0 and 1/2 // using directly CodeMirror breaks canvas alignment // var ly = led.charCoords({line: Math.max(change["lhs-line-from"],0), ch: 0}, "local").top; @@ -790,7 +793,6 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { red.scrollTo(null, Math.max(change["rhs-y-start"]-yref, 0)); // right pane should simply follows - } }, _scrolling: function(editor_name) { @@ -949,6 +951,12 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('change', 'diff time', timer.stop()); this.changes = Mgly.DiffParser(d.normal_form()); this.trace('change', 'parse time', timer.stop()); + if (this.current_diff===undefined && this.changes.length!==0) { + // go to first differnece on start-up + this.current_diff = 0; + this._scroll_to_change(this.changes[0]); + } + this.trace('change', 'scroll_to_change time', timer.stop()); this._calculate_offsets(editor_name1, editor_name2, this.changes); this.trace('change', 'offsets time', timer.stop()); this._markup_changes(editor_name1, editor_name2, this.changes); From 572eb08aaadc08271914e8dcd0ffd732898fde8c Mon Sep 17 00:00:00 2001 From: Kljh Date: Sun, 17 Aug 2014 08:11:13 +0100 Subject: [PATCH 6/8] Morning clean up. Removed useless function get_changes. Moved current_diff out of this.settings (and along this.changes) Handle the case where comparing identical files (e.g. changes.length==0). ajax.html uses https for jquery so that it can be hosted on a https server. ajax.html uses full available screen width, auto height is trickier (note that call to _auto_height is commented out in mergely.js). --- examples/ajax.html | 9 ++++-- lib/codemirror.css | 2 +- lib/mergely.js | 68 ++++++++++++++++------------------------------ 3 files changed, 30 insertions(+), 49 deletions(-) diff --git a/examples/ajax.html b/examples/ajax.html index 16926f1..24e4489 100644 --- a/examples/ajax.html +++ b/examples/ajax.html @@ -12,7 +12,7 @@ This example demonstrates how to set left and right editors using ajax. - + @@ -37,6 +37,8 @@ This example demonstrates how to set left and right editors using ajax. $(document).ready(function () { $('#compare').mergely({ + width: 'auto', + height: 'auto', // containing div must be given a height cmsettings: { readOnly: false }, }); var lhs_url = 'lhs.txt'; @@ -112,7 +114,7 @@ This example demonstrates how to set left and right editors using ajax. }); - + @@ -123,7 +125,8 @@ This example demonstrates how to set left and right editors using ajax.
Drop files here
ignore witespaces   save   save
-
+ +
diff --git a/lib/codemirror.css b/lib/codemirror.css index a831faa..8de0b19 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -3,7 +3,7 @@ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; - height: 400px; + height: 300px; } .CodeMirror-scroll { /* Set scrolling behaviour here */ diff --git a/lib/mergely.js b/lib/mergely.js index dc6161a..af35d56 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -390,7 +390,6 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { editor_height: '400px', resize_timeout: 500, change_timeout: 150, - current_diff: 0, // currently active difference, in 0 .. changes.length range. fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color) ca:'#4b73ff',cc:'#737373',cd:'#ff4f4f'}, // color for currently active difference (bright color) bgcolor: '#eee', @@ -399,7 +398,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { rhs: function(setValue) { }, loaded: function() { }, //_auto_height: function(h) { return h - 20; }, - _auto_width: function(w) { return w; }, + _auto_width: function(w) { return w - 30; }, resize: function(init) { var scrollbar = init ? 16 : 0; var w = jQuery(el).parent().width() + scrollbar; @@ -720,42 +719,34 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { case "Down": case "ArrowDown": case down_arrow: - // !! recalculating the changes !! - var changes = get_changes(); - - self.settings.current_diff++; - self.settings.current_diff = Math.min(self.settings.current_diff, changes.length-1); - self._scroll_to_change(changes[self.settings.current_diff]); + if (!self.changes.length) return; + self.current_diff++; + self.current_diff = Math.min(self.current_diff, self.changes.length-1); + self._scroll_to_change(self.changes[self.current_diff]); self._changed(self.id + '-lhs', self.id + '-rhs'); break; case "Up": case "ArrowUp": case up_arrow: - // !! recalculating the changes !! - var changes = get_changes(); - - self.settings.current_diff--; - self.settings.current_diff = Math.max(self.settings.current_diff, 0); - self._scroll_to_change(changes[self.settings.current_diff]); + if (!self.changes.length) return; + self.current_diff--; + self.current_diff = Math.max(self.current_diff, 0); + self._scroll_to_change(self.changes[self.current_diff]); self._changed(self.id + '-lhs', self.id + '-rhs'); break; case "Left": case "ArrowLeft": case left_arrow: - // !! recalculating the changes !! - var changes = get_changes(); - - self._merge_change(changes[self.settings.current_diff], "rhs", "lhs"); + if (!self.changes.length) return; + self._merge_change(self.changes[self.current_diff], "rhs", "lhs"); break; case "Right": case "ArrowRight": case right_arrow: - // !! recalculating the changes !! - var changes = get_changes(); - - self._merge_change(changes[self.settings.current_diff], "lhs", "rhs"); + if (!self.changes.length) return; + self._merge_change(self.changes[self.current_diff], "lhs", "rhs"); break; default: return; @@ -764,18 +755,10 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { // Consume the event for suppressing "double action". event.preventDefault(); }); - - function get_changes() { - var lhs = self.editor[self.id+'-lhs'].getValue(); - var rhs = self.editor[self.id+'-rhs'].getValue(); - var d = new Mgly.diff(lhs, rhs, self.settings); - var changes = Mgly.DiffParser(d.normal_form()); - return changes; - } - }, _scroll_to_change : function(change) { + if (!change) return; var self = this; var led = self.editor[self.id+'-lhs']; var red = self.editor[self.id+'-rhs']; @@ -951,8 +934,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('change', 'diff time', timer.stop()); this.changes = Mgly.DiffParser(d.normal_form()); this.trace('change', 'parse time', timer.stop()); - if (this.current_diff===undefined && this.changes.length!==0) { - // go to first differnece on start-up + if (this.current_diff===undefined) { + // go to first difference on start-up this.current_diff = 0; this._scroll_to_change(this.changes[0]); } @@ -1342,20 +1325,15 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { } }); var change = self.changes[cid]; - - // kljh: not used - //var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)}; - self._merge_change(change, side, oside); return false; }); this.trace('change', 'markup buttons time', timer.stop()); }, _merge_change : function(change, side, oside) { - var editor_name1 = this.id + '-lhs'; - var editor_name2 = this.id + '-rhs'; - var led = this.editor[editor_name1]; - var red = this.editor[editor_name2]; + if (!change) return; + var led = this.editor[this.id+'-lhs']; + var red = this.editor[this.id+'-rhs']; var ed = {lhs:led, rhs:red}; @@ -1468,14 +1446,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end); ctx_lhs.beginPath(); - ctx_lhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; + ctx_lhs.fillStyle = this.settings.fgcolor[(this.current_diff==i?'c':'')+change['op']]; ctx_lhs.strokeStyle = '#000'; ctx_lhs.lineWidth = 0.5; ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); ctx_rhs.beginPath(); - ctx_rhs.fillStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; + ctx_rhs.fillStyle = this.settings.fgcolor[(this.current_diff==i?'c':'')+change['op']]; ctx_rhs.strokeStyle = '#000'; ctx_rhs.lineWidth = 0.5; ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); @@ -1494,8 +1472,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { // draw left box ctx.beginPath(); - ctx.strokeStyle = this.settings.fgcolor[(this.settings.current_diff==i?'c':'')+change['op']]; - ctx.lineWidth = (this.settings.current_diff==i) ? 1.5 : 1; + ctx.strokeStyle = this.settings.fgcolor[(this.current_diff==i?'c':'')+change['op']]; + ctx.lineWidth = (this.current_diff==i) ? 1.5 : 1; var rectWidth = this.draw_lhs_width; var rectHeight = lhs_y_end - lhs_y_start - 1; From 4f8c91c28a8ed764a3ee8d1193fd37561ccd4175 Mon Sep 17 00:00:00 2001 From: Kljh Date: Sun, 17 Aug 2014 08:25:16 +0100 Subject: [PATCH 7/8] Morning clean up. Restrict keyboard handler for the Mergely div to play nicely with other element on the page. Darker grey for active difference. --- lib/mergely.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mergely.js b/lib/mergely.js index af35d56..56ff7ec 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -391,7 +391,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { resize_timeout: 500, change_timeout: 150, fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color) - ca:'#4b73ff',cc:'#737373',cd:'#ff4f4f'}, // color for currently active difference (bright color) + ca:'#4b73ff',cc:'#434343',cd:'#ff4f4f'}, // color for currently active difference (bright color) bgcolor: '#eee', vpcolor: 'rgba(0, 0, 200, 0.5)', lhs: function(setValue) { }, @@ -691,7 +691,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { // kljh: register WinMerge style key bindings var self = this; - document.body.addEventListener("keydown", function(evt) { + document.getElementById(this.id).addEventListener("keydown", function(evt) { if (evt.defaultPrevented) { //return; // Should do nothing if the key event was already consumed. //console.warn("Should do nothing if the key event was already consumed."); From 3c4fe7168350ab995427292f1f95ce97f9dfa8d6 Mon Sep 17 00:00:00 2001 From: Kljh Date: Sun, 17 Aug 2014 09:14:55 +0100 Subject: [PATCH 8/8] Editors cursor are now moved to currently selected difference. --- lib/mergely.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/mergely.js b/lib/mergely.js index 56ff7ec..159bb40 100644 --- a/lib/mergely.js +++ b/lib/mergely.js @@ -723,7 +723,6 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { self.current_diff++; self.current_diff = Math.min(self.current_diff, self.changes.length-1); self._scroll_to_change(self.changes[self.current_diff]); - self._changed(self.id + '-lhs', self.id + '-rhs'); break; case "Up": @@ -733,7 +732,6 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { self.current_diff--; self.current_diff = Math.max(self.current_diff, 0); self._scroll_to_change(self.changes[self.current_diff]); - self._changed(self.id + '-lhs', self.id + '-rhs'); break; case "Left": @@ -765,6 +763,10 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { var yref = led.getScrollerElement().offsetHeight * 1/2; // center between >0 and 1/2 + // set cursors + led.setCursor(Math.max(change["lhs-line-from"],0), 0); // use led.getCursor().ch ? + red.setCursor(Math.max(change["rhs-line-from"],0), 0); + // using directly CodeMirror breaks canvas alignment // var ly = led.charCoords({line: Math.max(change["lhs-line-from"],0), ch: 0}, "local").top; @@ -1376,6 +1378,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { //reset ed['lhs'].setValue(ed['lhs'].getValue()); ed['rhs'].setValue(ed['rhs'].getValue()); + + this._scroll_to_change(change) }, _draw_info: function(editor_name1, editor_name2) { var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height();