New inner-line diff algorithm. Added method 'unmarkup' to clear markup. Added option: vpcolor. Fixed autoresize and resize.

This commit is contained in:
Jamie Peabody
2013-05-12 19:52:47 +01:00
parent a3e50f8d8e
commit ea2263ff29
11 changed files with 283 additions and 291 deletions

View File

@@ -113,6 +113,8 @@ $(document).ready(function () {
</dd>
<dt>bgcolor</dt>
<dd>The background color that mergely fills the margin canvas with. Defaults to <span class="code">'#eeeeee'<span></dd>
<dt>vwcolor</dt>
<dd>The margin/viewport indicator color. Defaults to <span class="code">'rgba(0, 0, 200, 0.5)'<span></dd>
<dt>fadein</dt>
<dd>A jQuery fade-in value to enable the editor to fade in. Set to empty string to disable. Defaults to <span class="code">'fast'<span></dd>
</dl>
@@ -159,10 +161,13 @@ $(document).ready(function () {
<dt>$(selector).mergely('clear', side)</dt>
<dd>Clears the editor contents.</dd>
<dt>$(selector).mergely('unmarkup')</dt>
<dd>Clears the editor markup.</dd>
<dt>$(selector).mergely('search', side, text)</dt>
<dd>Search the editor for <span class="code">text</span>. Repeating the call will find the next available token.</dd>
<dt>$(selector).resize()</dt>
<dt>$(selector).mergely('resize')</dt>
<dd>Resize the editor.</dd>
<dt>$(selector).mergely('update')</dt>

View File

@@ -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%;}

View File

@@ -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; }';
$('<style type="text/css">' + text + '</style>').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);

View File

@@ -1,56 +0,0 @@
<!--
This example demonstrates how to enable line wrapping
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" /><title>Mergely - Simple Example</title>
<meta http-equiv="X-UA-Compatible" content="chrome=1, IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<meta name="description" content="Merge and Diff your documents with diff online and share" />
<meta name="keywords" content="diff,merge,compare,jsdiff,comparison,difference,file,text,unix,patch,algorithm,saas,longest common subsequence" />
<meta name="author" content="Jamie Peabody" />
<!-- Requires jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js" type="text/javascript"></script>
<!-- Requires CodeMirror -->
<script type="text/javascript" src="../lib/codemirror.js"></script>
<link type="text/css" rel="stylesheet" href="../lib/codemirror.css" />
<!-- Requires Mergely -->
<script type="text/javascript" src="../lib/mergely.js"></script>
<link type="text/css" rel="stylesheet" href="../lib/mergely.css" />
<script type="text/javascript">
$(document).ready(function () {
$('#compare').mergely({
cmsettings: { readOnly: false, lineWrapping: true }
});
$.ajax({
type: 'GET', async: true, dataType: 'text',
url: 'lhs_long.txt',
success: function (response) {
$('#compare').mergely('lhs', response);
}
});
$.ajax({
type: 'GET', async: true, dataType: 'text',
url: 'rhs_long.txt',
success: function (response) {
$('#compare').mergely('rhs', response);
}
});
});
</script>
</head>
<body>
<div id="mergely-resizer">
<div id="compare">
</div>
</div>
</body>
</html>

View File

@@ -1 +0,0 @@
the quick red fox jumped over the hairy dog on a cold, wet, and windy day in January

View File

@@ -1 +0,0 @@
the quick brown fox jumped over the lazy dog on a cold, wet, and windy day in December

View File

@@ -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);
}
});
});
@@ -92,5 +104,7 @@ This example demonstrates how to enable line wrapping
</tr>
</table>
<div id="compare5"></div>
</body>
</html>

View File

@@ -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,

View File

@@ -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,6 +452,7 @@ jQuery.extend(Mgly.mergely.prototype, {
this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); });
// resize
if (this.settings.autoresize) {
var sz_timeout1 = null;
var sz = function() {
//self.em_height = null; //recalculate
@@ -470,6 +468,7 @@ jQuery.extend(Mgly.mergely.prototype, {
}
);
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,8 +510,10 @@ jQuery.extend(Mgly.mergely.prototype, {
options: function(opts) {
if (opts) {
jQuery.extend(this.settings, opts);
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,18 +1149,46 @@ jQuery.extend(Mgly.mergely.prototype, {
{ line: change[side + '-line-from'], ch: 0 },
{ line: change[side + '-line-to'], ch: line[side].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 {// 'a' or 'd'
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 { // 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
ed['lhs'].setValue(ed['lhs'].getValue());
ed['rhs'].setValue(ed['rhs'].getValue());
@@ -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,29 +1365,29 @@ 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) {