##// END OF EJS Templates
js: updated mergely to 3.4.1
marcink -
r347:99b6d323 default
parent child Browse files
Show More
@@ -1,41 +1,50 b''
1
2 /* required */
1 /* required */
3 .mergely-column textarea { width: 80px; height: 200px; }
2 .mergely-column textarea { width: 80px; height: 200px; }
4 .mergely-column { float: left; }
3 .mergely-column { float: left; }
5 .mergely-margin { float: left; }
4 .mergely-margin { float: left; }
6 .mergely-canvas { float: left; width: 28px; }
5 .mergely-canvas { float: left; width: 28px; }
7
6
8 /* resizeable */
7 /* resizeable */
9 .mergely-resizer { width: 100%; height: 100%; }
8 .mergely-resizer { width: 100%; height: 100%; }
10
9
11 /* style configuration */
10 /* style configuration */
12 .mergely-column { border: 1px solid #ccc; }
11 .mergely-column { border: 1px solid #ccc; }
13 .mergely-active { border: 1px solid #a3d1ff; }
12 .mergely-active { border: 1px solid #a3d1ff; }
14
13
15 .mergely.a.rhs.start { border-top: 1px solid #ddffdd; }
14 .mergely.a,.mergely.d,.mergely.c { color: #000; }
16 .mergely.a.lhs.start.end,
17 .mergely.a.rhs.end { border-bottom: 1px solid #ddffdd; }
18 .mergely.a.rhs { background-color: #ddffdd; }
19 .mergely.a.lhs.start.end.first { border-bottom: 0; border-top: 1px solid #ddffdd; }
20
15
21 .mergely.d.lhs { background-color: #edc0c0; }
16 .mergely.a.rhs.start { border-top: 1px solid #a3d1ff; }
17 .mergely.a.lhs.start.end,
18 .mergely.a.rhs.end { border-bottom: 1px solid #a3d1ff; }
19 .mergely.a.rhs { background-color: #ddeeff; }
20 .mergely.a.lhs.start.end.first { border-bottom: 0; border-top: 1px solid #a3d1ff; }
21
22 .mergely.d.lhs { background-color: #ffe9e9; }
22 .mergely.d.lhs.end,
23 .mergely.d.lhs.end,
23 .mergely.d.rhs.start.end { border-bottom: 1px solid #ffdddd; }
24 .mergely.d.rhs.start.end { border-bottom: 1px solid #f8e8e8; }
24 .mergely.d.rhs.start.end.first { border-bottom: 0; border-top: 1px solid #ffdddd; }
25 .mergely.d.rhs.start.end.first { border-bottom: 0; border-top: 1px solid #f8e8e8; }
25 .mergely.d.lhs.start { border-top: 1px solid #ffdddd; }
26 .mergely.d.lhs.start { border-top: 1px solid #f8e8e8; }
26
27
27 .mergely.c.lhs,
28 .mergely.c.lhs,
28 .mergely.c.rhs { background-color: #fafafa; }
29 .mergely.c.rhs { background-color: #fafafa; }
29 .mergely.c.lhs.start,
30 .mergely.c.lhs.start,
30 .mergely.c.rhs.start { border-top: 1px solid #a3a3a3; }
31 .mergely.c.rhs.start { border-top: 1px solid #a3a3a3; }
31 .mergely.c.lhs.end,
32 .mergely.c.lhs.end,
32 .mergely.c.rhs.end { border-bottom: 1px solid #a3a3a3; }
33 .mergely.c.rhs.end { border-bottom: 1px solid #a3a3a3; }
33
34
34 .mergely.ch.a.rhs { background-color: #ddffdd; }
35 .mergely.ch.a.rhs { background-color: #ddeeff; }
35 .mergely.ch.d.lhs { background-color: #ffdddd; }
36 .mergely.ch.d.lhs { background-color: #ffe9e9; text-decoration: line-through; color: red !important; }
36
37
37
38 .mergely-margin #compare-lhs-margin,
38 .mergely-margin #compare-lhs-margin,
39 .mergely-margin #compare-rhs-margin {
39 .mergely-margin #compare-rhs-margin {
40 cursor: pointer
40 cursor: pointer
41 }
41 }
42
43 .mergely.current.start { border-top: 1px solid #000 !important; }
44 .mergely.current.end { border-bottom: 1px solid #000 !important; }
45 .mergely.current.lhs.a.start.end,
46 .mergely.current.rhs.d.start.end { border-top: 0 !important; }
47 .mergely.current.CodeMirror-linenumber { color: #F9F9F9; font-weight: bold; background-color: #777; }
48
49 .CodeMirror-linenumber { cursor: pointer; }
50 .CodeMirror-code { color: #717171; } No newline at end of file
@@ -1,1606 +1,1669 b''
1 "use strict";
1 "use strict";
2
2
3 (function( window, document, jQuery, CodeMirror ){
3 (function( window, document, jQuery, CodeMirror ){
4
4
5 var Mgly = {};
5 var Mgly = {};
6
6
7 Mgly.Timer = function(){
7 Mgly.Timer = function(){
8 var self = this;
8 var self = this;
9 self.start = function() { self.t0 = new Date().getTime(); };
9 self.start = function() { self.t0 = new Date().getTime(); };
10 self.stop = function() {
10 self.stop = function() {
11 var t1 = new Date().getTime();
11 var t1 = new Date().getTime();
12 var d = t1 - self.t0;
12 var d = t1 - self.t0;
13 self.t0 = t1;
13 self.t0 = t1;
14 return d;
14 return d;
15 };
15 };
16 self.start();
16 self.start();
17 };
17 };
18
18
19 Mgly.ChangeExpression = new RegExp(/(^(?![><\-])*\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
19 Mgly.ChangeExpression = new RegExp(/(^(?![><\-])*\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
20
20
21 Mgly.DiffParser = function(diff) {
21 Mgly.DiffParser = function(diff) {
22 var changes = [];
22 var changes = [];
23 var change_id = 0;
23 var change_id = 0;
24 // parse diff
24 // parse diff
25 var diff_lines = diff.split(/\n/);
25 var diff_lines = diff.split(/\n/);
26 for (var i = 0; i < diff_lines.length; ++i) {
26 for (var i = 0; i < diff_lines.length; ++i) {
27 if (diff_lines[i].length == 0) continue;
27 if (diff_lines[i].length == 0) continue;
28 var change = {};
28 var change = {};
29 var test = Mgly.ChangeExpression.exec(diff_lines[i]);
29 var test = Mgly.ChangeExpression.exec(diff_lines[i]);
30 if (test == null) continue;
30 if (test == null) continue;
31 // lines are zero-based
31 // lines are zero-based
32 var fr = test[1].split(',');
32 var fr = test[1].split(',');
33 change['lhs-line-from'] = fr[0] - 1;
33 change['lhs-line-from'] = fr[0] - 1;
34 if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1;
34 if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1;
35 else change['lhs-line-to'] = fr[1] - 1;
35 else change['lhs-line-to'] = fr[1] - 1;
36 var to = test[3].split(',');
36 var to = test[3].split(',');
37 change['rhs-line-from'] = to[0] - 1;
37 change['rhs-line-from'] = to[0] - 1;
38 if (to.length == 1) change['rhs-line-to'] = to[0] - 1;
38 if (to.length == 1) change['rhs-line-to'] = to[0] - 1;
39 else change['rhs-line-to'] = to[1] - 1;
39 else change['rhs-line-to'] = to[1] - 1;
40 change['op'] = test[2];
40 change['op'] = test[2];
41 changes[change_id++] = change;
41 changes[change_id++] = change;
42 }
42 }
43 return changes;
43 return changes;
44 };
44 };
45
45
46 Mgly.sizeOf = function(obj) {
46 Mgly.sizeOf = function(obj) {
47 var size = 0, key;
47 var size = 0, key;
48 for (key in obj) {
48 for (key in obj) {
49 if (obj.hasOwnProperty(key)) size++;
49 if (obj.hasOwnProperty(key)) size++;
50 }
50 }
51 return size;
51 return size;
52 };
52 };
53
53
54 Mgly.LCS = function(x, y) {
54 Mgly.LCS = function(x, y) {
55 this.x = x.replace(/[ ]{1}/g, '\n');
55 this.x = x.replace(/[ ]{1}/g, '\n');
56 this.y = y.replace(/[ ]{1}/g, '\n');
56 this.y = y.replace(/[ ]{1}/g, '\n');
57 };
57 };
58
58
59 jQuery.extend(Mgly.LCS.prototype, {
59 jQuery.extend(Mgly.LCS.prototype, {
60 clear: function() { this.ready = 0; },
60 clear: function() { this.ready = 0; },
61 diff: function(added, removed) {
61 diff: function(added, removed) {
62 var d = new Mgly.diff(this.x, this.y, {ignorews: false});
62 var d = new Mgly.diff(this.x, this.y, {ignorews: false});
63 var changes = Mgly.DiffParser(d.normal_form());
63 var changes = Mgly.DiffParser(d.normal_form());
64 var li = 0, lj = 0;
64 var li = 0, lj = 0;
65 for (var i = 0; i < changes.length; ++i) {
65 for (var i = 0; i < changes.length; ++i) {
66 var change = changes[i];
66 var change = changes[i];
67 if (change.op != 'a') {
67 if (change.op != 'a') {
68 // find the starting index of the line
68 // find the starting index of the line
69 li = d.getLines('lhs').slice(0, change['lhs-line-from']).join(' ').length;
69 li = d.getLines('lhs').slice(0, change['lhs-line-from']).join(' ').length;
70 // get the index of the the span of the change
70 // get the index of the the span of the change
71 lj = change['lhs-line-to'] + 1;
71 lj = change['lhs-line-to'] + 1;
72 // get the changed text
72 // get the changed text
73 var lchange = d.getLines('lhs').slice(change['lhs-line-from'], lj).join(' ');
73 var lchange = d.getLines('lhs').slice(change['lhs-line-from'], lj).join(' ');
74 if (change.op == 'd') lchange += ' ';// include the leading space
74 if (change.op == 'd') lchange += ' ';// include the leading space
75 else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word
75 else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word
76 // output the changed index and text
76 // output the changed index and text
77 removed(li, li + lchange.length);
77 removed(li, li + lchange.length);
78 }
78 }
79 if (change.op != 'd') {
79 if (change.op != 'd') {
80 // find the starting index of the line
80 // find the starting index of the line
81 li = d.getLines('rhs').slice(0, change['rhs-line-from']).join(' ').length;
81 li = d.getLines('rhs').slice(0, change['rhs-line-from']).join(' ').length;
82 // get the index of the the span of the change
82 // get the index of the the span of the change
83 lj = change['rhs-line-to'] + 1;
83 lj = change['rhs-line-to'] + 1;
84 // get the changed text
84 // get the changed text
85 var rchange = d.getLines('rhs').slice(change['rhs-line-from'], lj).join(' ');
85 var rchange = d.getLines('rhs').slice(change['rhs-line-from'], lj).join(' ');
86 if (change.op == 'a') rchange += ' ';// include the leading space
86 if (change.op == 'a') rchange += ' ';// include the leading space
87 else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word
87 else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word
88 // output the changed index and text
88 // output the changed index and text
89 added(li, li + rchange.length);
89 added(li, li + rchange.length);
90 }
90 }
91 }
91 }
92 }
92 }
93 });
93 });
94
94
95 Mgly.CodeifyText = function(settings) {
95 Mgly.CodeifyText = function(settings) {
96 this._max_code = 0;
96 this._max_code = 0;
97 this._diff_codes = {};
97 this._diff_codes = {};
98 this.ctxs = {};
98 this.ctxs = {};
99 this.options = {ignorews: false};
99 this.options = {ignorews: false};
100 jQuery.extend(this, settings);
100 jQuery.extend(this, settings);
101 this.lhs = settings.lhs.split('\n');
101 this.lhs = settings.lhs.split('\n');
102 this.rhs = settings.rhs.split('\n');
102 this.rhs = settings.rhs.split('\n');
103 };
103 };
104
104
105 jQuery.extend(Mgly.CodeifyText.prototype, {
105 jQuery.extend(Mgly.CodeifyText.prototype, {
106 getCodes: function(side) {
106 getCodes: function(side) {
107 if (!this.ctxs.hasOwnProperty(side)) {
107 if (!this.ctxs.hasOwnProperty(side)) {
108 var ctx = this._diff_ctx(this[side]);
108 var ctx = this._diff_ctx(this[side]);
109 this.ctxs[side] = ctx;
109 this.ctxs[side] = ctx;
110 ctx.codes.length = Object.keys(ctx.codes).length;
110 ctx.codes.length = Object.keys(ctx.codes).length;
111 }
111 }
112 return this.ctxs[side].codes;
112 return this.ctxs[side].codes;
113 },
113 },
114 getLines: function(side) {
114 getLines: function(side) {
115 return this.ctxs[side].lines;
115 return this.ctxs[side].lines;
116 },
116 },
117 _diff_ctx: function(lines) {
117 _diff_ctx: function(lines) {
118 var ctx = {i: 0, codes: {}, lines: lines};
118 var ctx = {i: 0, codes: {}, lines: lines};
119 this._codeify(lines, ctx);
119 this._codeify(lines, ctx);
120 return ctx;
120 return ctx;
121 },
121 },
122 _codeify: function(lines, ctx) {
122 _codeify: function(lines, ctx) {
123 var code = this._max_code;
123 var code = this._max_code;
124 for (var i = 0; i < lines.length; ++i) {
124 for (var i = 0; i < lines.length; ++i) {
125 var line = lines[i];
125 var line = lines[i];
126 if (this.options.ignorews) {
126 if (this.options.ignorews) {
127 line = line.replace(/\s+/g, '');
127 line = line.replace(/\s+/g, '');
128 }
128 }
129 var aCode = this._diff_codes[line];
129 var aCode = this._diff_codes[line];
130 if (aCode != undefined) {
130 if (aCode != undefined) {
131 ctx.codes[i] = aCode;
131 ctx.codes[i] = aCode;
132 }
132 }
133 else {
133 else {
134 this._max_code++;
134 this._max_code++;
135 this._diff_codes[line] = this._max_code;
135 this._diff_codes[line] = this._max_code;
136 ctx.codes[i] = this._max_code;
136 ctx.codes[i] = this._max_code;
137 }
137 }
138 }
138 }
139 }
139 }
140 });
140 });
141
141
142 Mgly.diff = function(lhs, rhs, options) {
142 Mgly.diff = function(lhs, rhs, options) {
143 var opts = jQuery.extend({ignorews: false}, options);
143 var opts = jQuery.extend({ignorews: false}, options);
144 this.codeify = new Mgly.CodeifyText({
144 this.codeify = new Mgly.CodeifyText({
145 lhs: lhs,
145 lhs: lhs,
146 rhs: rhs,
146 rhs: rhs,
147 options: opts
147 options: opts
148 });
148 });
149 var lhs_ctx = {
149 var lhs_ctx = {
150 codes: this.codeify.getCodes('lhs'),
150 codes: this.codeify.getCodes('lhs'),
151 modified: {}
151 modified: {}
152 };
152 };
153 var rhs_ctx = {
153 var rhs_ctx = {
154 codes: this.codeify.getCodes('rhs'),
154 codes: this.codeify.getCodes('rhs'),
155 modified: {}
155 modified: {}
156 };
156 };
157 var max = (lhs_ctx.codes.length + rhs_ctx.codes.length + 1);
157 var max = (lhs_ctx.codes.length + rhs_ctx.codes.length + 1);
158 var vector_d = [];
158 var vector_d = [];
159 var vector_u = [];
159 var vector_u = [];
160 this._lcs(lhs_ctx, 0, lhs_ctx.codes.length, rhs_ctx, 0, rhs_ctx.codes.length, vector_u, vector_d);
160 this._lcs(lhs_ctx, 0, lhs_ctx.codes.length, rhs_ctx, 0, rhs_ctx.codes.length, vector_u, vector_d);
161 this._optimize(lhs_ctx);
161 this._optimize(lhs_ctx);
162 this._optimize(rhs_ctx);
162 this._optimize(rhs_ctx);
163 this.items = this._create_diffs(lhs_ctx, rhs_ctx);
163 this.items = this._create_diffs(lhs_ctx, rhs_ctx);
164 };
164 };
165
165
166 jQuery.extend(Mgly.diff.prototype, {
166 jQuery.extend(Mgly.diff.prototype, {
167 changes: function() { return this.items; },
167 changes: function() { return this.items; },
168 getLines: function(side) {
168 getLines: function(side) {
169 return this.codeify.getLines(side);
169 return this.codeify.getLines(side);
170 },
170 },
171 normal_form: function() {
171 normal_form: function() {
172 var nf = '';
172 var nf = '';
173 for (var index = 0; index < this.items.length; ++index) {
173 for (var index = 0; index < this.items.length; ++index) {
174 var item = this.items[index];
174 var item = this.items[index];
175 var lhs_str = '';
175 var lhs_str = '';
176 var rhs_str = '';
176 var rhs_str = '';
177 var change = 'c';
177 var change = 'c';
178 if (item.lhs_deleted_count == 0 && item.rhs_inserted_count > 0) change = 'a';
178 if (item.lhs_deleted_count == 0 && item.rhs_inserted_count > 0) change = 'a';
179 else if (item.lhs_deleted_count > 0 && item.rhs_inserted_count == 0) change = 'd';
179 else if (item.lhs_deleted_count > 0 && item.rhs_inserted_count == 0) change = 'd';
180
180
181 if (item.lhs_deleted_count == 1) lhs_str = item.lhs_start + 1;
181 if (item.lhs_deleted_count == 1) lhs_str = item.lhs_start + 1;
182 else if (item.lhs_deleted_count == 0) lhs_str = item.lhs_start;
182 else if (item.lhs_deleted_count == 0) lhs_str = item.lhs_start;
183 else lhs_str = (item.lhs_start + 1) + ',' + (item.lhs_start + item.lhs_deleted_count);
183 else lhs_str = (item.lhs_start + 1) + ',' + (item.lhs_start + item.lhs_deleted_count);
184
184
185 if (item.rhs_inserted_count == 1) rhs_str = item.rhs_start + 1;
185 if (item.rhs_inserted_count == 1) rhs_str = item.rhs_start + 1;
186 else if (item.rhs_inserted_count == 0) rhs_str = item.rhs_start;
186 else if (item.rhs_inserted_count == 0) rhs_str = item.rhs_start;
187 else rhs_str = (item.rhs_start + 1) + ',' + (item.rhs_start + item.rhs_inserted_count);
187 else rhs_str = (item.rhs_start + 1) + ',' + (item.rhs_start + item.rhs_inserted_count);
188 nf += lhs_str + change + rhs_str + '\n';
188 nf += lhs_str + change + rhs_str + '\n';
189
189
190 var lhs_lines = this.getLines('lhs');
190 var lhs_lines = this.getLines('lhs');
191 var rhs_lines = this.getLines('rhs');
191 var rhs_lines = this.getLines('rhs');
192 if (rhs_lines && lhs_lines) {
192 if (rhs_lines && lhs_lines) {
193 var i;
193 var i;
194 // if rhs/lhs lines have been retained, output contextual diff
194 // if rhs/lhs lines have been retained, output contextual diff
195 for (i = item.lhs_start; i < item.lhs_start + item.lhs_deleted_count; ++i) {
195 for (i = item.lhs_start; i < item.lhs_start + item.lhs_deleted_count; ++i) {
196 nf += '< ' + lhs_lines[i] + '\n';
196 nf += '< ' + lhs_lines[i] + '\n';
197 }
197 }
198 if (item.rhs_inserted_count && item.lhs_deleted_count) nf += '---\n';
198 if (item.rhs_inserted_count && item.lhs_deleted_count) nf += '---\n';
199 for (i = item.rhs_start; i < item.rhs_start + item.rhs_inserted_count; ++i) {
199 for (i = item.rhs_start; i < item.rhs_start + item.rhs_inserted_count; ++i) {
200 nf += '> ' + rhs_lines[i] + '\n';
200 nf += '> ' + rhs_lines[i] + '\n';
201 }
201 }
202 }
202 }
203 }
203 }
204 return nf;
204 return nf;
205 },
205 },
206 _lcs: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) {
206 _lcs: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) {
207 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_lower] == rhs_ctx.codes[rhs_lower]) ) {
207 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_lower] == rhs_ctx.codes[rhs_lower]) ) {
208 ++lhs_lower;
208 ++lhs_lower;
209 ++rhs_lower;
209 ++rhs_lower;
210 }
210 }
211 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_upper - 1] == rhs_ctx.codes[rhs_upper - 1]) ) {
211 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_upper - 1] == rhs_ctx.codes[rhs_upper - 1]) ) {
212 --lhs_upper;
212 --lhs_upper;
213 --rhs_upper;
213 --rhs_upper;
214 }
214 }
215 if (lhs_lower == lhs_upper) {
215 if (lhs_lower == lhs_upper) {
216 while (rhs_lower < rhs_upper) {
216 while (rhs_lower < rhs_upper) {
217 rhs_ctx.modified[ rhs_lower++ ] = true;
217 rhs_ctx.modified[ rhs_lower++ ] = true;
218 }
218 }
219 }
219 }
220 else if (rhs_lower == rhs_upper) {
220 else if (rhs_lower == rhs_upper) {
221 while (lhs_lower < lhs_upper) {
221 while (lhs_lower < lhs_upper) {
222 lhs_ctx.modified[ lhs_lower++ ] = true;
222 lhs_ctx.modified[ lhs_lower++ ] = true;
223 }
223 }
224 }
224 }
225 else {
225 else {
226 var sms = this._sms(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d);
226 var sms = this._sms(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d);
227 this._lcs(lhs_ctx, lhs_lower, sms.x, rhs_ctx, rhs_lower, sms.y, vector_u, vector_d);
227 this._lcs(lhs_ctx, lhs_lower, sms.x, rhs_ctx, rhs_lower, sms.y, vector_u, vector_d);
228 this._lcs(lhs_ctx, sms.x, lhs_upper, rhs_ctx, sms.y, rhs_upper, vector_u, vector_d);
228 this._lcs(lhs_ctx, sms.x, lhs_upper, rhs_ctx, sms.y, rhs_upper, vector_u, vector_d);
229 }
229 }
230 },
230 },
231 _sms: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) {
231 _sms: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) {
232 var max = lhs_ctx.codes.length + rhs_ctx.codes.length + 1;
232 var max = lhs_ctx.codes.length + rhs_ctx.codes.length + 1;
233 var kdown = lhs_lower - rhs_lower;
233 var kdown = lhs_lower - rhs_lower;
234 var kup = lhs_upper - rhs_upper;
234 var kup = lhs_upper - rhs_upper;
235 var delta = (lhs_upper - lhs_lower) - (rhs_upper - rhs_lower);
235 var delta = (lhs_upper - lhs_lower) - (rhs_upper - rhs_lower);
236 var odd = (delta & 1) != 0;
236 var odd = (delta & 1) != 0;
237 var offset_down = max - kdown;
237 var offset_down = max - kdown;
238 var offset_up = max - kup;
238 var offset_up = max - kup;
239 var maxd = ((lhs_upper - lhs_lower + rhs_upper - rhs_lower) / 2) + 1;
239 var maxd = ((lhs_upper - lhs_lower + rhs_upper - rhs_lower) / 2) + 1;
240 vector_d[ offset_down + kdown + 1 ] = lhs_lower;
240 vector_d[ offset_down + kdown + 1 ] = lhs_lower;
241 vector_u[ offset_up + kup - 1 ] = lhs_upper;
241 vector_u[ offset_up + kup - 1 ] = lhs_upper;
242 var ret = {x:0,y:0}, d, k, x, y;
242 var ret = {x:0,y:0}, d, k, x, y;
243 for (d = 0; d <= maxd; ++d) {
243 for (d = 0; d <= maxd; ++d) {
244 for (k = kdown - d; k <= kdown + d; k += 2) {
244 for (k = kdown - d; k <= kdown + d; k += 2) {
245 if (k == kdown - d) {
245 if (k == kdown - d) {
246 x = vector_d[ offset_down + k + 1 ];//down
246 x = vector_d[ offset_down + k + 1 ];//down
247 }
247 }
248 else {
248 else {
249 x = vector_d[ offset_down + k - 1 ] + 1;//right
249 x = vector_d[ offset_down + k - 1 ] + 1;//right
250 if ((k < (kdown + d)) && (vector_d[ offset_down + k + 1 ] >= x)) {
250 if ((k < (kdown + d)) && (vector_d[ offset_down + k + 1 ] >= x)) {
251 x = vector_d[ offset_down + k + 1 ];//down
251 x = vector_d[ offset_down + k + 1 ];//down
252 }
252 }
253 }
253 }
254 y = x - k;
254 y = x - k;
255 // find the end of the furthest reaching forward D-path in diagonal k.
255 // find the end of the furthest reaching forward D-path in diagonal k.
256 while ((x < lhs_upper) && (y < rhs_upper) && (lhs_ctx.codes[x] == rhs_ctx.codes[y])) {
256 while ((x < lhs_upper) && (y < rhs_upper) && (lhs_ctx.codes[x] == rhs_ctx.codes[y])) {
257 x++; y++;
257 x++; y++;
258 }
258 }
259 vector_d[ offset_down + k ] = x;
259 vector_d[ offset_down + k ] = x;
260 // overlap ?
260 // overlap ?
261 if (odd && (kup - d < k) && (k < kup + d)) {
261 if (odd && (kup - d < k) && (k < kup + d)) {
262 if (vector_u[offset_up + k] <= vector_d[offset_down + k]) {
262 if (vector_u[offset_up + k] <= vector_d[offset_down + k]) {
263 ret.x = vector_d[offset_down + k];
263 ret.x = vector_d[offset_down + k];
264 ret.y = vector_d[offset_down + k] - k;
264 ret.y = vector_d[offset_down + k] - k;
265 return (ret);
265 return (ret);
266 }
266 }
267 }
267 }
268 }
268 }
269 // Extend the reverse path.
269 // Extend the reverse path.
270 for (k = kup - d; k <= kup + d; k += 2) {
270 for (k = kup - d; k <= kup + d; k += 2) {
271 // find the only or better starting point
271 // find the only or better starting point
272 if (k == kup + d) {
272 if (k == kup + d) {
273 x = vector_u[offset_up + k - 1]; // up
273 x = vector_u[offset_up + k - 1]; // up
274 } else {
274 } else {
275 x = vector_u[offset_up + k + 1] - 1; // left
275 x = vector_u[offset_up + k + 1] - 1; // left
276 if ((k > kup - d) && (vector_u[offset_up + k - 1] < x))
276 if ((k > kup - d) && (vector_u[offset_up + k - 1] < x))
277 x = vector_u[offset_up + k - 1]; // up
277 x = vector_u[offset_up + k - 1]; // up
278 }
278 }
279 y = x - k;
279 y = x - k;
280 while ((x > lhs_lower) && (y > rhs_lower) && (lhs_ctx.codes[x - 1] == rhs_ctx.codes[y - 1])) {
280 while ((x > lhs_lower) && (y > rhs_lower) && (lhs_ctx.codes[x - 1] == rhs_ctx.codes[y - 1])) {
281 // diagonal
281 // diagonal
282 x--;
282 x--;
283 y--;
283 y--;
284 }
284 }
285 vector_u[offset_up + k] = x;
285 vector_u[offset_up + k] = x;
286 // overlap ?
286 // overlap ?
287 if (!odd && (kdown - d <= k) && (k <= kdown + d)) {
287 if (!odd && (kdown - d <= k) && (k <= kdown + d)) {
288 if (vector_u[offset_up + k] <= vector_d[offset_down + k]) {
288 if (vector_u[offset_up + k] <= vector_d[offset_down + k]) {
289 ret.x = vector_d[offset_down + k];
289 ret.x = vector_d[offset_down + k];
290 ret.y = vector_d[offset_down + k] - k;
290 ret.y = vector_d[offset_down + k] - k;
291 return (ret);
291 return (ret);
292 }
292 }
293 }
293 }
294 }
294 }
295 }
295 }
296 throw "the algorithm should never come here.";
296 throw "the algorithm should never come here.";
297 },
297 },
298 _optimize: function(ctx) {
298 _optimize: function(ctx) {
299 var start = 0, end = 0;
299 var start = 0, end = 0;
300 while (start < ctx.length) {
300 while (start < ctx.codes.length) {
301 while ((start < ctx.length) && (ctx.modified[start] == undefined || ctx.modified[start] == false)) {
301 while ((start < ctx.codes.length) && (ctx.modified[start] == undefined || ctx.modified[start] == false)) {
302 start++;
302 start++;
303 }
303 }
304 end = start;
304 end = start;
305 while ((end < ctx.length) && (ctx.modified[end] == true)) {
305 while ((end < ctx.codes.length) && (ctx.modified[end] == true)) {
306 end++;
306 end++;
307 }
307 }
308 if ((end < ctx.length) && (ctx.ctx[start] == ctx.codes[end])) {
308 if ((end < ctx.codes.length) && (ctx.codes[start] == ctx.codes[end])) {
309 ctx.modified[start] = false;
309 ctx.modified[start] = false;
310 ctx.modified[end] = true;
310 ctx.modified[end] = true;
311 }
311 }
312 else {
312 else {
313 start = end;
313 start = end;
314 }
314 }
315 }
315 }
316 },
316 },
317 _create_diffs: function(lhs_ctx, rhs_ctx) {
317 _create_diffs: function(lhs_ctx, rhs_ctx) {
318 var items = [];
318 var items = [];
319 var lhs_start = 0, rhs_start = 0;
319 var lhs_start = 0, rhs_start = 0;
320 var lhs_line = 0, rhs_line = 0;
320 var lhs_line = 0, rhs_line = 0;
321
321
322 while (lhs_line < lhs_ctx.codes.length || rhs_line < rhs_ctx.codes.length) {
322 while (lhs_line < lhs_ctx.codes.length || rhs_line < rhs_ctx.codes.length) {
323 if ((lhs_line < lhs_ctx.codes.length) && (!lhs_ctx.modified[lhs_line])
323 if ((lhs_line < lhs_ctx.codes.length) && (!lhs_ctx.modified[lhs_line])
324 && (rhs_line < rhs_ctx.codes.length) && (!rhs_ctx.modified[rhs_line])) {
324 && (rhs_line < rhs_ctx.codes.length) && (!rhs_ctx.modified[rhs_line])) {
325 // equal lines
325 // equal lines
326 lhs_line++;
326 lhs_line++;
327 rhs_line++;
327 rhs_line++;
328 }
328 }
329 else {
329 else {
330 // maybe deleted and/or inserted lines
330 // maybe deleted and/or inserted lines
331 lhs_start = lhs_line;
331 lhs_start = lhs_line;
332 rhs_start = rhs_line;
332 rhs_start = rhs_line;
333
333
334 while (lhs_line < lhs_ctx.codes.length && (rhs_line >= rhs_ctx.codes.length || lhs_ctx.modified[lhs_line]))
334 while (lhs_line < lhs_ctx.codes.length && (rhs_line >= rhs_ctx.codes.length || lhs_ctx.modified[lhs_line]))
335 lhs_line++;
335 lhs_line++;
336
336
337 while (rhs_line < rhs_ctx.codes.length && (lhs_line >= lhs_ctx.codes.length || rhs_ctx.modified[rhs_line]))
337 while (rhs_line < rhs_ctx.codes.length && (lhs_line >= lhs_ctx.codes.length || rhs_ctx.modified[rhs_line]))
338 rhs_line++;
338 rhs_line++;
339
339
340 if ((lhs_start < lhs_line) || (rhs_start < rhs_line)) {
340 if ((lhs_start < lhs_line) || (rhs_start < rhs_line)) {
341 // store a new difference-item
341 // store a new difference-item
342 items.push({
342 items.push({
343 lhs_start: lhs_start,
343 lhs_start: lhs_start,
344 rhs_start: rhs_start,
344 rhs_start: rhs_start,
345 lhs_deleted_count: lhs_line - lhs_start,
345 lhs_deleted_count: lhs_line - lhs_start,
346 rhs_inserted_count: rhs_line - rhs_start
346 rhs_inserted_count: rhs_line - rhs_start
347 });
347 });
348 }
348 }
349 }
349 }
350 }
350 }
351 return items;
351 return items;
352 }
352 }
353 });
353 });
354
354
355 Mgly.mergely = function(el, options) {
355 Mgly.mergely = function(el, options) {
356 if (el) {
356 if (el) {
357 this.init(el, options);
357 this.init(el, options);
358 }
358 }
359 };
359 };
360
360
361 jQuery.extend(Mgly.mergely.prototype, {
361 jQuery.extend(Mgly.mergely.prototype, {
362 name: 'mergely',
362 name: 'mergely',
363 //http://jupiterjs.com/news/writing-the-perfect-jquery-plugin
363 //http://jupiterjs.com/news/writing-the-perfect-jquery-plugin
364 init: function(el, options) {
364 init: function(el, options) {
365 this.diffView = new Mgly.CodeMirrorDiffView(el, options);
365 this.diffView = new Mgly.CodeMirrorDiffView(el, options);
366 this.bind(el);
366 this.bind(el);
367 },
367 },
368 bind: function(el) {
368 bind: function(el) {
369 this.diffView.bind(el);
369 this.diffView.bind(el);
370 }
370 }
371 });
371 });
372
372
373 Mgly.CodeMirrorDiffView = function(el, options) {
373 Mgly.CodeMirrorDiffView = function(el, options) {
374 CodeMirror.defineExtension('centerOnCursor', function() {
374 CodeMirror.defineExtension('centerOnCursor', function() {
375 var coords = this.cursorCoords(null, 'local');
375 var coords = this.cursorCoords(null, 'local');
376 this.scrollTo(null,
376 this.scrollTo(null,
377 (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2));
377 (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2));
378 });
378 });
379 this.init(el, options);
379 this.init(el, options);
380 };
380 };
381
381
382 jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
382 jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
383 init: function(el, options) {
383 init: function(el, options) {
384 this.settings = {
384 this.settings = {
385 autoupdate: true,
385 autoupdate: true,
386 autoresize: true,
386 autoresize: true,
387 rhs_margin: 'right',
387 rhs_margin: 'right',
388 wrap_lines: false,
388 wrap_lines: false,
389 line_numbers: true,
389 line_numbers: true,
390 lcs: true,
390 lcs: true,
391 sidebar: true,
391 sidebar: true,
392 viewport: false,
392 viewport: false,
393 ignorews: false,
393 ignorews: false,
394 fadein: 'fast',
394 fadein: 'fast',
395 editor_width: '650px',
395 editor_width: '650px',
396 editor_height: '400px',
396 editor_height: '400px',
397 resize_timeout: 500,
397 resize_timeout: 500,
398 change_timeout: 150,
398 change_timeout: 150,
399 fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color)
399 fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color)
400 ca:'#4b73ff',cc:'#434343',cd:'#ff4f4f'}, // color for currently active difference (bright color)
400 ca:'#4b73ff',cc:'#434343',cd:'#ff4f4f'}, // color for currently active difference (bright color)
401 bgcolor: '#eee',
401 bgcolor: '#eee',
402 vpcolor: 'rgba(0, 0, 200, 0.5)',
402 vpcolor: 'rgba(0, 0, 200, 0.5)',
403 lhs: function(setValue) { },
403 lhs: function(setValue) { },
404 rhs: function(setValue) { },
404 rhs: function(setValue) { },
405 loaded: function() { },
405 loaded: function() { },
406 _auto_width: function(w) { return w; },
406 _auto_width: function(w) { return w; },
407 resize: function(init) {
407 resize: function(init) {
408 var scrollbar = init ? 16 : 0;
408 var scrollbar = init ? 16 : 0;
409 var w = jQuery(el).parent().width() + scrollbar, h = 0;
409 var w = jQuery(el).parent().width() + scrollbar, h = 0;
410 if (this.width == 'auto') {
410 if (this.width == 'auto') {
411 w = this._auto_width(w);
411 w = this._auto_width(w);
412 }
412 }
413 else {
413 else {
414 w = this.width;
414 w = this.width;
415 this.editor_width = w;
415 this.editor_width = w;
416 }
416 }
417 if (this.height == 'auto') {
417 if (this.height == 'auto') {
418 //h = this._auto_height(h);
418 //h = this._auto_height(h);
419 h = jQuery(el).parent().height();
419 h = jQuery(el).parent().height();
420 }
420 }
421 else {
421 else {
422 h = this.height;
422 h = this.height;
423 this.editor_height = h;
423 this.editor_height = h;
424 }
424 }
425 var content_width = w / 2.0 - 2 * 8 - 8;
425 var content_width = w / 2.0 - 2 * 8 - 8;
426 var content_height = h;
426 var content_height = h;
427 var self = jQuery(el);
427 var self = jQuery(el);
428 self.find('.mergely-column').css({ width: content_width + 'px' });
428 self.find('.mergely-column').css({ width: content_width + 'px' });
429 self.find('.mergely-column, .mergely-canvas, .mergely-margin, .mergely-column textarea, .CodeMirror-scroll, .cm-s-default').css({ height: content_height + 'px' });
429 self.find('.mergely-column, .mergely-canvas, .mergely-margin, .mergely-column textarea, .CodeMirror-scroll, .cm-s-default').css({ height: content_height + 'px' });
430 self.find('.mergely-canvas').css({ height: content_height + 'px' });
430 self.find('.mergely-canvas').css({ height: content_height + 'px' });
431 self.find('.mergely-column textarea').css({ width: content_width + 'px' });
431 self.find('.mergely-column textarea').css({ width: content_width + 'px' });
432 self.css({ width: w, height: h, clear: 'both' });
432 self.css({ width: w, height: h, clear: 'both' });
433 if (self.css('display') == 'none') {
433 if (self.css('display') == 'none') {
434 if (this.fadein != false) self.fadeIn(this.fadein);
434 if (this.fadein != false) self.fadeIn(this.fadein);
435 else self.show();
435 else self.show();
436 if (this.loaded) this.loaded();
436 if (this.loaded) this.loaded();
437 }
437 }
438 if (this.resized) this.resized();
438 if (this.resized) this.resized();
439 },
439 },
440 _debug: '', //scroll,draw,calc,diff,markup,change
440 _debug: '', //scroll,draw,calc,diff,markup,change
441 resized: function() { },
441 resized: function() { }
442 finished: function () { }
443 };
442 };
444 var cmsettings = {
443 var cmsettings = {
445 mode: 'text/plain',
444 mode: 'text/plain',
446 readOnly: false,
445 readOnly: false,
447 lineWrapping: this.settings.wrap_lines,
446 lineWrapping: this.settings.wrap_lines,
448 lineNumbers: this.settings.line_numbers,
447 lineNumbers: this.settings.line_numbers,
449 gutters: ['merge', 'CodeMirror-linenumbers']
448 gutters: ['merge', 'CodeMirror-linenumbers']
450 };
449 };
451 this.lhs_cmsettings = {};
450 this.lhs_cmsettings = {};
452 this.rhs_cmsettings = {};
451 this.rhs_cmsettings = {};
453
452
454 // save this element for faster queries
453 // save this element for faster queries
455 this.element = jQuery(el);
454 this.element = jQuery(el);
456
455
457 // save options if there are any
456 // save options if there are any
458 if (options && options.cmsettings) jQuery.extend(this.lhs_cmsettings, cmsettings, options.cmsettings, options.lhs_cmsettings);
457 if (options && options.cmsettings) jQuery.extend(this.lhs_cmsettings, cmsettings, options.cmsettings, options.lhs_cmsettings);
459 if (options && options.cmsettings) jQuery.extend(this.rhs_cmsettings, cmsettings, options.cmsettings, options.rhs_cmsettings);
458 if (options && options.cmsettings) jQuery.extend(this.rhs_cmsettings, cmsettings, options.cmsettings, options.rhs_cmsettings);
460 //if (options) jQuery.extend(this.settings, options);
459 //if (options) jQuery.extend(this.settings, options);
461
460
462 // bind if the element is destroyed
461 // bind if the element is destroyed
463 this.element.bind('destroyed', jQuery.proxy(this.teardown, this));
462 this.element.bind('destroyed', jQuery.proxy(this.teardown, this));
464
463
465 // save this instance in jQuery data, binding this view to the node
464 // save this instance in jQuery data, binding this view to the node
466 jQuery.data(el, 'mergely', this);
465 jQuery.data(el, 'mergely', this);
467
466
468 this._setOptions(options);
467 this._setOptions(options);
469 },
468 },
470 unbind: function() {
469 unbind: function() {
471 if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
470 if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
472 this.editor[this.id + '-lhs'].toTextArea();
471 this.editor[this.id + '-lhs'].toTextArea();
473 this.editor[this.id + '-rhs'].toTextArea();
472 this.editor[this.id + '-rhs'].toTextArea();
474 jQuery(window).off('.mergely');
473 jQuery(window).off('.mergely');
475 },
474 },
476 destroy: function() {
475 destroy: function() {
477 this.element.unbind('destroyed', this.teardown);
476 this.element.unbind('destroyed', this.teardown);
478 this.teardown();
477 this.teardown();
479 },
478 },
480 teardown: function() {
479 teardown: function() {
481 this.unbind();
480 this.unbind();
482 },
481 },
483 lhs: function(text) {
482 lhs: function(text) {
484 this.editor[this.id + '-lhs'].setValue(text);
483 this.editor[this.id + '-lhs'].setValue(text);
485 },
484 },
486 rhs: function(text) {
485 rhs: function(text) {
487 this.editor[this.id + '-rhs'].setValue(text);
486 this.editor[this.id + '-rhs'].setValue(text);
488 },
487 },
489 update: function() {
488 update: function() {
490 this._changing(this.id + '-lhs', this.id + '-rhs');
489 this._changing(this.id + '-lhs', this.id + '-rhs');
491 },
490 },
492 unmarkup: function() {
491 unmarkup: function() {
493 this._clear();
492 this._clear();
494 },
493 },
495 scrollToDiff: function(direction) {
494 scrollToDiff: function(direction) {
496 if (!this.changes.length) return;
495 if (!this.changes.length) return;
497 if (direction == 'next') {
496 if (direction == 'next') {
498 this._current_diff = Math.min(++this._current_diff, this.changes.length - 1);
497 this._current_diff = Math.min(++this._current_diff, this.changes.length - 1);
499 }
498 }
500 else {
499 else if (direction == 'prev') {
501 this._current_diff = Math.max(--this._current_diff, 0);
500 this._current_diff = Math.max(--this._current_diff, 0);
502 }
501 }
503 this._scroll_to_change(this.changes[this._current_diff]);
502 this._scroll_to_change(this.changes[this._current_diff]);
504 this._changed(this.id + '-lhs', this.id + '-rhs');
503 this._changed(this.id + '-lhs', this.id + '-rhs');
505 },
504 },
506 mergeCurrentChange: function(side) {
505 mergeCurrentChange: function(side) {
507 if (!this.changes.length) return;
506 if (!this.changes.length) return;
508 if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
507 if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
509 this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
508 this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
510 }
509 }
511 else if (side == 'rhs' && !this.rhs_cmsettings.readOnly) {
510 else if (side == 'rhs' && !this.rhs_cmsettings.readOnly) {
512 this._merge_change(this.changes[this._current_diff], 'lhs', 'rhs');
511 this._merge_change(this.changes[this._current_diff], 'lhs', 'rhs');
513 }
512 }
514 },
513 },
515 scrollTo: function(side, num) {
514 scrollTo: function(side, num) {
516 var le = this.editor[this.id + '-lhs'];
515 var le = this.editor[this.id + '-lhs'];
517 var re = this.editor[this.id + '-rhs'];
516 var re = this.editor[this.id + '-rhs'];
518 if (side == 'lhs') {
517 if (side == 'lhs') {
519 le.setCursor(num);
518 le.setCursor(num);
520 le.centerOnCursor();
519 le.centerOnCursor();
521 }
520 }
522 else {
521 else {
523 re.setCursor(num);
522 re.setCursor(num);
524 re.centerOnCursor();
523 re.centerOnCursor();
525 }
524 }
526 },
525 },
527 _setOptions: function(opts) {
526 _setOptions: function(opts) {
528 jQuery.extend(this.settings, opts);
527 jQuery.extend(this.settings, opts);
529 if (this.settings.hasOwnProperty('rhs_margin')) {
528 if (this.settings.hasOwnProperty('rhs_margin')) {
530 // dynamically swap the margin
529 // dynamically swap the margin
531 if (this.settings.rhs_margin == 'left') {
530 if (this.settings.rhs_margin == 'left') {
532 this.element.find('.mergely-margin:last-child').insertAfter(
531 this.element.find('.mergely-margin:last-child').insertAfter(
533 this.element.find('.mergely-canvas'));
532 this.element.find('.mergely-canvas'));
534 }
533 }
535 else {
534 else {
536 var target = this.element.find('.mergely-margin').last();
535 var target = this.element.find('.mergely-margin').last();
537 target.appendTo(target.parent());
536 target.appendTo(target.parent());
538 }
537 }
539 }
538 }
540 if (this.settings.hasOwnProperty('sidebar')) {
539 if (this.settings.hasOwnProperty('sidebar')) {
541 // dynamically enable sidebars
540 // dynamically enable sidebars
542 if (this.settings.sidebar) {
541 if (this.settings.sidebar) {
543 jQuery(this.element).find('.mergely-margin').css({display: 'block'});
542 this.element.find('.mergely-margin').css({display: 'block'});
544 }
543 }
545 else {
544 else {
546 jQuery(this.element).find('.mergely-margin').css({display: 'none'});
545 this.element.find('.mergely-margin').css({display: 'none'});
547 }
546 }
548 }
547 }
549 var le, re;
548 var le, re;
550 if (this.settings.hasOwnProperty('wrap_lines')) {
549 if (this.settings.hasOwnProperty('wrap_lines')) {
551 if (this.editor) {
550 if (this.editor) {
552 le = this.editor[this.id + '-lhs'];
551 le = this.editor[this.id + '-lhs'];
553 re = this.editor[this.id + '-rhs'];
552 re = this.editor[this.id + '-rhs'];
554 le.setOption('lineWrapping', this.settings.wrap_lines);
553 le.setOption('lineWrapping', this.settings.wrap_lines);
555 re.setOption('lineWrapping', this.settings.wrap_lines);
554 re.setOption('lineWrapping', this.settings.wrap_lines);
556 }
555 }
557 }
556 }
558 if (this.settings.hasOwnProperty('line_numbers')) {
557 if (this.settings.hasOwnProperty('line_numbers')) {
559 if (this.editor) {
558 if (this.editor) {
560 le = this.editor[this.id + '-lhs'];
559 le = this.editor[this.id + '-lhs'];
561 re = this.editor[this.id + '-rhs'];
560 re = this.editor[this.id + '-rhs'];
562 le.setOption('lineNumbers', this.settings.line_numbers);
561 le.setOption('lineNumbers', this.settings.line_numbers);
563 re.setOption('lineNumbers', this.settings.line_numbers);
562 re.setOption('lineNumbers', this.settings.line_numbers);
564 }
563 }
565 }
564 }
566 },
565 },
567 options: function(opts) {
566 options: function(opts) {
568 if (opts) {
567 if (opts) {
569 this._setOptions(opts);
568 this._setOptions(opts);
570 if (this.settings.autoresize) this.resize();
569 if (this.settings.autoresize) this.resize();
571 if (this.settings.autoupdate) this.update();
570 if (this.settings.autoupdate) this.update();
572 }
571 }
573 else {
572 else {
574 return this.settings;
573 return this.settings;
575 }
574 }
576 },
575 },
577 swap: function() {
576 swap: function() {
578 if (this.lhs_cmsettings.readOnly || this.rhs_cmsettings.readOnly) return;
577 if (this.lhs_cmsettings.readOnly || this.rhs_cmsettings.readOnly) return;
579 var le = this.editor[this.id + '-lhs'];
578 var le = this.editor[this.id + '-lhs'];
580 var re = this.editor[this.id + '-rhs'];
579 var re = this.editor[this.id + '-rhs'];
581 var tmp = re.getValue();
580 var tmp = re.getValue();
582 re.setValue(le.getValue());
581 re.setValue(le.getValue());
583 le.setValue(tmp);
582 le.setValue(tmp);
584 },
583 },
585 merge: function(side) {
584 merge: function(side) {
586 var le = this.editor[this.id + '-lhs'];
585 var le = this.editor[this.id + '-lhs'];
587 var re = this.editor[this.id + '-rhs'];
586 var re = this.editor[this.id + '-rhs'];
588 if (side == 'lhs' && !this.lhs_cmsettings.readOnly) le.setValue(re.getValue());
587 if (side == 'lhs' && !this.lhs_cmsettings.readOnly) le.setValue(re.getValue());
589 else if (!this.rhs_cmsettings.readOnly) re.setValue(le.getValue());
588 else if (!this.rhs_cmsettings.readOnly) re.setValue(le.getValue());
590 },
589 },
591 get: function(side) {
590 get: function(side) {
592 var ed = this.editor[this.id + '-' + side];
591 var ed = this.editor[this.id + '-' + side];
593 var t = ed.getValue();
592 var t = ed.getValue();
594 if (t == undefined) return '';
593 if (t == undefined) return '';
595 return t;
594 return t;
596 },
595 },
597 clear: function(side) {
596 clear: function(side) {
598 if (side == 'lhs' && this.lhs_cmsettings.readOnly) return;
597 if (side == 'lhs' && this.lhs_cmsettings.readOnly) return;
599 if (side == 'rhs' && this.rhs_cmsettings.readOnly) return;
598 if (side == 'rhs' && this.rhs_cmsettings.readOnly) return;
600 var ed = this.editor[this.id + '-' + side];
599 var ed = this.editor[this.id + '-' + side];
601 ed.setValue('');
600 ed.setValue('');
602 },
601 },
603 cm: function(side) {
602 cm: function(side) {
604 return this.editor[this.id + '-' + side];
603 return this.editor[this.id + '-' + side];
605 },
604 },
606 search: function(side, query, direction) {
605 search: function(side, query, direction) {
607 var le = this.editor[this.id + '-lhs'];
606 var le = this.editor[this.id + '-lhs'];
608 var re = this.editor[this.id + '-rhs'];
607 var re = this.editor[this.id + '-rhs'];
609 var editor;
608 var editor;
610 if (side == 'lhs') editor = le;
609 if (side == 'lhs') editor = le;
611 else editor = re;
610 else editor = re;
612 direction = (direction == 'prev') ? 'findPrevious' : 'findNext';
611 direction = (direction == 'prev') ? 'findPrevious' : 'findNext';
613 if ((editor.getSelection().length == 0) || (this.prev_query[side] != query)) {
612 if ((editor.getSelection().length == 0) || (this.prev_query[side] != query)) {
614 this.cursor[this.id] = editor.getSearchCursor(query, { line: 0, ch: 0 }, false);
613 this.cursor[this.id] = editor.getSearchCursor(query, { line: 0, ch: 0 }, false);
615 this.prev_query[side] = query;
614 this.prev_query[side] = query;
616 }
615 }
617 var cursor = this.cursor[this.id];
616 var cursor = this.cursor[this.id];
618
617
619 if (cursor[direction]()) {
618 if (cursor[direction]()) {
620 editor.setSelection(cursor.from(), cursor.to());
619 editor.setSelection(cursor.from(), cursor.to());
621 }
620 }
622 else {
621 else {
623 cursor = editor.getSearchCursor(query, { line: 0, ch: 0 }, false);
622 cursor = editor.getSearchCursor(query, { line: 0, ch: 0 }, false);
624 }
623 }
625 },
624 },
626 resize: function() {
625 resize: function() {
627 this.settings.resize();
626 this.settings.resize();
628 this._changing(this.id + '-lhs', this.id + '-rhs');
627 this._changing(this.id + '-lhs', this.id + '-rhs');
629 this._set_top_offset(this.id + '-lhs');
628 this._set_top_offset(this.id + '-lhs');
630 },
629 },
631 diff: function() {
630 diff: function() {
632 var lhs = this.editor[this.id + '-lhs'].getValue();
631 var lhs = this.editor[this.id + '-lhs'].getValue();
633 var rhs = this.editor[this.id + '-rhs'].getValue();
632 var rhs = this.editor[this.id + '-rhs'].getValue();
634 var d = new Mgly.diff(lhs, rhs, this.settings);
633 var d = new Mgly.diff(lhs, rhs, this.settings);
635 return d.normal_form();
634 return d.normal_form();
636 },
635 },
637 bind: function(el) {
636 bind: function(el) {
638 this.element.hide();//hide
637 this.element.hide();//hide
639 this.id = jQuery(el).attr('id');
638 this.id = jQuery(el).attr('id');
640 this.changed_timeout = null;
639 this.changed_timeout = null;
641 this.chfns = {};
640 this.chfns = {};
642 this.chfns[this.id + '-lhs'] = [];
641 this.chfns[this.id + '-lhs'] = [];
643 this.chfns[this.id + '-rhs'] = [];
642 this.chfns[this.id + '-rhs'] = [];
644 this.prev_query = [];
643 this.prev_query = [];
645 this.cursor = [];
644 this.cursor = [];
646 this._skipscroll = {};
645 this._skipscroll = {};
647 this.change_exp = new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
646 this.change_exp = new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
648 var merge_lhs_button;
647 var merge_lhs_button;
649 var merge_rhs_button;
648 var merge_rhs_button;
650 if (jQuery.button != undefined) {
649 if (jQuery.button != undefined) {
651 //jquery ui
650 //jquery ui
652 merge_lhs_button = '<button title="Merge left"></button>';
651 merge_lhs_button = '<button title="Merge left"></button>';
653 merge_rhs_button = '<button title="Merge right"></button>';
652 merge_rhs_button = '<button title="Merge right"></button>';
654 }
653 }
655 else {
654 else {
656 // homebrew
655 // homebrew
657 var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid: #222;margin-right:5px;margin-top: -2px;';
656 var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid: #222;margin-right:5px;margin-top: -2px;';
658 merge_lhs_button = '<div style="' + style + '" title="Merge left">&lt;</div>';
657 merge_lhs_button = '<div style="' + style + '" title="Merge left">&lt;</div>';
659 merge_rhs_button = '<div style="' + style + '" title="Merge right">&gt;</div>';
658 merge_rhs_button = '<div style="' + style + '" title="Merge right">&gt;</div>';
660 }
659 }
661 this.merge_rhs_button = jQuery(merge_rhs_button);
660 this.merge_rhs_button = jQuery(merge_rhs_button);
662 this.merge_lhs_button = jQuery(merge_lhs_button);
661 this.merge_lhs_button = jQuery(merge_lhs_button);
663
662
664 // create the textarea and canvas elements
663 // create the textarea and canvas elements
665 var height = this.settings.editor_height;
664 var height = this.settings.editor_height;
666 var width = this.settings.editor_width;
665 var width = this.settings.editor_width;
667 this.element.append(jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-margin" width="8px" height="' + height + '"></canvas></div>'));
666 this.element.append(jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-margin" width="8px" height="' + height + '"></canvas></div>'));
668 this.element.append(jQuery('<div style="position:relative;width:' + width + '; height:' + height + '" id="' + this.id + '-editor-lhs" class="mergely-column"><textarea style="" id="' + this.id + '-lhs"></textarea></div>'));
667 this.element.append(jQuery('<div style="position:relative;width:' + width + '; height:' + height + '" id="' + this.id + '-editor-lhs" class="mergely-column"><textarea style="" id="' + this.id + '-lhs"></textarea></div>'));
669 this.element.append(jQuery('<div class="mergely-canvas" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-' + this.id + '-rhs-canvas" style="width:28px" width="28px" height="' + height + '"></canvas></div>'));
668 this.element.append(jQuery('<div class="mergely-canvas" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-' + this.id + '-rhs-canvas" style="width:28px" width="28px" height="' + height + '"></canvas></div>'));
670 var rmargin = jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-rhs-margin" width="8px" height="' + height + '"></canvas></div>');
669 var rmargin = jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-rhs-margin" width="8px" height="' + height + '"></canvas></div>');
671 if (!this.settings.sidebar) {
670 if (!this.settings.sidebar) {
672 this.element.find('.mergely-margin').css({display: 'none'});
671 this.element.find('.mergely-margin').css({display: 'none'});
673 }
672 }
674 if (this.settings.rhs_margin == 'left') {
673 if (this.settings.rhs_margin == 'left') {
675 this.element.append(rmargin);
674 this.element.append(rmargin);
676 }
675 }
677 this.element.append(jQuery('<div style="width:' + width + '; height:' + height + '" id="' + this.id + '-editor-rhs" class="mergely-column"><textarea style="" id="' + this.id + '-rhs"></textarea></div>'));
676 this.element.append(jQuery('<div style="width:' + width + '; height:' + height + '" id="' + this.id + '-editor-rhs" class="mergely-column"><textarea style="" id="' + this.id + '-rhs"></textarea></div>'));
678 if (this.settings.rhs_margin != 'left') {
677 if (this.settings.rhs_margin != 'left') {
679 this.element.append(rmargin);
678 this.element.append(rmargin);
680 }
679 }
681 //codemirror
680 //codemirror
682 var cmstyle = '#' + this.id + ' .CodeMirror-gutter-text { padding: 5px 0 0 0; }' +
681 var cmstyle = '#' + this.id + ' .CodeMirror-gutter-text { padding: 5px 0 0 0; }' +
683 '#' + this.id + ' .CodeMirror-lines pre, ' + '#' + this.id + ' .CodeMirror-gutter-text pre { line-height: 18px; }' +
682 '#' + this.id + ' .CodeMirror-lines pre, ' + '#' + this.id + ' .CodeMirror-gutter-text pre { line-height: 18px; }' +
684 '.CodeMirror-linewidget { overflow: hidden; };';
683 '.CodeMirror-linewidget { overflow: hidden; };';
685 if (this.settings.autoresize) {
684 if (this.settings.autoresize) {
686 cmstyle += this.id + ' .CodeMirror-scroll { height: 100%; overflow: auto; }';
685 cmstyle += this.id + ' .CodeMirror-scroll { height: 100%; overflow: auto; }';
687 }
686 }
688 // adjust the margin line height
687 // adjust the margin line height
689 cmstyle += '\n.CodeMirror { line-height: 18px; }';
688 cmstyle += '\n.CodeMirror { line-height: 18px; }';
690 jQuery('<style type="text/css">' + cmstyle + '</style>').appendTo('head');
689 jQuery('<style type="text/css">' + cmstyle + '</style>').appendTo('head');
691
690
692 //bind
691 //bind
693 var rhstx = jQuery('#' + this.id + '-rhs').get(0);
692 var rhstx = this.element.find('#' + this.id + '-rhs').get(0);
694 if (!rhstx) {
693 if (!rhstx) {
695 console.error('rhs textarea not defined - Mergely not initialized properly');
694 console.error('rhs textarea not defined - Mergely not initialized properly');
696 return;
695 return;
697 }
696 }
698 var lhstx = jQuery('#' + this.id + '-lhs').get(0);
697 var lhstx = this.element.find('#' + this.id + '-lhs').get(0);
699 if (!rhstx) {
698 if (!rhstx) {
700 console.error('lhs textarea not defined - Mergely not initialized properly');
699 console.error('lhs textarea not defined - Mergely not initialized properly');
701 return;
700 return;
702 }
701 }
703 var self = this;
702 var self = this;
704 this.editor = [];
703 this.editor = [];
705 this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);
704 this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);
706 this.editor[this.id + '-rhs'] = CodeMirror.fromTextArea(rhstx, this.rhs_cmsettings);
705 this.editor[this.id + '-rhs'] = CodeMirror.fromTextArea(rhstx, this.rhs_cmsettings);
707 this.editor[this.id + '-lhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
706 this.editor[this.id + '-lhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
708 this.editor[this.id + '-lhs'].on('scroll', function(){ self._scrolling(self.id + '-lhs'); });
707 this.editor[this.id + '-lhs'].on('scroll', function(){ self._scrolling(self.id + '-lhs'); });
709 this.editor[this.id + '-rhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
708 this.editor[this.id + '-rhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
710 this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); });
709 this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); });
711 // resize
710 // resize
712 if (this.settings.autoresize) {
711 if (this.settings.autoresize) {
713 var sz_timeout1 = null;
712 var sz_timeout1 = null;
714 var sz = function(init) {
713 var sz = function(init) {
715 //self.em_height = null; //recalculate
714 //self.em_height = null; //recalculate
716 if (self.settings.resize) self.settings.resize(init);
715 if (self.settings.resize) self.settings.resize(init);
717 self.editor[self.id + '-lhs'].refresh();
716 self.editor[self.id + '-lhs'].refresh();
718 self.editor[self.id + '-rhs'].refresh();
717 self.editor[self.id + '-rhs'].refresh();
719 if (self.settings.autoupdate) {
718 if (self.settings.autoupdate) {
720 self._changing(self.id + '-lhs', self.id + '-rhs');
719 self._changing(self.id + '-lhs', self.id + '-rhs');
721 }
720 }
722 };
721 };
723 jQuery(window).on('resize.mergely',
722 jQuery(window).on('resize.mergely',
724 function () {
723 function () {
725 if (sz_timeout1) clearTimeout(sz_timeout1);
724 if (sz_timeout1) clearTimeout(sz_timeout1);
726 sz_timeout1 = setTimeout(sz, self.settings.resize_timeout);
725 sz_timeout1 = setTimeout(sz, self.settings.resize_timeout);
727 }
726 }
728 );
727 );
729 sz(true);
728 sz(true);
730 }
729 }
730
731 // scrollToDiff() from gutter
732 function gutterClicked(side, line, ev) {
733 // The "Merge left/right" buttons are also located in the gutter.
734 // Don't interfere with them:
735 if (ev.target && (jQuery(ev.target).closest('.merge-button').length > 0)) {
736 return;
737 }
738
739 // See if the user clicked the line number of a difference:
740 var i, change;
741 for (i = 0; i < this.changes.length; i++) {
742 change = this.changes[i];
743 if (line >= change[side+'-line-from'] && line <= change[side+'-line-to']) {
744 this._current_diff = i;
745 // I really don't like this here - something about gutterClick does not
746 // like mutating editor here. Need to trigger the scroll to diff from
747 // a timeout.
748 setTimeout(function() { this.scrollToDiff(); }.bind(this), 10);
749 break;
750 }
751 }
752 }
753
754 this.editor[this.id + '-lhs'].on('gutterClick', function(cm, n, gutterClass, ev) {
755 gutterClicked.call(this, 'lhs', n, ev);
756 }.bind(this));
757
758 this.editor[this.id + '-rhs'].on('gutterClick', function(cm, n, gutterClass, ev) {
759 gutterClicked.call(this, 'rhs', n, ev);
760 }.bind(this));
761
731 //bind
762 //bind
732 var setv;
763 var setv;
733 if (this.settings.lhs) {
764 if (this.settings.lhs) {
734 setv = this.editor[this.id + '-lhs'].getDoc().setValue;
765 setv = this.editor[this.id + '-lhs'].getDoc().setValue;
735 this.settings.lhs(setv.bind(this.editor[this.id + '-lhs'].getDoc()));
766 this.settings.lhs(setv.bind(this.editor[this.id + '-lhs'].getDoc()));
736 }
767 }
737 if (this.settings.rhs) {
768 if (this.settings.rhs) {
738 setv = this.editor[this.id + '-rhs'].getDoc().setValue;
769 setv = this.editor[this.id + '-rhs'].getDoc().setValue;
739 this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc()));
770 this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc()));
740 }
771 }
741 },
772 },
742
773
743 _scroll_to_change : function(change) {
774 _scroll_to_change : function(change) {
744 if (!change) return;
775 if (!change) return;
745 var self = this;
776 var self = this;
746 var led = self.editor[self.id+'-lhs'];
777 var led = self.editor[self.id+'-lhs'];
747 var red = self.editor[self.id+'-rhs'];
778 var red = self.editor[self.id+'-rhs'];
748
749 var yref = led.getScrollerElement().offsetHeight * 0.5; // center between >0 and 1/2
750
751 // set cursors
779 // set cursors
752 led.setCursor(Math.max(change["lhs-line-from"],0), 0); // use led.getCursor().ch ?
780 led.setCursor(Math.max(change["lhs-line-from"],0), 0); // use led.getCursor().ch ?
753 red.setCursor(Math.max(change["rhs-line-from"],0), 0);
781 red.setCursor(Math.max(change["rhs-line-from"],0), 0);
754
782 led.scrollIntoView({line: change["lhs-line-to"]});
755 // using directly CodeMirror breaks canvas alignment
756 // var ly = led.charCoords({line: Math.max(change["lhs-line-from"],0), ch: 0}, "local").top;
757
758 // calculate scroll offset for current change. Warning: returns relative y position so we scroll to 0 first.
759 led.scrollTo(null, 0);
760 red.scrollTo(null, 0);
761 self._calculate_offsets(self.id+'-lhs', self.id+'-rhs', [change]);
762 led.scrollTo(null, Math.max(change["lhs-y-start"]-yref, 0));
763 red.scrollTo(null, Math.max(change["rhs-y-start"]-yref, 0));
764 // right pane should simply follows
765 },
783 },
766
784
767 _scrolling: function(editor_name) {
785 _scrolling: function(editor_name) {
768 if (this._skipscroll[editor_name] === true) {
786 if (this._skipscroll[editor_name] === true) {
769 // scrolling one side causes the other to event - ignore it
787 // scrolling one side causes the other to event - ignore it
770 this._skipscroll[editor_name] = false;
788 this._skipscroll[editor_name] = false;
771 return;
789 return;
772 }
790 }
773 var scroller = jQuery(this.editor[editor_name].getScrollerElement());
791 var scroller = jQuery(this.editor[editor_name].getScrollerElement());
774 if (this.midway == undefined) {
792 if (this.midway == undefined) {
775 this.midway = (scroller.height() / 2.0 + scroller.offset().top).toFixed(2);
793 this.midway = (scroller.height() / 2.0 + scroller.offset().top).toFixed(2);
776 }
794 }
777 // balance-line
795 // balance-line
778 var midline = this.editor[editor_name].coordsChar({left:0, top:this.midway});
796 var midline = this.editor[editor_name].coordsChar({left:0, top:this.midway});
779 var top_to = scroller.scrollTop();
797 var top_to = scroller.scrollTop();
780 var left_to = scroller.scrollLeft();
798 var left_to = scroller.scrollLeft();
781
799
782 this.trace('scroll', 'side', editor_name);
800 this.trace('scroll', 'side', editor_name);
783 this.trace('scroll', 'midway', this.midway);
801 this.trace('scroll', 'midway', this.midway);
784 this.trace('scroll', 'midline', midline);
802 this.trace('scroll', 'midline', midline);
785 this.trace('scroll', 'top_to', top_to);
803 this.trace('scroll', 'top_to', top_to);
786 this.trace('scroll', 'left_to', left_to);
804 this.trace('scroll', 'left_to', left_to);
787
805
788 var editor_name1 = this.id + '-lhs';
806 var editor_name1 = this.id + '-lhs';
789 var editor_name2 = this.id + '-rhs';
807 var editor_name2 = this.id + '-rhs';
790
808
791 for (var name in this.editor) {
809 for (var name in this.editor) {
792 if (!this.editor.hasOwnProperty(name)) continue;
810 if (!this.editor.hasOwnProperty(name)) continue;
793 if (editor_name == name) continue; //same editor
811 if (editor_name == name) continue; //same editor
794 var this_side = editor_name.replace(this.id + '-', '');
812 var this_side = editor_name.replace(this.id + '-', '');
795 var other_side = name.replace(this.id + '-', '');
813 var other_side = name.replace(this.id + '-', '');
796 var top_adjust = 0;
814 var top_adjust = 0;
797
815
798 // find the last change that is less than or within the midway point
816 // find the last change that is less than or within the midway point
799 // do not move the rhs until the lhs end point is >= the rhs end point.
817 // do not move the rhs until the lhs end point is >= the rhs end point.
800 var last_change = null;
818 var last_change = null;
801 var force_scroll = false;
819 var force_scroll = false;
802 for (var i = 0; i < this.changes.length; ++i) {
820 for (var i = 0; i < this.changes.length; ++i) {
803 var change = this.changes[i];
821 var change = this.changes[i];
804 if ((midline.line >= change[this_side+'-line-from'])) {
822 if ((midline.line >= change[this_side+'-line-from'])) {
805 last_change = change;
823 last_change = change;
806 if (midline.line >= last_change[this_side+'-line-to']) {
824 if (midline.line >= last_change[this_side+'-line-to']) {
807 if (!change.hasOwnProperty(this_side+'-y-start') ||
825 if (!change.hasOwnProperty(this_side+'-y-start') ||
808 !change.hasOwnProperty(this_side+'-y-end') ||
826 !change.hasOwnProperty(this_side+'-y-end') ||
809 !change.hasOwnProperty(other_side+'-y-start') ||
827 !change.hasOwnProperty(other_side+'-y-start') ||
810 !change.hasOwnProperty(other_side+'-y-end')){
828 !change.hasOwnProperty(other_side+'-y-end')){
811 // change outside of viewport
829 // change outside of viewport
812 force_scroll = true;
830 force_scroll = true;
813 }
831 }
814 else {
832 else {
815 top_adjust +=
833 top_adjust +=
816 (change[this_side+'-y-end'] - change[this_side+'-y-start']) -
834 (change[this_side+'-y-end'] - change[this_side+'-y-start']) -
817 (change[other_side+'-y-end'] - change[other_side+'-y-start']);
835 (change[other_side+'-y-end'] - change[other_side+'-y-start']);
818 }
836 }
819 }
837 }
820 }
838 }
821 }
839 }
822
840
823 var vp = this.editor[name].getViewport();
841 var vp = this.editor[name].getViewport();
824 var scroll = true;
842 var scroll = true;
825 if (last_change) {
843 if (last_change) {
826 this.trace('scroll', 'last change before midline', last_change);
844 this.trace('scroll', 'last change before midline', last_change);
827 if (midline.line >= vp.from && midline <= vp.to) {
845 if (midline.line >= vp.from && midline <= vp.to) {
828 scroll = false;
846 scroll = false;
829 }
847 }
830 }
848 }
831 this.trace('scroll', 'scroll', scroll);
849 this.trace('scroll', 'scroll', scroll);
832 if (scroll || force_scroll) {
850 if (scroll || force_scroll) {
833 // scroll the other side
851 // scroll the other side
834 this.trace('scroll', 'scrolling other side', top_to - top_adjust);
852 this.trace('scroll', 'scrolling other side', top_to - top_adjust);
835 this._skipscroll[name] = true;//disable next event
853 this._skipscroll[name] = true;//disable next event
836 this.editor[name].scrollTo(left_to, top_to - top_adjust);
854 this.editor[name].scrollTo(left_to, top_to - top_adjust);
837 }
855 }
838 else this.trace('scroll', 'not scrolling other side');
856 else this.trace('scroll', 'not scrolling other side');
839
857
840 if (this.settings.autoupdate) {
858 if (this.settings.autoupdate) {
841 var timer = new Mgly.Timer();
859 var timer = new Mgly.Timer();
842 this._calculate_offsets(editor_name1, editor_name2, this.changes);
860 this._calculate_offsets(editor_name1, editor_name2, this.changes);
843 this.trace('change', 'offsets time', timer.stop());
861 this.trace('change', 'offsets time', timer.stop());
844 this._markup_changes(editor_name1, editor_name2, this.changes);
862 this._markup_changes(editor_name1, editor_name2, this.changes);
845 this.trace('change', 'markup time', timer.stop());
863 this.trace('change', 'markup time', timer.stop());
846 this._draw_diff(editor_name1, editor_name2, this.changes);
864 this._draw_diff(editor_name1, editor_name2, this.changes);
847 this.trace('change', 'draw time', timer.stop());
865 this.trace('change', 'draw time', timer.stop());
848 }
866 }
849 this.trace('scroll', 'scrolled');
867 this.trace('scroll', 'scrolled');
850 }
868 }
851 },
869 },
852 _changing: function(editor_name1, editor_name2) {
870 _changing: function(editor_name1, editor_name2) {
853 this.trace('change', 'changing-timeout', this.changed_timeout);
871 this.trace('change', 'changing-timeout', this.changed_timeout);
854 var self = this;
872 var self = this;
855 if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
873 if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
856 this.changed_timeout = setTimeout(function(){
874 this.changed_timeout = setTimeout(function(){
857 var timer = new Mgly.Timer();
875 var timer = new Mgly.Timer();
858 self._changed(editor_name1, editor_name2);
876 self._changed(editor_name1, editor_name2);
859 self.trace('change', 'total time', timer.stop());
877 self.trace('change', 'total time', timer.stop());
860 }, this.settings.change_timeout);
878 }, this.settings.change_timeout);
861 },
879 },
862 _changed: function(editor_name1, editor_name2) {
880 _changed: function(editor_name1, editor_name2) {
863 this._clear();
881 this._clear();
864 this._diff(editor_name1, editor_name2);
882 this._diff(editor_name1, editor_name2);
865 },
883 },
866 _clear: function() {
884 _clear: function() {
867 var self = this, name, editor, fns, timer, i, change, l;
885 var self = this, name, editor, fns, timer, i, change, l;
868
886
869 var clear_changes = function() {
887 var clear_changes = function() {
870 timer = new Mgly.Timer();
888 timer = new Mgly.Timer();
871 for (i = 0, l = editor.lineCount(); i < l; ++i) {
889 for (i = 0, l = editor.lineCount(); i < l; ++i) {
872 editor.removeLineClass(i, 'background');
890 editor.removeLineClass(i, 'background');
873 }
891 }
874 for (i = 0; i < fns.length; ++i) {
892 for (i = 0; i < fns.length; ++i) {
875 //var edid = editor.getDoc().id;
893 //var edid = editor.getDoc().id;
876 change = fns[i];
894 change = fns[i];
877 //if (change.doc.id != edid) continue;
895 //if (change.doc.id != edid) continue;
878 if (change.lines.length) {
896 if (change.lines.length) {
879 self.trace('change', 'clear text', change.lines[0].text);
897 self.trace('change', 'clear text', change.lines[0].text);
880 }
898 }
881 change.clear();
899 change.clear();
882 }
900 }
883 editor.clearGutter('merge');
901 editor.clearGutter('merge');
884 self.trace('change', 'clear time', timer.stop());
902 self.trace('change', 'clear time', timer.stop());
885 };
903 };
886
904
887 for (name in this.editor) {
905 for (name in this.editor) {
888 if (!this.editor.hasOwnProperty(name)) continue;
906 if (!this.editor.hasOwnProperty(name)) continue;
889 editor = this.editor[name];
907 editor = this.editor[name];
890 fns = self.chfns[name];
908 fns = self.chfns[name];
891 // clear editor changes
909 // clear editor changes
892 editor.operation(clear_changes);
910 editor.operation(clear_changes);
893 }
911 }
894 self.chfns[name] = [];
912 self.chfns[name] = [];
895
913
896 var ex = this._draw_info(this.id + '-lhs', this.id + '-rhs');
914 var ex = this._draw_info(this.id + '-lhs', this.id + '-rhs');
897 var ctx_lhs = ex.clhs.get(0).getContext('2d');
915 var ctx_lhs = ex.clhs.get(0).getContext('2d');
898 var ctx_rhs = ex.crhs.get(0).getContext('2d');
916 var ctx_rhs = ex.crhs.get(0).getContext('2d');
899 var ctx = ex.dcanvas.getContext('2d');
917 var ctx = ex.dcanvas.getContext('2d');
900
918
901 ctx_lhs.beginPath();
919 ctx_lhs.beginPath();
902 ctx_lhs.fillStyle = this.settings.bgcolor;
920 ctx_lhs.fillStyle = this.settings.bgcolor;
903 ctx_lhs.strokeStyle = '#888';
921 ctx_lhs.strokeStyle = '#888';
904 ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height);
922 ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height);
905 ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
923 ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
906
924
907 ctx_rhs.beginPath();
925 ctx_rhs.beginPath();
908 ctx_rhs.fillStyle = this.settings.bgcolor;
926 ctx_rhs.fillStyle = this.settings.bgcolor;
909 ctx_rhs.strokeStyle = '#888';
927 ctx_rhs.strokeStyle = '#888';
910 ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height);
928 ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height);
911 ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
929 ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
912
930
913 ctx.beginPath();
931 ctx.beginPath();
914 ctx.fillStyle = '#fff';
932 ctx.fillStyle = '#fff';
915 ctx.fillRect(0, 0, this.draw_mid_width, ex.visible_page_height);
933 ctx.fillRect(0, 0, this.draw_mid_width, ex.visible_page_height);
916 },
934 },
917 _diff: function(editor_name1, editor_name2) {
935 _diff: function(editor_name1, editor_name2) {
918 var lhs = this.editor[editor_name1].getValue();
936 var lhs = this.editor[editor_name1].getValue();
919 var rhs = this.editor[editor_name2].getValue();
937 var rhs = this.editor[editor_name2].getValue();
920 var timer = new Mgly.Timer();
938 var timer = new Mgly.Timer();
921 var d = new Mgly.diff(lhs, rhs, this.settings);
939 var d = new Mgly.diff(lhs, rhs, this.settings);
922 this.trace('change', 'diff time', timer.stop());
940 this.trace('change', 'diff time', timer.stop());
923 this.changes = Mgly.DiffParser(d.normal_form());
941 this.changes = Mgly.DiffParser(d.normal_form());
924 this.trace('change', 'parse time', timer.stop());
942 this.trace('change', 'parse time', timer.stop());
925
926 if (this._current_diff === undefined && this.changes.length) {
943 if (this._current_diff === undefined && this.changes.length) {
927 // go to first difference on start-up
944 // go to first difference on start-up
928 this._current_diff = 0;
945 this._current_diff = 0;
929 this._scroll_to_change(this.changes[0]);
946 this._scroll_to_change(this.changes[0]);
930 }
947 }
931
932 this.trace('change', 'scroll_to_change time', timer.stop());
948 this.trace('change', 'scroll_to_change time', timer.stop());
933 this._calculate_offsets(editor_name1, editor_name2, this.changes);
949 this._calculate_offsets(editor_name1, editor_name2, this.changes);
934 this.trace('change', 'offsets time', timer.stop());
950 this.trace('change', 'offsets time', timer.stop());
935 this._markup_changes(editor_name1, editor_name2, this.changes);
951 this._markup_changes(editor_name1, editor_name2, this.changes);
936 this.trace('change', 'markup time', timer.stop());
952 this.trace('change', 'markup time', timer.stop());
937 this._draw_diff(editor_name1, editor_name2, this.changes);
953 this._draw_diff(editor_name1, editor_name2, this.changes);
938 this.trace('change', 'draw time', timer.stop());
954 this.trace('change', 'draw time', timer.stop());
939 },
955 },
940 _parse_diff: function (editor_name1, editor_name2, diff) {
956 _parse_diff: function (editor_name1, editor_name2, diff) {
941 this.trace('diff', 'diff results:\n', diff);
957 this.trace('diff', 'diff results:\n', diff);
942 var changes = [];
958 var changes = [];
943 var change_id = 0;
959 var change_id = 0;
944 // parse diff
960 // parse diff
945 var diff_lines = diff.split(/\n/);
961 var diff_lines = diff.split(/\n/);
946 for (var i = 0; i < diff_lines.length; ++i) {
962 for (var i = 0; i < diff_lines.length; ++i) {
947 if (diff_lines[i].length == 0) continue;
963 if (diff_lines[i].length == 0) continue;
948 var change = {};
964 var change = {};
949 var test = this.change_exp.exec(diff_lines[i]);
965 var test = this.change_exp.exec(diff_lines[i]);
950 if (test == null) continue;
966 if (test == null) continue;
951 // lines are zero-based
967 // lines are zero-based
952 var fr = test[1].split(',');
968 var fr = test[1].split(',');
953 change['lhs-line-from'] = fr[0] - 1;
969 change['lhs-line-from'] = fr[0] - 1;
954 if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1;
970 if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1;
955 else change['lhs-line-to'] = fr[1] - 1;
971 else change['lhs-line-to'] = fr[1] - 1;
956 var to = test[3].split(',');
972 var to = test[3].split(',');
957 change['rhs-line-from'] = to[0] - 1;
973 change['rhs-line-from'] = to[0] - 1;
958 if (to.length == 1) change['rhs-line-to'] = to[0] - 1;
974 if (to.length == 1) change['rhs-line-to'] = to[0] - 1;
959 else change['rhs-line-to'] = to[1] - 1;
975 else change['rhs-line-to'] = to[1] - 1;
960 // TODO: optimize for changes that are adds/removes
976 // TODO: optimize for changes that are adds/removes
961 if (change['lhs-line-from'] < 0) change['lhs-line-from'] = 0;
977 if (change['lhs-line-from'] < 0) change['lhs-line-from'] = 0;
962 if (change['lhs-line-to'] < 0) change['lhs-line-to'] = 0;
978 if (change['lhs-line-to'] < 0) change['lhs-line-to'] = 0;
963 if (change['rhs-line-from'] < 0) change['rhs-line-from'] = 0;
979 if (change['rhs-line-from'] < 0) change['rhs-line-from'] = 0;
964 if (change['rhs-line-to'] < 0) change['rhs-line-to'] = 0;
980 if (change['rhs-line-to'] < 0) change['rhs-line-to'] = 0;
965 change['op'] = test[2];
981 change['op'] = test[2];
966 changes[change_id++] = change;
982 changes[change_id++] = change;
967 this.trace('diff', 'change', change);
983 this.trace('diff', 'change', change);
968 }
984 }
969 return changes;
985 return changes;
970 },
986 },
971 _get_viewport: function(editor_name1, editor_name2) {
987 _get_viewport: function(editor_name1, editor_name2) {
972 var lhsvp = this.editor[editor_name1].getViewport();
988 var lhsvp = this.editor[editor_name1].getViewport();
973 var rhsvp = this.editor[editor_name2].getViewport();
989 var rhsvp = this.editor[editor_name2].getViewport();
974 return {from: Math.min(lhsvp.from, rhsvp.from), to: Math.max(lhsvp.to, rhsvp.to)};
990 return {from: Math.min(lhsvp.from, rhsvp.from), to: Math.max(lhsvp.to, rhsvp.to)};
975 },
991 },
976 _is_change_in_view: function(vp, change) {
992 _is_change_in_view: function(vp, change) {
977 if (!this.settings.viewport) return true;
993 if (!this.settings.viewport) return true;
978 if ((change['lhs-line-from'] < vp.from && change['lhs-line-to'] < vp.to) ||
994 if ((change['lhs-line-from'] < vp.from && change['lhs-line-to'] < vp.to) ||
979 (change['lhs-line-from'] > vp.from && change['lhs-line-to'] > vp.to) ||
995 (change['lhs-line-from'] > vp.from && change['lhs-line-to'] > vp.to) ||
980 (change['rhs-line-from'] < vp.from && change['rhs-line-to'] < vp.to) ||
996 (change['rhs-line-from'] < vp.from && change['rhs-line-to'] < vp.to) ||
981 (change['rhs-line-from'] > vp.from && change['rhs-line-to'] > vp.to)) {
997 (change['rhs-line-from'] > vp.from && change['rhs-line-to'] > vp.to)) {
982 // if the change is outside the viewport, skip
998 // if the change is outside the viewport, skip
983 return false;
999 return false;
984 }
1000 }
985 return true;
1001 return true;
986 },
1002 },
987 _set_top_offset: function (editor_name1) {
1003 _set_top_offset: function (editor_name1) {
988 // save the current scroll position of the editor
1004 // save the current scroll position of the editor
989 var saveY = this.editor[editor_name1].getScrollInfo().top;
1005 var saveY = this.editor[editor_name1].getScrollInfo().top;
990 // temporarily scroll to top
1006 // temporarily scroll to top
991 this.editor[editor_name1].scrollTo(null, 0);
1007 this.editor[editor_name1].scrollTo(null, 0);
992
1008
993 // this is the distance from the top of the screen to the top of the
1009 // this is the distance from the top of the screen to the top of the
994 // content of the first codemirror editor
1010 // content of the first codemirror editor
995 var topnode = jQuery('#' + this.id + ' .CodeMirror-measure').first();
1011 var topnode = this.element.find('.CodeMirror-measure').first();
996 var top_offset = topnode.offset().top - 4;
1012 var top_offset = topnode.offset().top - 4;
997 if(!top_offset) return false;
1013 if(!top_offset) return false;
998
1014
999 // restore editor's scroll position
1015 // restore editor's scroll position
1000 this.editor[editor_name1].scrollTo(null, saveY);
1016 this.editor[editor_name1].scrollTo(null, saveY);
1001
1017
1002 this.draw_top_offset = 0.5 - top_offset;
1018 this.draw_top_offset = 0.5 - top_offset;
1003 return true;
1019 return true;
1004 },
1020 },
1005 _calculate_offsets: function (editor_name1, editor_name2, changes) {
1021 _calculate_offsets: function (editor_name1, editor_name2, changes) {
1006 if (this.em_height == null) {
1022 if (this.em_height == null) {
1007 if(!this._set_top_offset(editor_name1)) return; //try again
1023 if(!this._set_top_offset(editor_name1)) return; //try again
1008 this.em_height = this.editor[editor_name1].defaultTextHeight();
1024 this.em_height = this.editor[editor_name1].defaultTextHeight();
1009 if (!this.em_height) {
1025 if (!this.em_height) {
1010 console.warn('Failed to calculate offsets, using 18 by default');
1026 console.warn('Failed to calculate offsets, using 18 by default');
1011 this.em_height = 18;
1027 this.em_height = 18;
1012 }
1028 }
1013 this.draw_lhs_min = 0.5;
1029 this.draw_lhs_min = 0.5;
1014 var c = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas');
1030 var c = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas');
1015 if (!c.length) {
1031 if (!c.length) {
1016 console.error('failed to find canvas', '#' + editor_name1 + '-' + editor_name2 + '-canvas');
1032 console.error('failed to find canvas', '#' + editor_name1 + '-' + editor_name2 + '-canvas');
1017 }
1033 }
1018 if (!c.width()) {
1034 if (!c.width()) {
1019 console.error('canvas width is 0');
1035 console.error('canvas width is 0');
1020 return;
1036 return;
1021 }
1037 }
1022 this.draw_mid_width = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas').width();
1038 this.draw_mid_width = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas').width();
1023 this.draw_rhs_max = this.draw_mid_width - 0.5; //24.5;
1039 this.draw_rhs_max = this.draw_mid_width - 0.5; //24.5;
1024 this.draw_lhs_width = 5;
1040 this.draw_lhs_width = 5;
1025 this.draw_rhs_width = 5;
1041 this.draw_rhs_width = 5;
1026 this.trace('calc', 'change offsets calculated', {top_offset: this.draw_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});
1042 this.trace('calc', 'change offsets calculated', {top_offset: this.draw_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});
1027 }
1043 }
1028 var lhschc = this.editor[editor_name1].charCoords({line: 0});
1044 var lhschc = this.editor[editor_name1].charCoords({line: 0});
1029 var rhschc = this.editor[editor_name2].charCoords({line: 0});
1045 var rhschc = this.editor[editor_name2].charCoords({line: 0});
1030 var vp = this._get_viewport(editor_name1, editor_name2);
1046 var vp = this._get_viewport(editor_name1, editor_name2);
1031
1047
1032 for (var i = 0; i < changes.length; ++i) {
1048 for (var i = 0; i < changes.length; ++i) {
1033 var change = changes[i];
1049 var change = changes[i];
1034
1050
1035 if (!this.settings.sidebar && !this._is_change_in_view(vp, change)) {
1051 if (!this.settings.sidebar && !this._is_change_in_view(vp, change)) {
1036 // if the change is outside the viewport, skip
1052 // if the change is outside the viewport, skip
1037 delete change['lhs-y-start'];
1053 delete change['lhs-y-start'];
1038 delete change['lhs-y-end'];
1054 delete change['lhs-y-end'];
1039 delete change['rhs-y-start'];
1055 delete change['rhs-y-start'];
1040 delete change['rhs-y-end'];
1056 delete change['rhs-y-end'];
1041 continue;
1057 continue;
1042 }
1058 }
1043 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1059 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1044 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1060 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1045 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1061 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1046 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1062 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1047
1063
1048 var ls, le, rs, re, tls, tle, lhseh, lhssh, rhssh, rhseh;
1064 var ls, le, rs, re, tls, tle, lhseh, lhssh, rhssh, rhseh;
1049 if (this.editor[editor_name1].getOption('lineWrapping') || this.editor[editor_name2].getOption('lineWrapping')) {
1065 if (this.editor[editor_name1].getOption('lineWrapping') || this.editor[editor_name2].getOption('lineWrapping')) {
1050 // If using line-wrapping, we must get the height of the line
1066 // If using line-wrapping, we must get the height of the line
1051 tls = this.editor[editor_name1].cursorCoords({line: llf, ch: 0}, 'page');
1067 tls = this.editor[editor_name1].cursorCoords({line: llf, ch: 0}, 'page');
1052 lhssh = this.editor[editor_name1].getLineHandle(llf);
1068 lhssh = this.editor[editor_name1].getLineHandle(llf);
1053 ls = { top: tls.top, bottom: tls.top + lhssh.height };
1069 ls = { top: tls.top, bottom: tls.top + lhssh.height };
1054
1070
1055 tle = this.editor[editor_name1].cursorCoords({line: llt, ch: 0}, 'page');
1071 tle = this.editor[editor_name1].cursorCoords({line: llt, ch: 0}, 'page');
1056 lhseh = this.editor[editor_name1].getLineHandle(llt);
1072 lhseh = this.editor[editor_name1].getLineHandle(llt);
1057 le = { top: tle.top, bottom: tle.top + lhseh.height };
1073 le = { top: tle.top, bottom: tle.top + lhseh.height };
1058
1074
1059 tls = this.editor[editor_name2].cursorCoords({line: rlf, ch: 0}, 'page');
1075 tls = this.editor[editor_name2].cursorCoords({line: rlf, ch: 0}, 'page');
1060 rhssh = this.editor[editor_name2].getLineHandle(rlf);
1076 rhssh = this.editor[editor_name2].getLineHandle(rlf);
1061 rs = { top: tls.top, bottom: tls.top + rhssh.height };
1077 rs = { top: tls.top, bottom: tls.top + rhssh.height };
1062
1078
1063 tle = this.editor[editor_name2].cursorCoords({line: rlt, ch: 0}, 'page');
1079 tle = this.editor[editor_name2].cursorCoords({line: rlt, ch: 0}, 'page');
1064 rhseh = this.editor[editor_name2].getLineHandle(rlt);
1080 rhseh = this.editor[editor_name2].getLineHandle(rlt);
1065 re = { top: tle.top, bottom: tle.top + rhseh.height };
1081 re = { top: tle.top, bottom: tle.top + rhseh.height };
1066 }
1082 }
1067 else {
1083 else {
1068 // If not using line-wrapping, we can calculate the line position
1084 // If not using line-wrapping, we can calculate the line position
1069 ls = {
1085 ls = {
1070 top: lhschc.top + llf * this.em_height,
1086 top: lhschc.top + llf * this.em_height,
1071 bottom: lhschc.bottom + llf * this.em_height + 2
1087 bottom: lhschc.bottom + llf * this.em_height + 2
1072 };
1088 };
1073 le = {
1089 le = {
1074 top: lhschc.top + llt * this.em_height,
1090 top: lhschc.top + llt * this.em_height,
1075 bottom: lhschc.bottom + llt * this.em_height + 2
1091 bottom: lhschc.bottom + llt * this.em_height + 2
1076 };
1092 };
1077 rs = {
1093 rs = {
1078 top: rhschc.top + rlf * this.em_height,
1094 top: rhschc.top + rlf * this.em_height,
1079 bottom: rhschc.bottom + rlf * this.em_height + 2
1095 bottom: rhschc.bottom + rlf * this.em_height + 2
1080 };
1096 };
1081 re = {
1097 re = {
1082 top: rhschc.top + rlt * this.em_height,
1098 top: rhschc.top + rlt * this.em_height,
1083 bottom: rhschc.bottom + rlt * this.em_height + 2
1099 bottom: rhschc.bottom + rlt * this.em_height + 2
1084 };
1100 };
1085 }
1101 }
1086
1102
1087 if (change['op'] == 'a') {
1103 if (change['op'] == 'a') {
1088 // adds (right), normally start from the end of the lhs,
1104 // adds (right), normally start from the end of the lhs,
1089 // except for the case when the start of the rhs is 0
1105 // except for the case when the start of the rhs is 0
1090 if (rlf > 0) {
1106 if (rlf > 0) {
1091 ls.top = ls.bottom;
1107 ls.top = ls.bottom;
1092 ls.bottom += this.em_height;
1108 ls.bottom += this.em_height;
1093 le = ls;
1109 le = ls;
1094 }
1110 }
1095 }
1111 }
1096 else if (change['op'] == 'd') {
1112 else if (change['op'] == 'd') {
1097 // deletes (left) normally finish from the end of the rhs,
1113 // deletes (left) normally finish from the end of the rhs,
1098 // except for the case when the start of the lhs is 0
1114 // except for the case when the start of the lhs is 0
1099 if (llf > 0) {
1115 if (llf > 0) {
1100 rs.top = rs.bottom;
1116 rs.top = rs.bottom;
1101 rs.bottom += this.em_height;
1117 rs.bottom += this.em_height;
1102 re = rs;
1118 re = rs;
1103 }
1119 }
1104 }
1120 }
1105 change['lhs-y-start'] = this.draw_top_offset + ls.top;
1121 change['lhs-y-start'] = this.draw_top_offset + ls.top;
1106 if (change['op'] == 'c' || change['op'] == 'd') {
1122 if (change['op'] == 'c' || change['op'] == 'd') {
1107 change['lhs-y-end'] = this.draw_top_offset + le.bottom;
1123 change['lhs-y-end'] = this.draw_top_offset + le.bottom;
1108 }
1124 }
1109 else {
1125 else {
1110 change['lhs-y-end'] = this.draw_top_offset + le.top;
1126 change['lhs-y-end'] = this.draw_top_offset + le.top;
1111 }
1127 }
1112 change['rhs-y-start'] = this.draw_top_offset + rs.top;
1128 change['rhs-y-start'] = this.draw_top_offset + rs.top;
1113 if (change['op'] == 'c' || change['op'] == 'a') {
1129 if (change['op'] == 'c' || change['op'] == 'a') {
1114 change['rhs-y-end'] = this.draw_top_offset + re.bottom;
1130 change['rhs-y-end'] = this.draw_top_offset + re.bottom;
1115 }
1131 }
1116 else {
1132 else {
1117 change['rhs-y-end'] = this.draw_top_offset + re.top;
1133 change['rhs-y-end'] = this.draw_top_offset + re.top;
1118 }
1134 }
1119 this.trace('calc', 'change calculated', i, change);
1135 this.trace('calc', 'change calculated', i, change);
1120 }
1136 }
1121 return changes;
1137 return changes;
1122 },
1138 },
1123 _markup_changes: function (editor_name1, editor_name2, changes) {
1139 _markup_changes: function (editor_name1, editor_name2, changes) {
1124 jQuery('.merge-button').remove(); // clear
1140 this.element.find('.merge-button').remove(); //clear
1125
1141
1126 var self = this;
1142 var self = this;
1127 var led = this.editor[editor_name1];
1143 var led = this.editor[editor_name1];
1128 var red = this.editor[editor_name2];
1144 var red = this.editor[editor_name2];
1145 var current_diff = this._current_diff;
1129
1146
1130 var timer = new Mgly.Timer();
1147 var timer = new Mgly.Timer();
1131 led.operation(function() {
1148 led.operation(function() {
1132 for (var i = 0; i < changes.length; ++i) {
1149 for (var i = 0; i < changes.length; ++i) {
1133 var change = changes[i];
1150 var change = changes[i];
1134 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1151 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1135 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1152 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1136 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1153 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1137 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1154 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1138
1155
1139 var clazz = ['mergely', 'lhs', change['op'], 'cid-' + i];
1156 var clazz = ['mergely', 'lhs', change['op'], 'cid-' + i];
1140 led.addLineClass(llf, 'background', 'start');
1157 led.addLineClass(llf, 'background', 'start');
1141 led.addLineClass(llt, 'background', 'end');
1158 led.addLineClass(llt, 'background', 'end');
1142
1159
1160 if (current_diff == i) {
1161 if (llf != llt) {
1162 led.addLineClass(llf, 'background', 'current');
1163 }
1164 led.addLineClass(llt, 'background', 'current');
1165 }
1143 if (llf == 0 && llt == 0 && rlf == 0) {
1166 if (llf == 0 && llt == 0 && rlf == 0) {
1144 led.addLineClass(llf, 'background', clazz.join(' '));
1167 led.addLineClass(llf, 'background', clazz.join(' '));
1145 led.addLineClass(llf, 'background', 'first');
1168 led.addLineClass(llf, 'background', 'first');
1146 }
1169 }
1147 else {
1170 else {
1148 // apply change for each line in-between the changed lines
1171 // apply change for each line in-between the changed lines
1149 for (var j = llf; j <= llt; ++j) {
1172 for (var j = llf; j <= llt; ++j) {
1150 led.addLineClass(j, 'background', clazz.join(' '));
1173 led.addLineClass(j, 'background', clazz.join(' '));
1151 led.addLineClass(j, 'background', clazz.join(' '));
1174 led.addLineClass(j, 'background', clazz.join(' '));
1152 }
1175 }
1153 }
1176 }
1154
1177
1155 if (!red.getOption('readOnly')) {
1178 if (!red.getOption('readOnly')) {
1156 // add widgets to lhs, if rhs is not read only
1179 // add widgets to lhs, if rhs is not read only
1157 var rhs_button = self.merge_rhs_button.clone();
1180 var rhs_button = self.merge_rhs_button.clone();
1158 if (rhs_button.button) {
1181 if (rhs_button.button) {
1159 //jquery-ui support
1182 //jquery-ui support
1160 rhs_button.button({icons: {primary: 'ui-icon-triangle-1-e'}, text: false});
1183 rhs_button.button({icons: {primary: 'ui-icon-triangle-1-e'}, text: false});
1161 }
1184 }
1162 rhs_button.addClass('merge-button');
1185 rhs_button.addClass('merge-button');
1163 rhs_button.attr('id', 'merge-rhs-' + i);
1186 rhs_button.attr('id', 'merge-rhs-' + i);
1164 led.setGutterMarker(llf, 'merge', rhs_button.get(0));
1187 led.setGutterMarker(llf, 'merge', rhs_button.get(0));
1165 }
1188 }
1166 }
1189 }
1167 });
1190 });
1168
1191
1169 var vp = this._get_viewport(editor_name1, editor_name2);
1192 var vp = this._get_viewport(editor_name1, editor_name2);
1170
1193
1171 this.trace('change', 'markup lhs-editor time', timer.stop());
1194 this.trace('change', 'markup lhs-editor time', timer.stop());
1172 red.operation(function() {
1195 red.operation(function() {
1173 for (var i = 0; i < changes.length; ++i) {
1196 for (var i = 0; i < changes.length; ++i) {
1174 var change = changes[i];
1197 var change = changes[i];
1175 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1198 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1176 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1199 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1177 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1200 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1178 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1201 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1179
1202
1180 if (!self._is_change_in_view(vp, change)) {
1203 if (!self._is_change_in_view(vp, change)) {
1181 // if the change is outside the viewport, skip
1204 // if the change is outside the viewport, skip
1182 continue;
1205 continue;
1183 }
1206 }
1184
1207
1185 var clazz = ['mergely', 'rhs', change['op'], 'cid-' + i];
1208 var clazz = ['mergely', 'rhs', change['op'], 'cid-' + i];
1186 red.addLineClass(rlf, 'background', 'start');
1209 red.addLineClass(rlf, 'background', 'start');
1187 red.addLineClass(rlt, 'background', 'end');
1210 red.addLineClass(rlt, 'background', 'end');
1188
1211
1212 if (current_diff == i) {
1213 if (rlf != rlt) {
1214 red.addLineClass(rlf, 'background', 'current');
1215 }
1216 red.addLineClass(rlt, 'background', 'current');
1217 }
1189 if (rlf == 0 && rlt == 0 && llf == 0) {
1218 if (rlf == 0 && rlt == 0 && llf == 0) {
1190 red.addLineClass(rlf, 'background', clazz.join(' '));
1219 red.addLineClass(rlf, 'background', clazz.join(' '));
1191 red.addLineClass(rlf, 'background', 'first');
1220 red.addLineClass(rlf, 'background', 'first');
1192 }
1221 }
1193 else {
1222 else {
1194 // apply change for each line in-between the changed lines
1223 // apply change for each line in-between the changed lines
1195 for (var j = rlf; j <= rlt; ++j) {
1224 for (var j = rlf; j <= rlt; ++j) {
1196 red.addLineClass(j, 'background', clazz.join(' '));
1225 red.addLineClass(j, 'background', clazz.join(' '));
1197 red.addLineClass(j, 'background', clazz.join(' '));
1226 red.addLineClass(j, 'background', clazz.join(' '));
1198 }
1227 }
1199 }
1228 }
1200
1229
1201 if (!led.getOption('readOnly')) {
1230 if (!led.getOption('readOnly')) {
1202 // add widgets to rhs, if lhs is not read only
1231 // add widgets to rhs, if lhs is not read only
1203 var lhs_button = self.merge_lhs_button.clone();
1232 var lhs_button = self.merge_lhs_button.clone();
1204 if (lhs_button.button) {
1233 if (lhs_button.button) {
1205 //jquery-ui support
1234 //jquery-ui support
1206 lhs_button.button({icons: {primary: 'ui-icon-triangle-1-w'}, text: false});
1235 lhs_button.button({icons: {primary: 'ui-icon-triangle-1-w'}, text: false});
1207 }
1236 }
1208 lhs_button.addClass('merge-button');
1237 lhs_button.addClass('merge-button');
1209 lhs_button.attr('id', 'merge-lhs-' + i);
1238 lhs_button.attr('id', 'merge-lhs-' + i);
1210 red.setGutterMarker(rlf, 'merge', lhs_button.get(0));
1239 red.setGutterMarker(rlf, 'merge', lhs_button.get(0));
1211 }
1240 }
1212 }
1241 }
1213 });
1242 });
1214 this.trace('change', 'markup rhs-editor time', timer.stop());
1243 this.trace('change', 'markup rhs-editor time', timer.stop());
1215
1244
1216 // mark text deleted, LCS changes
1245 // mark text deleted, LCS changes
1217 var marktext = [], i, j, k, p;
1246 var marktext = [], i, j, k, p;
1218 for (i = 0; this.settings.lcs && i < changes.length; ++i) {
1247 for (i = 0; this.settings.lcs && i < changes.length; ++i) {
1219 var change = changes[i];
1248 var change = changes[i];
1220 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1249 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1221 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1250 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1222 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1251 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1223 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1252 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1224
1253
1225 if (!this._is_change_in_view(vp, change)) {
1254 if (!this._is_change_in_view(vp, change)) {
1226 // if the change is outside the viewport, skip
1255 // if the change is outside the viewport, skip
1227 continue;
1256 continue;
1228 }
1257 }
1229 if (change['op'] == 'd') {
1258 if (change['op'] == 'd') {
1230 // apply delete to cross-out (left-hand side only)
1259 // apply delete to cross-out (left-hand side only)
1231 var from = llf;
1260 var from = llf;
1232 var to = llt;
1261 var to = llt;
1233 var to_ln = led.lineInfo(to);
1262 var to_ln = led.lineInfo(to);
1234 if (to_ln) {
1263 if (to_ln) {
1235 marktext.push([led, {line:from, ch:0}, {line:to, ch:to_ln.text.length}, {className: 'mergely ch d lhs'}]);
1264 marktext.push([led, {line:from, ch:0}, {line:to, ch:to_ln.text.length}, {className: 'mergely ch d lhs'}]);
1236 }
1265 }
1237 }
1266 }
1238 else if (change['op'] == 'c') {
1267 else if (change['op'] == 'c') {
1239 // apply LCS changes to each line
1268 // apply LCS changes to each line
1240 for (j = llf, k = rlf, p = 0;
1269 for (j = llf, k = rlf, p = 0;
1241 ((j >= 0) && (j <= llt)) || ((k >= 0) && (k <= rlt));
1270 ((j >= 0) && (j <= llt)) || ((k >= 0) && (k <= rlt));
1242 ++j, ++k) {
1271 ++j, ++k) {
1243 var lhs_line, rhs_line;
1272 var lhs_line, rhs_line;
1244 if (k + p > rlt) {
1273 if (k + p > rlt) {
1245 // lhs continues past rhs, mark lhs as deleted
1274 // lhs continues past rhs, mark lhs as deleted
1246 lhs_line = led.getLine( j );
1275 lhs_line = led.getLine( j );
1247 marktext.push([led, {line:j, ch:0}, {line:j, ch:lhs_line.length}, {className: 'mergely ch d lhs'}]);
1276 marktext.push([led, {line:j, ch:0}, {line:j, ch:lhs_line.length}, {className: 'mergely ch d lhs'}]);
1248 continue;
1277 continue;
1249 }
1278 }
1250 if (j + p > llt) {
1279 if (j + p > llt) {
1251 // rhs continues past lhs, mark rhs as added
1280 // rhs continues past lhs, mark rhs as added
1252 rhs_line = red.getLine( k );
1281 rhs_line = red.getLine( k );
1253 marktext.push([red, {line:k, ch:0}, {line:k, ch:rhs_line.length}, {className: 'mergely ch a rhs'}]);
1282 marktext.push([red, {line:k, ch:0}, {line:k, ch:rhs_line.length}, {className: 'mergely ch a rhs'}]);
1254 continue;
1283 continue;
1255 }
1284 }
1256 lhs_line = led.getLine( j );
1285 lhs_line = led.getLine( j );
1257 rhs_line = red.getLine( k );
1286 rhs_line = red.getLine( k );
1258 var lcs = new Mgly.LCS(lhs_line, rhs_line);
1287 var lcs = new Mgly.LCS(lhs_line, rhs_line);
1259 lcs.diff(
1288 lcs.diff(
1260 function added (from, to) {
1289 function added (from, to) {
1261 marktext.push([red, {line:k, ch:from}, {line:k, ch:to}, {className: 'mergely ch a rhs'}]);
1290 marktext.push([red, {line:k, ch:from}, {line:k, ch:to}, {className: 'mergely ch a rhs'}]);
1262 },
1291 },
1263 function removed (from, to) {
1292 function removed (from, to) {
1264 marktext.push([led, {line:j, ch:from}, {line:j, ch:to}, {className: 'mergely ch d lhs'}]);
1293 marktext.push([led, {line:j, ch:from}, {line:j, ch:to}, {className: 'mergely ch d lhs'}]);
1265 }
1294 }
1266 );
1295 );
1267 }
1296 }
1268 }
1297 }
1269 }
1298 }
1270 this.trace('change', 'LCS marktext time', timer.stop());
1299 this.trace('change', 'LCS marktext time', timer.stop());
1271
1300
1272 // mark changes outside closure
1301 // mark changes outside closure
1273 led.operation(function() {
1302 led.operation(function() {
1274 // apply lhs markup
1303 // apply lhs markup
1275 for (var i = 0; i < marktext.length; ++i) {
1304 for (var i = 0; i < marktext.length; ++i) {
1276 var m = marktext[i];
1305 var m = marktext[i];
1277 if (m[0].doc.id != led.getDoc().id) continue;
1306 if (m[0].doc.id != led.getDoc().id) continue;
1278 self.chfns[self.id + '-lhs'].push(m[0].markText(m[1], m[2], m[3]));
1307 self.chfns[self.id + '-lhs'].push(m[0].markText(m[1], m[2], m[3]));
1279 }
1308 }
1280 });
1309 });
1281 red.operation(function() {
1310 red.operation(function() {
1282 // apply lhs markup
1311 // apply lhs markup
1283 for (var i = 0; i < marktext.length; ++i) {
1312 for (var i = 0; i < marktext.length; ++i) {
1284 var m = marktext[i];
1313 var m = marktext[i];
1285 if (m[0].doc.id != red.getDoc().id) continue;
1314 if (m[0].doc.id != red.getDoc().id) continue;
1286 self.chfns[self.id + '-rhs'].push(m[0].markText(m[1], m[2], m[3]));
1315 self.chfns[self.id + '-rhs'].push(m[0].markText(m[1], m[2], m[3]));
1287 }
1316 }
1288 });
1317 });
1318
1289 this.trace('change', 'LCS markup time', timer.stop());
1319 this.trace('change', 'LCS markup time', timer.stop());
1290
1320
1291 // merge buttons
1321 // merge buttons
1292 var ed = {lhs:led, rhs:red};
1322 var ed = {lhs:led, rhs:red};
1293 jQuery('.merge-button').on('click', function(ev){
1323 this.element.find('.merge-button').on('click', function(ev){
1294 // side of mouseenter
1324 // side of mouseenter
1295 var side = 'rhs';
1325 var side = 'rhs';
1296 var oside = 'lhs';
1326 var oside = 'lhs';
1297 var parent = jQuery(this).parents('#' + self.id + '-editor-lhs');
1327 var parent = jQuery(this).parents('#' + self.id + '-editor-lhs');
1298 if (parent.length) {
1328 if (parent.length) {
1299 side = 'lhs';
1329 side = 'lhs';
1300 oside = 'rhs';
1330 oside = 'rhs';
1301 }
1331 }
1302 var pos = ed[side].coordsChar({left:ev.pageX, top:ev.pageY});
1332 var pos = ed[side].coordsChar({left:ev.pageX, top:ev.pageY});
1303
1333
1304 // get the change id
1334 // get the change id
1305 var cid = null;
1335 var cid = null;
1306 var info = ed[side].lineInfo(pos.line);
1336 var info = ed[side].lineInfo(pos.line);
1307 jQuery.each(info.bgClass.split(' '), function(i, clazz) {
1337 jQuery.each(info.bgClass.split(' '), function(i, clazz) {
1308 if (clazz.indexOf('cid-') == 0) {
1338 if (clazz.indexOf('cid-') == 0) {
1309 cid = parseInt(clazz.split('-')[1], 10);
1339 cid = parseInt(clazz.split('-')[1], 10);
1310 return false;
1340 return false;
1311 }
1341 }
1312 });
1342 });
1313 var change = self.changes[cid];
1343 var change = self.changes[cid];
1314 self._merge_change(change, side, oside);
1344 self._merge_change(change, side, oside);
1315 return false;
1345 return false;
1316 });
1346 });
1347
1348 // gutter markup
1349 var lhsLineNumbers = $('#mergely-lhs ~ .CodeMirror').find('.CodeMirror-linenumber');
1350 var rhsLineNumbers = $('#mergely-rhs ~ .CodeMirror').find('.CodeMirror-linenumber');
1351 rhsLineNumbers.removeClass('mergely current');
1352 lhsLineNumbers.removeClass('mergely current');
1353 for (var i = 0; i < changes.length; ++i) {
1354 if (current_diff == i && change.op !== 'd') {
1355 var change = changes[i];
1356 var j, jf = change['rhs-line-from'], jt = change['rhs-line-to'] + 1;
1357 for (j = jf; j < jt; j++) {
1358 var n = (j + 1).toString();
1359 rhsLineNumbers
1360 .filter(function(i, node) { return $(node).text() === n; })
1361 .addClass('mergely current');
1362 }
1363 }
1364 if (current_diff == i && change.op !== 'a') {
1365 var change = changes[i];
1366 jf = change['lhs-line-from'], jt = change['lhs-line-to'] + 1;
1367 for (j = jf; j < jt; j++) {
1368 var n = (j + 1).toString();
1369 lhsLineNumbers
1370 .filter(function(i, node) { return $(node).text() === n; })
1371 .addClass('mergely current');
1372 }
1373 }
1374 }
1375
1317 this.trace('change', 'markup buttons time', timer.stop());
1376 this.trace('change', 'markup buttons time', timer.stop());
1318 },
1377 },
1319 _merge_change : function(change, side, oside) {
1378 _merge_change : function(change, side, oside) {
1320 if (!change) return;
1379 if (!change) return;
1321 var led = this.editor[this.id+'-lhs'];
1380 var led = this.editor[this.id+'-lhs'];
1322 var red = this.editor[this.id+'-rhs'];
1381 var red = this.editor[this.id+'-rhs'];
1323 var ed = {lhs:led, rhs:red};
1382 var ed = {lhs:led, rhs:red};
1324 var i, from, to;
1383 var i, from, to;
1325
1384
1326 var text = ed[side].getRange(
1385 var text = ed[side].getRange(
1327 CodeMirror.Pos(change[side + '-line-from'], 0),
1386 CodeMirror.Pos(change[side + '-line-from'], 0),
1328 CodeMirror.Pos(change[side + '-line-to'] + 1, 0));
1387 CodeMirror.Pos(change[side + '-line-to'] + 1, 0));
1329
1388
1330 if (change['op'] == 'c') {
1389 if (change['op'] == 'c') {
1331 ed[oside].replaceRange(text,
1390 ed[oside].replaceRange(text,
1332 CodeMirror.Pos(change[oside + '-line-from'], 0),
1391 CodeMirror.Pos(change[oside + '-line-from'], 0),
1333 CodeMirror.Pos(change[oside + '-line-to'] + 1, 0));
1392 CodeMirror.Pos(change[oside + '-line-to'] + 1, 0));
1334 }
1393 }
1335 else if (side == 'rhs') {
1394 else if (side == 'rhs') {
1336 if (change['op'] == 'a') {
1395 if (change['op'] == 'a') {
1337 ed[oside].replaceRange(text,
1396 ed[oside].replaceRange(text,
1338 CodeMirror.Pos(change[oside + '-line-from'] + 1, 0),
1397 CodeMirror.Pos(change[oside + '-line-from'] + 1, 0),
1339 CodeMirror.Pos(change[oside + '-line-to'] + 1, 0));
1398 CodeMirror.Pos(change[oside + '-line-to'] + 1, 0));
1340 }
1399 }
1341 else {// 'd'
1400 else {// 'd'
1342 from = parseInt(change[oside + '-line-from'], 10);
1401 from = parseInt(change[oside + '-line-from'], 10);
1343 to = parseInt(change[oside + '-line-to'], 10);
1402 to = parseInt(change[oside + '-line-to'], 10);
1344 for (i = to; i >= from; --i) {
1403 for (i = to; i >= from; --i) {
1345 ed[oside].setCursor({line: i, ch: -1});
1404 ed[oside].setCursor({line: i, ch: -1});
1346 ed[oside].execCommand('deleteLine');
1405 ed[oside].execCommand('deleteLine');
1347 }
1406 }
1348 }
1407 }
1349 }
1408 }
1350 else if (side == 'lhs') {
1409 else if (side == 'lhs') {
1351 if (change['op'] == 'a') {
1410 if (change['op'] == 'a') {
1352 from = parseInt(change[oside + '-line-from'], 10);
1411 from = parseInt(change[oside + '-line-from'], 10);
1353 to = parseInt(change[oside + '-line-to'], 10);
1412 to = parseInt(change[oside + '-line-to'], 10);
1354 for (i = to; i >= from; --i) {
1413 for (i = to; i >= from; --i) {
1355 //ed[oside].removeLine(i);
1414 //ed[oside].removeLine(i);
1356 ed[oside].setCursor({line: i, ch: -1});
1415 ed[oside].setCursor({line: i, ch: -1});
1357 ed[oside].execCommand('deleteLine');
1416 ed[oside].execCommand('deleteLine');
1358 }
1417 }
1359 }
1418 }
1360 else {// 'd'
1419 else {// 'd'
1361 ed[oside].replaceRange( text,
1420 ed[oside].replaceRange( text,
1362 CodeMirror.Pos(change[oside + '-line-from'] + 1, 0));
1421 CodeMirror.Pos(change[oside + '-line-from'] + 1, 0));
1363 }
1422 }
1364 }
1423 }
1365 //reset
1424 //reset
1366 ed['lhs'].setValue(ed['lhs'].getValue());
1425 ed['lhs'].setValue(ed['lhs'].getValue());
1367 ed['rhs'].setValue(ed['rhs'].getValue());
1426 ed['rhs'].setValue(ed['rhs'].getValue());
1368
1427
1369 this._scroll_to_change(change);
1428 this._scroll_to_change(change);
1370 },
1429 },
1371 _draw_info: function(editor_name1, editor_name2) {
1430 _draw_info: function(editor_name1, editor_name2) {
1372 var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height();
1431 var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height();
1373 var gutter_height = jQuery(this.editor[editor_name1].getScrollerElement()).children(':first-child').height();
1432 var gutter_height = jQuery(this.editor[editor_name1].getScrollerElement()).children(':first-child').height();
1374 var dcanvas = document.getElementById(editor_name1 + '-' + editor_name2 + '-canvas');
1433 var dcanvas = document.getElementById(editor_name1 + '-' + editor_name2 + '-canvas');
1375 if (dcanvas == undefined) throw 'Failed to find: ' + editor_name1 + '-' + editor_name2 + '-canvas';
1434 if (dcanvas == undefined) throw 'Failed to find: ' + editor_name1 + '-' + editor_name2 + '-canvas';
1376 var clhs = jQuery('#' + this.id + '-lhs-margin');
1435 var clhs = this.element.find('#' + this.id + '-lhs-margin');
1377 var crhs = jQuery('#' + this.id + '-rhs-margin');
1436 var crhs = this.element.find('#' + this.id + '-rhs-margin');
1378 return {
1437 return {
1379 visible_page_height: visible_page_height,
1438 visible_page_height: visible_page_height,
1380 gutter_height: gutter_height,
1439 gutter_height: gutter_height,
1381 visible_page_ratio: (visible_page_height / gutter_height),
1440 visible_page_ratio: (visible_page_height / gutter_height),
1382 margin_ratio: (visible_page_height / gutter_height),
1441 margin_ratio: (visible_page_height / gutter_height),
1383 lhs_scroller: jQuery(this.editor[editor_name1].getScrollerElement()),
1442 lhs_scroller: jQuery(this.editor[editor_name1].getScrollerElement()),
1384 rhs_scroller: jQuery(this.editor[editor_name2].getScrollerElement()),
1443 rhs_scroller: jQuery(this.editor[editor_name2].getScrollerElement()),
1385 lhs_lines: this.editor[editor_name1].lineCount(),
1444 lhs_lines: this.editor[editor_name1].lineCount(),
1386 rhs_lines: this.editor[editor_name2].lineCount(),
1445 rhs_lines: this.editor[editor_name2].lineCount(),
1387 dcanvas: dcanvas,
1446 dcanvas: dcanvas,
1388 clhs: clhs,
1447 clhs: clhs,
1389 crhs: crhs,
1448 crhs: crhs,
1390 lhs_xyoffset: jQuery(clhs).offset(),
1449 lhs_xyoffset: jQuery(clhs).offset(),
1391 rhs_xyoffset: jQuery(crhs).offset()
1450 rhs_xyoffset: jQuery(crhs).offset()
1392 };
1451 };
1393 },
1452 },
1394 _draw_diff: function(editor_name1, editor_name2, changes) {
1453 _draw_diff: function(editor_name1, editor_name2, changes) {
1395 var ex = this._draw_info(editor_name1, editor_name2);
1454 var ex = this._draw_info(editor_name1, editor_name2);
1396 var mcanvas_lhs = ex.clhs.get(0);
1455 var mcanvas_lhs = ex.clhs.get(0);
1397 var mcanvas_rhs = ex.crhs.get(0);
1456 var mcanvas_rhs = ex.crhs.get(0);
1398 var ctx = ex.dcanvas.getContext('2d');
1457 var ctx = ex.dcanvas.getContext('2d');
1399 var ctx_lhs = mcanvas_lhs.getContext('2d');
1458 var ctx_lhs = mcanvas_lhs.getContext('2d');
1400 var ctx_rhs = mcanvas_rhs.getContext('2d');
1459 var ctx_rhs = mcanvas_rhs.getContext('2d');
1401
1460
1402 this.trace('draw', 'visible_page_height', ex.visible_page_height);
1461 this.trace('draw', 'visible_page_height', ex.visible_page_height);
1403 this.trace('draw', 'gutter_height', ex.gutter_height);
1462 this.trace('draw', 'gutter_height', ex.gutter_height);
1404 this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio);
1463 this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio);
1405 this.trace('draw', 'lhs-scroller-top', ex.lhs_scroller.scrollTop());
1464 this.trace('draw', 'lhs-scroller-top', ex.lhs_scroller.scrollTop());
1406 this.trace('draw', 'rhs-scroller-top', ex.rhs_scroller.scrollTop());
1465 this.trace('draw', 'rhs-scroller-top', ex.rhs_scroller.scrollTop());
1407
1466
1408 jQuery.each(jQuery.find('#' + this.id + ' canvas'), function () {
1467 jQuery.each(this.element.find('canvas'), function () {
1409 jQuery(this).get(0).height = ex.visible_page_height;
1468 jQuery(this).get(0).height = ex.visible_page_height;
1410 });
1469 });
1411
1470
1412 ex.clhs.unbind('click');
1471 ex.clhs.unbind('click');
1413 ex.crhs.unbind('click');
1472 ex.crhs.unbind('click');
1414
1473
1415 ctx_lhs.beginPath();
1474 ctx_lhs.beginPath();
1416 ctx_lhs.fillStyle = this.settings.bgcolor;
1475 ctx_lhs.fillStyle = this.settings.bgcolor;
1417 ctx_lhs.strokeStyle = '#888';
1476 ctx_lhs.strokeStyle = '#888';
1418 ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height);
1477 ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height);
1419 ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
1478 ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
1420
1479
1421 ctx_rhs.beginPath();
1480 ctx_rhs.beginPath();
1422 ctx_rhs.fillStyle = this.settings.bgcolor;
1481 ctx_rhs.fillStyle = this.settings.bgcolor;
1423 ctx_rhs.strokeStyle = '#888';
1482 ctx_rhs.strokeStyle = '#888';
1424 ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height);
1483 ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height);
1425 ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
1484 ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
1426
1485
1427 var vp = this._get_viewport(editor_name1, editor_name2);
1486 var vp = this._get_viewport(editor_name1, editor_name2);
1428 for (var i = 0; i < changes.length; ++i) {
1487 for (var i = 0; i < changes.length; ++i) {
1429 var change = changes[i];
1488 var change = changes[i];
1489 var fill = this.settings.fgcolor[change['op']];
1490 if (this._current_diff==i) {
1491 fill = '#000';
1492 }
1430
1493
1431 this.trace('draw', change);
1494 this.trace('draw', change);
1432 // margin indicators
1495 // margin indicators
1433 var lhs_y_start = ((change['lhs-y-start'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio);
1496 var lhs_y_start = ((change['lhs-y-start'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio);
1434 var lhs_y_end = ((change['lhs-y-end'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1;
1497 var lhs_y_end = ((change['lhs-y-end'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1;
1435 var rhs_y_start = ((change['rhs-y-start'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio);
1498 var rhs_y_start = ((change['rhs-y-start'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio);
1436 var rhs_y_end = ((change['rhs-y-end'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1;
1499 var rhs_y_end = ((change['rhs-y-end'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1;
1437 this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
1500 this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
1438
1501
1439 ctx_lhs.beginPath();
1502 ctx_lhs.beginPath();
1440 ctx_lhs.fillStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']];
1503 ctx_lhs.fillStyle = fill;
1441 ctx_lhs.strokeStyle = '#000';
1504 ctx_lhs.strokeStyle = '#000';
1442 ctx_lhs.lineWidth = 0.5;
1505 ctx_lhs.lineWidth = 0.5;
1443 ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
1506 ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
1444 ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
1507 ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
1445
1508
1446 ctx_rhs.beginPath();
1509 ctx_rhs.beginPath();
1447 ctx_rhs.fillStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']];
1510 ctx_rhs.fillStyle = fill;
1448 ctx_rhs.strokeStyle = '#000';
1511 ctx_rhs.strokeStyle = '#000';
1449 ctx_rhs.lineWidth = 0.5;
1512 ctx_rhs.lineWidth = 0.5;
1450 ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
1513 ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
1451 ctx_rhs.strokeRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
1514 ctx_rhs.strokeRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
1452
1515
1453 if (!this._is_change_in_view(vp, change)) {
1516 if (!this._is_change_in_view(vp, change)) {
1454 continue;
1517 continue;
1455 }
1518 }
1456
1519
1457 lhs_y_start = change['lhs-y-start'];
1520 lhs_y_start = change['lhs-y-start'];
1458 lhs_y_end = change['lhs-y-end'];
1521 lhs_y_end = change['lhs-y-end'];
1459 rhs_y_start = change['rhs-y-start'];
1522 rhs_y_start = change['rhs-y-start'];
1460 rhs_y_end = change['rhs-y-end'];
1523 rhs_y_end = change['rhs-y-end'];
1461
1524
1462 var radius = 3;
1525 var radius = 3;
1463
1526
1464 // draw left box
1527 // draw left box
1465 ctx.beginPath();
1528 ctx.beginPath();
1466 ctx.strokeStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']];
1529 ctx.strokeStyle = fill;
1467 ctx.lineWidth = (this._current_diff==i) ? 1.5 : 1;
1530 ctx.lineWidth = (this._current_diff==i) ? 1.5 : 1;
1468
1531
1469 var rectWidth = this.draw_lhs_width;
1532 var rectWidth = this.draw_lhs_width;
1470 var rectHeight = lhs_y_end - lhs_y_start - 1;
1533 var rectHeight = lhs_y_end - lhs_y_start - 1;
1471 var rectX = this.draw_lhs_min;
1534 var rectX = this.draw_lhs_min;
1472 var rectY = lhs_y_start;
1535 var rectY = lhs_y_start;
1473 // top and top top-right corner
1536 // top and top top-right corner
1474
1537
1475 // draw left box
1538 // draw left box
1476 ctx.moveTo(rectX, rectY);
1539 ctx.moveTo(rectX, rectY);
1477 if (navigator.appName == 'Microsoft Internet Explorer') {
1540 if (navigator.appName == 'Microsoft Internet Explorer') {
1478 // IE arcs look awful
1541 // IE arcs look awful
1479 ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_start);
1542 ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_start);
1480 ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_end + 1);
1543 ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_end + 1);
1481 ctx.lineTo(this.draw_lhs_min, lhs_y_end + 1);
1544 ctx.lineTo(this.draw_lhs_min, lhs_y_end + 1);
1482 }
1545 }
1483 else {
1546 else {
1484 if (rectHeight <= 0) {
1547 if (rectHeight <= 0) {
1485 ctx.lineTo(rectX + rectWidth, rectY);
1548 ctx.lineTo(rectX + rectWidth, rectY);
1486 }
1549 }
1487 else {
1550 else {
1488 ctx.arcTo(rectX + rectWidth, rectY, rectX + rectWidth, rectY + radius, radius);
1551 ctx.arcTo(rectX + rectWidth, rectY, rectX + rectWidth, rectY + radius, radius);
1489 ctx.arcTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth - radius, rectY + rectHeight, radius);
1552 ctx.arcTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth - radius, rectY + rectHeight, radius);
1490 }
1553 }
1491 // bottom line
1554 // bottom line
1492 ctx.lineTo(rectX, rectY + rectHeight);
1555 ctx.lineTo(rectX, rectY + rectHeight);
1493 }
1556 }
1494 ctx.stroke();
1557 ctx.stroke();
1495
1558
1496 rectWidth = this.draw_rhs_width;
1559 rectWidth = this.draw_rhs_width;
1497 rectHeight = rhs_y_end - rhs_y_start - 1;
1560 rectHeight = rhs_y_end - rhs_y_start - 1;
1498 rectX = this.draw_rhs_max;
1561 rectX = this.draw_rhs_max;
1499 rectY = rhs_y_start;
1562 rectY = rhs_y_start;
1500
1563
1501 // draw right box
1564 // draw right box
1502 ctx.moveTo(rectX, rectY);
1565 ctx.moveTo(rectX, rectY);
1503 if (navigator.appName == 'Microsoft Internet Explorer') {
1566 if (navigator.appName == 'Microsoft Internet Explorer') {
1504 ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_start);
1567 ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_start);
1505 ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_end + 1);
1568 ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_end + 1);
1506 ctx.lineTo(this.draw_rhs_max, rhs_y_end + 1);
1569 ctx.lineTo(this.draw_rhs_max, rhs_y_end + 1);
1507 }
1570 }
1508 else {
1571 else {
1509 if (rectHeight <= 0) {
1572 if (rectHeight <= 0) {
1510 ctx.lineTo(rectX - rectWidth, rectY);
1573 ctx.lineTo(rectX - rectWidth, rectY);
1511 }
1574 }
1512 else {
1575 else {
1513 ctx.arcTo(rectX - rectWidth, rectY, rectX - rectWidth, rectY + radius, radius);
1576 ctx.arcTo(rectX - rectWidth, rectY, rectX - rectWidth, rectY + radius, radius);
1514 ctx.arcTo(rectX - rectWidth, rectY + rectHeight, rectX - radius, rectY + rectHeight, radius);
1577 ctx.arcTo(rectX - rectWidth, rectY + rectHeight, rectX - radius, rectY + rectHeight, radius);
1515 }
1578 }
1516 ctx.lineTo(rectX, rectY + rectHeight);
1579 ctx.lineTo(rectX, rectY + rectHeight);
1517 }
1580 }
1518 ctx.stroke();
1581 ctx.stroke();
1519
1582
1520 // connect boxes
1583 // connect boxes
1521 var cx = this.draw_lhs_min + this.draw_lhs_width;
1584 var cx = this.draw_lhs_min + this.draw_lhs_width;
1522 var cy = lhs_y_start + (lhs_y_end + 1 - lhs_y_start) / 2.0;
1585 var cy = lhs_y_start + (lhs_y_end + 1 - lhs_y_start) / 2.0;
1523 var dx = this.draw_rhs_max - this.draw_rhs_width;
1586 var dx = this.draw_rhs_max - this.draw_rhs_width;
1524 var dy = rhs_y_start + (rhs_y_end + 1 - rhs_y_start) / 2.0;
1587 var dy = rhs_y_start + (rhs_y_end + 1 - rhs_y_start) / 2.0;
1525 ctx.moveTo(cx, cy);
1588 ctx.moveTo(cx, cy);
1526 if (cy == dy) {
1589 if (cy == dy) {
1527 ctx.lineTo(dx, dy);
1590 ctx.lineTo(dx, dy);
1528 }
1591 }
1529 else {
1592 else {
1530 // fancy!
1593 // fancy!
1531 ctx.bezierCurveTo(
1594 ctx.bezierCurveTo(
1532 cx + 12, cy - 3, // control-1 X,Y
1595 cx + 12, cy - 3, // control-1 X,Y
1533 dx - 12, dy - 3, // control-2 X,Y
1596 dx - 12, dy - 3, // control-2 X,Y
1534 dx, dy);
1597 dx, dy);
1535 }
1598 }
1536 ctx.stroke();
1599 ctx.stroke();
1537 }
1600 }
1538
1601
1539 // visible window feedback
1602 // visible window feedback
1540 ctx_lhs.fillStyle = this.settings.vpcolor;
1603 ctx_lhs.fillStyle = this.settings.vpcolor;
1541 ctx_rhs.fillStyle = this.settings.vpcolor;
1604 ctx_rhs.fillStyle = this.settings.vpcolor;
1542
1605
1543 var lto = ex.clhs.height() * ex.visible_page_ratio;
1606 var lto = ex.clhs.height() * ex.visible_page_ratio;
1544 var lfrom = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height();
1607 var lfrom = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height();
1545 var rto = ex.crhs.height() * ex.visible_page_ratio;
1608 var rto = ex.crhs.height() * ex.visible_page_ratio;
1546 var rfrom = (ex.rhs_scroller.scrollTop() / ex.gutter_height) * ex.crhs.height();
1609 var rfrom = (ex.rhs_scroller.scrollTop() / ex.gutter_height) * ex.crhs.height();
1547 this.trace('draw', 'cls.height', ex.clhs.height());
1610 this.trace('draw', 'cls.height', ex.clhs.height());
1548 this.trace('draw', 'lhs_scroller.scrollTop()', ex.lhs_scroller.scrollTop());
1611 this.trace('draw', 'lhs_scroller.scrollTop()', ex.lhs_scroller.scrollTop());
1549 this.trace('draw', 'gutter_height', ex.gutter_height);
1612 this.trace('draw', 'gutter_height', ex.gutter_height);
1550 this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio);
1613 this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio);
1551 this.trace('draw', 'lhs from', lfrom, 'lhs to', lto);
1614 this.trace('draw', 'lhs from', lfrom, 'lhs to', lto);
1552 this.trace('draw', 'rhs from', rfrom, 'rhs to', rto);
1615 this.trace('draw', 'rhs from', rfrom, 'rhs to', rto);
1553
1616
1554 ctx_lhs.fillRect(1.5, lfrom, 4.5, lto);
1617 ctx_lhs.fillRect(1.5, lfrom, 4.5, lto);
1555 ctx_rhs.fillRect(1.5, rfrom, 4.5, rto);
1618 ctx_rhs.fillRect(1.5, rfrom, 4.5, rto);
1556
1619
1557 ex.clhs.click(function (ev) {
1620 ex.clhs.click(function (ev) {
1558 var y = ev.pageY - ex.lhs_xyoffset.top - (lto / 2);
1621 var y = ev.pageY - ex.lhs_xyoffset.top - (lto / 2);
1559 var sto = Math.max(0, (y / mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight);
1622 var sto = Math.max(0, (y / mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight);
1560 ex.lhs_scroller.scrollTop(sto);
1623 ex.lhs_scroller.scrollTop(sto);
1561 });
1624 });
1562 ex.crhs.click(function (ev) {
1625 ex.crhs.click(function (ev) {
1563 var y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2);
1626 var y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2);
1564 var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight);
1627 var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight);
1565 ex.rhs_scroller.scrollTop(sto);
1628 ex.rhs_scroller.scrollTop(sto);
1566 });
1629 });
1567 },
1630 },
1568 trace: function(name) {
1631 trace: function(name) {
1569 if(this.settings._debug.indexOf(name) >= 0) {
1632 if(this.settings._debug.indexOf(name) >= 0) {
1570 arguments[0] = name + ':';
1633 arguments[0] = name + ':';
1571 console.log([].slice.apply(arguments));
1634 console.log([].slice.apply(arguments));
1572 }
1635 }
1573 }
1636 }
1574 });
1637 });
1575
1638
1576 jQuery.pluginMaker = function(plugin) {
1639 jQuery.pluginMaker = function(plugin) {
1577 // add the plugin function as a jQuery plugin
1640 // add the plugin function as a jQuery plugin
1578 jQuery.fn[plugin.prototype.name] = function(options) {
1641 jQuery.fn[plugin.prototype.name] = function(options) {
1579 // get the arguments
1642 // get the arguments
1580 var args = jQuery.makeArray(arguments),
1643 var args = jQuery.makeArray(arguments),
1581 after = args.slice(1);
1644 after = args.slice(1);
1582 var rc;
1645 var rc;
1583 this.each(function() {
1646 this.each(function() {
1584 // see if we have an instance
1647 // see if we have an instance
1585 var instance = jQuery.data(this, plugin.prototype.name);
1648 var instance = jQuery.data(this, plugin.prototype.name);
1586 if (instance) {
1649 if (instance) {
1587 // call a method on the instance
1650 // call a method on the instance
1588 if (typeof options == "string") {
1651 if (typeof options == "string") {
1589 rc = instance[options].apply(instance, after);
1652 rc = instance[options].apply(instance, after);
1590 } else if (instance.update) {
1653 } else if (instance.update) {
1591 // call update on the instance
1654 // call update on the instance
1592 return instance.update.apply(instance, args);
1655 return instance.update.apply(instance, args);
1593 }
1656 }
1594 } else {
1657 } else {
1595 // create the plugin
1658 // create the plugin
1596 var _plugin = new plugin(this, options);
1659 var _plugin = new plugin(this, options);
1597 }
1660 }
1598 });
1661 });
1599 if (rc != undefined) return rc;
1662 if (rc != undefined) return rc;
1600 };
1663 };
1601 };
1664 };
1602
1665
1603 // make the mergely widget
1666 // make the mergely widget
1604 jQuery.pluginMaker(Mgly.mergely);
1667 jQuery.pluginMaker(Mgly.mergely);
1605
1668
1606 })( window, document, jQuery, CodeMirror ); No newline at end of file
1669 })( window, document, jQuery, CodeMirror );
General Comments 0
You need to be logged in to leave comments. Login now