##// END OF EJS Templates
Migrate to Mergely 3.3.4....
"Bradley M. Kuhn" -
r4125:aa3b5594 rhodecode-2.2.5-gpl
parent child Browse files
Show More
@@ -0,0 +1,111 b''
1 <!DOCTYPE html>
2 <html lang="en">
3 <!--[if IE]>
4 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
5 <![endif]-->
6 <head>
7 <meta charset="utf-8" /><title>Mergely License</title>
8 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
9 <meta name="description" content="Mergely license requirements for open source software and commercial software" />
10 <meta name="keywords" content="diff,merge,compare,compare documents,js diff,javascript diff,comparison,online diff,difference,file,text,unix,patch,algorithm,saas,longest common subsequence,diff online" />
11 <meta name="author" content="Jamie Peabody" />
12 <meta name="author" content="Jamie Peabody" />
13 <link rel="shortcut icon" href="http://www.mergely.com/favicon.ico" />
14 <link href='http://fonts.googleapis.com/css?family=Noto+Sans:400,700' rel='stylesheet' type='text/css' />
15 <link href='fonts/berlin-sans-fb-demi.css' rel='stylesheet' type='text/css' />
16 <link href='style/mergely.css' rel='stylesheet' type='text/css' />
17 <link href='/Mergely/lib/mergely.css' rel='stylesheet' type='text/css' />
18 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
19 <script type="text/javascript">
20 var _gaq = _gaq || [];
21 _gaq.push(['_setAccount', 'UA-85576-5']);
22 _gaq.push(['_trackPageview']);
23 (function() {
24 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
25 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
26 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
27 })();
28 </script>
29 </head>
30 <body>
31 <div id="page">
32 <div id="content">
33 <div id="header">
34 <h1><span>Mergely License - Closed Distribution License</span></h1>
35 <div id="options">
36 <a href="/editor" class="button">Online Diff</a>
37 <a href="/download" class="button">Download</a>
38 </div>
39 <nav>
40 <ul>
41 <li><a href="/">Home</a></li> <li><a href="/doc">Documentation</a></li> <li><a href="/about">About Mergely</a></li> <li><a href="/license">License</a></li> <li><a href="#footer">Contact</a></li> </ul>
42 </nav>
43 </div>
44
45 <div id="main">
46 <h1>Mergely License</h1>
47 <p>
48 All Mergely code is Copyright 2014 by Jamie Peabody.
49 Mergely is distributed under the
50 <a href="http://www.gnu.org/licenses/gpl.html">GPL</a>,
51 <a href="http://www.gnu.org/licenses/lgpl.html">LGPL</a>
52 and
53 <a href="http://www.mozilla.org/MPL/MPL-1.1.html">MPL</a> open source licenses.
54 This triple <b>copyleft</b> licensing model avoids incompatibility with other open
55 source licenses. These open source licenses are specially indicated for:
56 <ul>
57 <li>Integrating Mergely into Open Source software;</li>
58 <li>Personal and educational use of Mergely;</li>
59 <li>
60 Integrating Mergely in commercial software, taking care of satisfying
61 the Open Source licenses terms, while not able or interested on supporting
62 Mergely and its development.
63 </li>
64 </ul>
65 </p>
66 <h2>Mergely Commercial License - Closed Distribution License - CDL</h2>
67 <p>
68 You may contact <a href="mailto:jamie.peabody@gmail.com">Jamie Peabody</a> to enquire about
69 obtaining a CDL license.
70 </p>
71 <p>
72 This license offers a very flexible way to integrate Mergely in your commercial
73 application. These are the main advantages it offers over an Open Source license:
74 </p>
75 <p>
76 Modifications and enhancements do not need to be released under an Open
77 Source license; There is no need to distribute any Open Source license terms
78 along with your product and no reference to it have to be done; You do not have
79 to mention any reference to Mergely in your product; Mergely source code does not
80 have to be distributed with your product; You can remove any file from Mergely
81 when integrating it with your product.
82 </p>
83 <p>
84 The CDL is a lifetime license valid for all previous releases of Mergely published
85 prior to the year of purchase, and any releases in the following year. Please select
86 the license option that best fit your needs above. It includes 1 year of
87 <b>personal e-mail support</b>.
88 </p>
89 <h2>Third party codes</h2>
90 <p>
91 Mergely utilizes <b>CodeMirror</b>, a third-party library released under an
92 <a href="http://en.wikipedia.org/wiki/MIT_License">MIT</a>
93 license. Also used is <b>jQuery</b> and is released under the
94 <a href="http://en.wikipedia.org/wiki/MIT_License">MIT</a> or
95 <a href="http://www.gnu.org/licenses/gpl.html">GPL</a> Version 2 license.
96 </p>
97 </div>
98
99 <div id="footer">
100 <a href="/download" class="download">Download</a>
101 <ul>
102 <li id="google-plus"><a target="_blank" href="http://groups.google.com/group/mergely">http://groups.google.com/group/mergely</a></li>
103 <li id="github"><a target="_blank" href="https://github.com/wickedest/Mergely">https://github.com/wickedest/Mergely</a></li>
104 <li id="email"><a target="_blank" href="mailto:jamie.peabody@gmail.com">jamie.peabody@gmail.com</a></li>
105 </ul>
106 </div>
107 </div>
108 <div id="copyright">By <b>Jamie Peabody</b></div>
109 </div>
110 </body>
111 </html>
@@ -1,58 +1,70 b''
1 1 Third-Party Code Included Herein
2 2 ================================
3 3
4 4 Various third-party code under GPLv3-compatible licenses is included as part
5 5 of Kallithea.
6 6
7 7
8 8
9 9 Bootstrap
10 10 ---------
11 11
12 12 Kallithea incorporates parts of the Javascript system called
13 13 [Bootstrap](http://getbootstrap.com/), which is:
14 14
15 15 Copyright &copy; 2012 Twitter, Inc.
16 16
17 17 and licensed under
18 18 [the Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
19 19
20 20 A copy of the Apache License 2.0 is also included in this distribution in its
21 21 entirety in the file Apache-License-2.0.txt
22 22
23 23
24 24
25 25 Codemirror
26 26 ----------
27 27
28 28 Kallithea incorporates parts of the Javascript system called
29 29 [Codemirror](http://codemirror.net/), which is primarily:
30 30
31 31 Copyright &copy; 2013 by Marijn Haverbeke <marijnh@gmail.com>
32 32
33 33 and licensed under the MIT-permissive license, which is
34 34 [included in this distribution](MIT-Permissive-License.txt).
35 35
36 36 Additional files from upstream Codemirror are copyrighted by various authors
37 37 and licensed under other permissive licenses. The sub-directories under
38 38 [.../public/js/mode/](rhodecode/public/js/mode) include the copyright and
39 39 license notice and information as they appeared in Codemirror's upstream
40 40 release.
41 41
42 42
43 43
44 44 jQuery
45 45 ------
46 46
47 47 Kallithea incorporates the Javascript system called
48 48 [jQuery](http://jquery.org/),
49 49 [herein](rhodecode/public/js/jquery-1.10.2.min.js), and the Corresponding
50 50 Source can be found in https://github.com/jquery/jquery at tag 1.10.2
51 51 (mirrored at https://kallithea-scm.org/repos/mirror/jquery/files/1.10.2/ ).
52 52
53 53 It is Copyright 2013 jQuery Foundation and other contributors http://jquery.com/ and is under an
54 54 [MIT-permissive license](MIT-Permissive-License.txt).
55 55
56 56
57 57
58 Mergely
59 -------
60
61 Kallithea incorporates some code from the Javascript system called
62 [Mergely](http://http://www.mergely.com/).
63 [Mergely's license](http://www.mergely.com/license.php), a
64 [copy of which is included in this repository](LICENSE-MERGELY.html),
65 is (GPL|LGPL|MPL). Kallithea as GPLv3'd project chooses the GPL arm of that
66 tri-license.
67
68
69
58 70 EOF
@@ -1,41 +1,43 b''
1 /**
2 * Copyright (c) 2013 by Jamie Peabody, http://www.mergely.com
3 * All rights reserved.
4 * Version: 3.3.4 2013-11-02
5 *
6 * NOTE by bkuhn@sfconservancy.org for Kallithea:
7 * Mergely license appears at http://www.mergely.com/license.php and in LICENSE-MERGELY.html
8 */
1 9
2 10 /* required */
3 11 .mergely-column textarea { width: 80px; height: 200px; }
4 12 .mergely-column { float: left; }
5 13 .mergely-margin { float: left; }
6 14 .mergely-canvas { float: left; width: 28px; }
7 15
8 16 /* resizeable */
9 17 .mergely-resizer { width: 100%; height: 100%; }
10 18
11 19 /* style configuration */
12 20 .mergely-column { border: 1px solid #ccc; }
13 21 .mergely-active { border: 1px solid #a3d1ff; }
14 22
15 .mergely.a.rhs.start { border-top: 1px solid #ddffdd; }
23 .mergely.a.rhs.start { border-top: 1px solid #a3d1ff; }
16 24 .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; }
25 .mergely.a.rhs.end { border-bottom: 1px solid #a3d1ff; }
26 .mergely.a.rhs { background-color: #ddeeff; }
27 .mergely.a.lhs.start.end.first { border-bottom: 0; border-top: 1px solid #a3d1ff; }
20 28
21 29 .mergely.d.lhs { background-color: #edc0c0; }
22 30 .mergely.d.lhs.end,
23 .mergely.d.rhs.start.end { border-bottom: 1px solid #ffdddd; }
24 .mergely.d.rhs.start.end.first { border-bottom: 0; border-top: 1px solid #ffdddd; }
25 .mergely.d.lhs.start { border-top: 1px solid #ffdddd; }
31 .mergely.d.rhs.start.end { border-bottom: 1px solid #ff7f7f; }
32 .mergely.d.rhs.start.end.first { border-bottom: 0; border-top: 1px solid #ff7f7f; }
33 .mergely.d.lhs.start { border-top: 1px solid #ff7f7f; }
26 34
27 35 .mergely.c.lhs,
28 36 .mergely.c.rhs { background-color: #fafafa; }
29 37 .mergely.c.lhs.start,
30 38 .mergely.c.rhs.start { border-top: 1px solid #a3a3a3; }
31 39 .mergely.c.lhs.end,
32 40 .mergely.c.rhs.end { border-bottom: 1px solid #a3a3a3; }
33 41
34 .mergely.ch.a.rhs { background-color: #ddffdd; }
35 .mergely.ch.d.lhs { background-color: #ffdddd; }
36
37
38 .mergely-margin #compare-lhs-margin,
39 .mergely-margin #compare-rhs-margin {
40 cursor: pointer
41 }
42 .mergely.ch.a.rhs { background-color: #ddeeff; }
43 .mergely.ch.d.lhs { background-color: #edc0c0; text-decoration: line-through; color: #888; }
@@ -1,1460 +1,1516 b''
1 /**
2 * Copyright (c) 2013 by Jamie Peabody, http://www.mergely.com
3 * All rights reserved.
4 * Version: 3.3.4 2013-11-02
5 *
6 * NOTE by bkuhn@sfconservancy.org for Kallithea:
7 * Mergely license appears at http://www.mergely.com/license.php and in LICENSE-MERGELY.html
8 */
1 9 Mgly = {};
2 10
3 11 Mgly.Timer = function(){
4 12 var self = this;
5 13 self.start = function() { self.t0 = new Date().getTime(); }
6 14 self.stop = function() {
7 15 var t1 = new Date().getTime();
8 var d = t1 - self.t0;
16 var d = t1 - self.t0;
9 17 self.t0 = t1;
10 18 return d;
11 19 }
12 20 self.start();
13 21 }
14 22
15 23 Mgly.ChangeExpression = new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
16 24
17 25 Mgly.DiffParser = function(diff) {
18 26 var changes = [];
19 27 var change_id = 0;
20 28 // parse diff
21 29 var diff_lines = diff.split(/\n/);
22 30 for (var i = 0; i < diff_lines.length; ++i) {
23 31 if (diff_lines[i].length == 0) continue;
24 32 var change = {};
25 33 var test = Mgly.ChangeExpression.exec(diff_lines[i]);
26 34 if (test == null) continue;
27 35 // lines are zero-based
28 36 var fr = test[1].split(',');
29 37 change['lhs-line-from'] = fr[0] - 1;
30 38 if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1;
31 39 else change['lhs-line-to'] = fr[1] - 1;
32 40 var to = test[3].split(',');
33 41 change['rhs-line-from'] = to[0] - 1;
34 42 if (to.length == 1) change['rhs-line-to'] = to[0] - 1;
35 43 else change['rhs-line-to'] = to[1] - 1;
36 44 change['op'] = test[2];
37 45 changes[change_id++] = change;
38 46 }
39 47 return changes;
40 48 }
41 49
42 50 Mgly.sizeOf = function(obj) {
43 51 var size = 0, key;
44 52 for (key in obj) {
45 53 if (obj.hasOwnProperty(key)) size++;
46 54 }
47 55 return size;
48 56 }
49 57
50 58 Mgly.LCS = function(x, y) {
51 59 this.x = x.replace(/[ ]{1}/g, '\n');
52 60 this.y = y.replace(/[ ]{1}/g, '\n');
53 61 }
54 62 jQuery.extend(Mgly.LCS.prototype, {
55 63 clear: function() { this.ready = 0; },
56 64 diff: function(added, removed) {
57 var d = new Mgly.diff(this.x, this.y, retain_lines = true, ignore_ws = false);
65 var d = new Mgly.diff(this.x, this.y, {ignorews: false});
58 66 var changes = Mgly.DiffParser(d.normal_form());
59 67 var li = 0, lj = 0;
60 68 for (var i = 0; i < changes.length; ++i) {
61 69 var change = changes[i];
62 70 if (change.op != 'a') {
63 71 // find the starting index of the line
64 li = d.lhs_lines.slice(0, change['lhs-line-from']).join(' ').length;
72 li = d.getLines('lhs').slice(0, change['lhs-line-from']).join(' ').length;
65 73 // get the index of the the span of the change
66 74 lj = change['lhs-line-to'] + 1;
67 75 // get the changed text
68 var lchange = d.lhs_lines.slice(change['lhs-line-from'], lj).join(' ');
76 var lchange = d.getLines('lhs').slice(change['lhs-line-from'], lj).join(' ');
69 77 if (change.op == 'd') lchange += ' ';// include the leading space
70 78 else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word
71 79 // output the changed index and text
72 80 removed(li, li + lchange.length);
73 81 }
74 82 if (change.op != 'd') {
75 83 // find the starting index of the line
76 li = d.rhs_lines.slice(0, change['rhs-line-from']).join(' ').length;
84 li = d.getLines('lhs').slice(0, change['rhs-line-from']).join(' ').length;
77 85 // get the index of the the span of the change
78 86 lj = change['rhs-line-to'] + 1;
79 87 // get the changed text
80 var rchange = d.rhs_lines.slice(change['rhs-line-from'], lj).join(' ');
88 var rchange = d.getLines('lhs').slice(change['rhs-line-from'], lj).join(' ');
81 89 if (change.op == 'a') rchange += ' ';// include the leading space
82 90 else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word
83 91 // output the changed index and text
84 92 added(li, li + rchange.length);
85 93 }
86 94 }
87 95 }
88 96 });
89 Mgly.diff = function(lhs, rhs, retain_lines, ignore_ws) {
90 this.diff_codes = {};
91 this.max_code = 0;
92 var lhs_lines = lhs.split('\n');
93 var rhs_lines = rhs.split('\n');
94 if (lhs.length == 0) lhs_lines = [];
95 if (rhs.length == 0) rhs_lines = [];
97
98 Mgly.CodeifyText = function(settings) {
99 this._max_code = 0;
100 this._diff_codes = {};
101 this.ctxs = {};
102 this.options = {ignorews: false};
103 jQuery.extend(this, settings);
104 this.lhs = settings.lhs.split('\n');
105 this.rhs = settings.rhs.split('\n');
106 }
96 107
97 var lhs_data = new Object();
98 lhs_data.data = this._diff_codes(lhs_lines, ignore_ws);
99 lhs_data.modified = {};
100 lhs_data.length = Mgly.sizeOf(lhs_data.data);
108 jQuery.extend(Mgly.CodeifyText.prototype, {
109 getCodes: function(side) {
110 if (!this.ctxs.hasOwnProperty(side)) {
111 var ctx = this._diff_ctx(this[side]);
112 this.ctxs[side] = ctx;
113 ctx.codes.length = Object.keys(ctx.codes).length;
114 }
115 return this.ctxs[side].codes;
116 },
117 getLines: function(side) {
118 return this.ctxs[side].lines;
119 },
120 _diff_ctx: function(lines) {
121 var ctx = {i: 0, codes: {}, lines: lines};
122 this._codeify(lines, ctx);
123 return ctx;
124 },
125 _codeify: function(lines, ctx) {
126 var code = this._max_code;
127 for (var i = 0; i < lines.length; ++i) {
128 var line = lines[i];
129 if (this.options.ignorews) {
130 line = line.replace(/\s+/g, '');
131 }
132 var aCode = this._diff_codes[line];
133 if (aCode != undefined) {
134 ctx.codes[i] = aCode;
135 }
136 else {
137 this._max_code++;
138 this._diff_codes[line] = this._max_code;
139 ctx.codes[i] = this._max_code;
140 }
141 }
142 }
143 });
101 144
102 var rhs_data = new Object();
103 rhs_data.data = this._diff_codes(rhs_lines, ignore_ws);
104 rhs_data.modified = {};
105 rhs_data.length = Mgly.sizeOf(rhs_data.data);
106
107 var max = (lhs_data.length + rhs_data.length + 1);
145 Mgly.diff = function(lhs, rhs, options) {
146 var opts = jQuery.extend({ignorews: false}, options);
147 this.codeify = new Mgly.CodeifyText({
148 lhs: lhs,
149 rhs: rhs,
150 options: opts
151 });
152 var lhs_ctx = {
153 codes: this.codeify.getCodes('lhs'),
154 modified: {}
155 };
156 var rhs_ctx = {
157 codes: this.codeify.getCodes('rhs'),
158 modified: {}
159 };
160 var max = (lhs_ctx.codes.length + rhs_ctx.codes.length + 1);
108 161 var vector_d = Array( 2 * max + 2 );
109 162 var vector_u = Array( 2 * max + 2 );
163 this._lcs(lhs_ctx, 0, lhs_ctx.codes.length, rhs_ctx, 0, rhs_ctx.codes.length, vector_u, vector_d);
164 this._optimize(lhs_ctx);
165 this._optimize(rhs_ctx);
166 this.items = this._create_diffs(lhs_ctx, rhs_ctx);
167 };
110 168
111 this._lcs(lhs_data, 0, lhs_data.length, rhs_data, 0, rhs_data.length, vector_u, vector_d);
112 this._optimize(lhs_data);
113 this._optimize(rhs_data);
114 this.items = this._create_diffs(lhs_data, rhs_data);
115 if (retain_lines) {
116 this.lhs_lines = lhs_lines;
117 this.rhs_lines = rhs_lines;
118 }
119 };
120 169 jQuery.extend(Mgly.diff.prototype, {
121 170 changes: function() { return this.items; },
171 getLines: function(side) {
172 return this.codeify.getLines(side);
173 },
122 174 normal_form: function() {
123 175 var nf = '';
124 176 for (var index = 0; index < this.items.length; ++index) {
125 177 var item = this.items[index];
126 178 var lhs_str = '';
127 179 var rhs_str = '';
128 180 var change = 'c';
129 181 if (item.lhs_deleted_count == 0 && item.rhs_inserted_count > 0) change = 'a';
130 182 else if (item.lhs_deleted_count > 0 && item.rhs_inserted_count == 0) change = 'd';
131
183
132 184 if (item.lhs_deleted_count == 1) lhs_str = item.lhs_start + 1;
133 185 else if (item.lhs_deleted_count == 0) lhs_str = item.lhs_start;
134 186 else lhs_str = (item.lhs_start + 1) + ',' + (item.lhs_start + item.lhs_deleted_count);
135
187
136 188 if (item.rhs_inserted_count == 1) rhs_str = item.rhs_start + 1;
137 189 else if (item.rhs_inserted_count == 0) rhs_str = item.rhs_start;
138 190 else rhs_str = (item.rhs_start + 1) + ',' + (item.rhs_start + item.rhs_inserted_count);
139 191 nf += lhs_str + change + rhs_str + '\n';
140 192 if (this.rhs_lines && this.lhs_lines) {
141 193 // if rhs/lhs lines have been retained, output contextual diff
142 194 for (var i = item.lhs_start; i < item.lhs_start + item.lhs_deleted_count; ++i) {
143 195 nf += '< ' + this.lhs_lines[i] + '\n';
144 196 }
145 197 if (item.rhs_inserted_count && item.lhs_deleted_count) nf += '---\n';
146 198 for (var i = item.rhs_start; i < item.rhs_start + item.rhs_inserted_count; ++i) {
147 199 nf += '> ' + this.rhs_lines[i] + '\n';
148 200 }
149 201 }
150 202 }
151 203 return nf;
152 204 },
153 _diff_codes: function(lines, ignore_ws) {
154 var code = this.max_code;
155 var codes = {};
156 for (var i = 0; i < lines.length; ++i) {
157 var line = lines[i];
158 if (ignore_ws) {
159 line = line.replace(/\s+/g, '');
160 }
161 var aCode = this.diff_codes[line];
162 if (aCode != undefined) {
163 codes[i] = aCode;
164 }
165 else {
166 this.max_code++;
167 this.diff_codes[line] = this.max_code;
168 codes[i] = this.max_code;
169 }
170 }
171 return codes;
172 },
173 _lcs: function(lhs, lhs_lower, lhs_upper, rhs, rhs_lower, rhs_upper, vector_u, vector_d) {
174 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs.data[lhs_lower] == rhs.data[rhs_lower]) ) {
205 _lcs: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) {
206 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_lower] == rhs_ctx.codes[rhs_lower]) ) {
175 207 ++lhs_lower;
176 208 ++rhs_lower;
177 209 }
178 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs.data[lhs_upper - 1] == rhs.data[rhs_upper - 1]) ) {
210 while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_upper - 1] == rhs_ctx.codes[rhs_upper - 1]) ) {
179 211 --lhs_upper;
180 212 --rhs_upper;
181 213 }
182 214 if (lhs_lower == lhs_upper) {
183 215 while (rhs_lower < rhs_upper) {
184 rhs.modified[ rhs_lower++ ] = true;
216 rhs_ctx.modified[ rhs_lower++ ] = true;
185 217 }
186 218 }
187 219 else if (rhs_lower == rhs_upper) {
188 220 while (lhs_lower < lhs_upper) {
189 lhs.modified[ lhs_lower++ ] = true;
221 lhs_ctx.modified[ lhs_lower++ ] = true;
190 222 }
191 223 }
192 224 else {
193 var sms = this._sms(lhs, lhs_lower, lhs_upper, rhs, rhs_lower, rhs_upper, vector_u, vector_d);
194 this._lcs(lhs, lhs_lower, sms.x, rhs, rhs_lower, sms.y, vector_u, vector_d);
195 this._lcs(lhs, sms.x, lhs_upper, rhs, sms.y, rhs_upper, vector_u, vector_d);
225 var sms = this._sms(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d);
226 this._lcs(lhs_ctx, lhs_lower, sms.x, rhs_ctx, rhs_lower, sms.y, vector_u, vector_d);
227 this._lcs(lhs_ctx, sms.x, lhs_upper, rhs_ctx, sms.y, rhs_upper, vector_u, vector_d);
196 228 }
197 229 },
198 _sms: function(lhs, lhs_lower, lhs_upper, rhs, rhs_lower, rhs_upper, vector_u, vector_d) {
199 var max = lhs.length + rhs.length + 1;
230 _sms: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) {
231 var max = lhs_ctx.codes.length + rhs_ctx.codes.length + 1;
200 232 var kdown = lhs_lower - rhs_lower;
201 233 var kup = lhs_upper - rhs_upper;
202 234 var delta = (lhs_upper - lhs_lower) - (rhs_upper - rhs_lower);
203 235 var odd = (delta & 1) != 0;
204 236 var offset_down = max - kdown;
205 237 var offset_up = max - kup;
206 238 var maxd = ((lhs_upper - lhs_lower + rhs_upper - rhs_lower) / 2) + 1;
207 239 vector_d[ offset_down + kdown + 1 ] = lhs_lower;
208 240 vector_u[ offset_up + kup - 1 ] = lhs_upper;
209 241 var ret = {x:0,y:0};
210 242 for (var d = 0; d <= maxd; ++d) {
211 243 for (var k = kdown - d; k <= kdown + d; k += 2) {
212 244 var x, y;
213 245 if (k == kdown - d) {
214 246 x = vector_d[ offset_down + k + 1 ];//down
215 247 }
216 248 else {
217 249 x = vector_d[ offset_down + k - 1 ] + 1;//right
218 250 if ((k < (kdown + d)) && (vector_d[ offset_down + k + 1 ] >= x)) {
219 251 x = vector_d[ offset_down + k + 1 ];//down
220 252 }
221 253 }
222 254 y = x - k;
223 255 // find the end of the furthest reaching forward D-path in diagonal k.
224 while ((x < lhs_upper) && (y < rhs_upper) && (lhs.data[x] == rhs.data[y])) {
256 while ((x < lhs_upper) && (y < rhs_upper) && (lhs_ctx.codes[x] == rhs_ctx.codes[y])) {
225 257 x++; y++;
226 258 }
227 259 vector_d[ offset_down + k ] = x;
228 260 // overlap ?
229 261 if (odd && (kup - d < k) && (k < kup + d)) {
230 262 if (vector_u[offset_up + k] <= vector_d[offset_down + k]) {
231 263 ret.x = vector_d[offset_down + k];
232 264 ret.y = vector_d[offset_down + k] - k;
233 265 return (ret);
234 266 }
235 267 }
236 268 }
237 269 // Extend the reverse path.
238 270 for (var k = kup - d; k <= kup + d; k += 2) {
239 271 // find the only or better starting point
240 272 var x, y;
241 273 if (k == kup + d) {
242 274 x = vector_u[offset_up + k - 1]; // up
243 275 } else {
244 276 x = vector_u[offset_up + k + 1] - 1; // left
245 277 if ((k > kup - d) && (vector_u[offset_up + k - 1] < x))
246 278 x = vector_u[offset_up + k - 1]; // up
247 279 }
248 280 y = x - k;
249 while ((x > lhs_lower) && (y > rhs_lower) && (lhs.data[x - 1] == rhs.data[y - 1])) {
281 while ((x > lhs_lower) && (y > rhs_lower) && (lhs_ctx.codes[x - 1] == rhs_ctx.codes[y - 1])) {
250 282 // diagonal
251 283 x--;
252 284 y--;
253 285 }
254 286 vector_u[offset_up + k] = x;
255 287 // overlap ?
256 288 if (!odd && (kdown - d <= k) && (k <= kdown + d)) {
257 289 if (vector_u[offset_up + k] <= vector_d[offset_down + k]) {
258 290 ret.x = vector_d[offset_down + k];
259 291 ret.y = vector_d[offset_down + k] - k;
260 292 return (ret);
261 293 }
262 294 }
263 295 }
264 296 }
265 297 throw "the algorithm should never come here.";
266 298 },
267 _optimize: function(data) {
299 _optimize: function(ctx) {
268 300 var start = 0, end = 0;
269 while (start < data.length) {
270 while ((start < data.length) && (data.modified[start] == undefined || data.modified[start] == false)) {
301 while (start < ctx.length) {
302 while ((start < ctx.length) && (ctx.modified[start] == undefined || ctx.modified[start] == false)) {
271 303 start++;
272 304 }
273 305 end = start;
274 while ((end < data.length) && (data.modified[end] == true)) {
306 while ((end < ctx.length) && (ctx.modified[end] == true)) {
275 307 end++;
276 308 }
277 if ((end < data.length) && (data.data[start] == data.data[end])) {
278 data.modified[start] = false;
279 data.modified[end] = true;
309 if ((end < ctx.length) && (ctx.ctx[start] == ctx.codes[end])) {
310 ctx.modified[start] = false;
311 ctx.modified[end] = true;
280 312 }
281 313 else {
282 314 start = end;
283 315 }
284 316 }
285 317 },
286 _create_diffs: function(lhs_data, rhs_data) {
318 _create_diffs: function(lhs_ctx, rhs_ctx) {
287 319 var items = [];
288 320 var lhs_start = 0, rhs_start = 0;
289 321 var lhs_line = 0, rhs_line = 0;
290 322
291 while (lhs_line < lhs_data.length || rhs_line < rhs_data.length) {
292 if ((lhs_line < lhs_data.length) && (!lhs_data.modified[lhs_line])
293 && (rhs_line < rhs_data.length) && (!rhs_data.modified[rhs_line])) {
323 while (lhs_line < lhs_ctx.codes.length || rhs_line < rhs_ctx.codes.length) {
324 if ((lhs_line < lhs_ctx.codes.length) && (!lhs_ctx.modified[lhs_line])
325 && (rhs_line < rhs_ctx.codes.length) && (!rhs_ctx.modified[rhs_line])) {
294 326 // equal lines
295 327 lhs_line++;
296 328 rhs_line++;
297 329 }
298 330 else {
299 331 // maybe deleted and/or inserted lines
300 332 lhs_start = lhs_line;
301 333 rhs_start = rhs_line;
302 334
303 while (lhs_line < lhs_data.length && (rhs_line >= rhs_data.length || lhs_data.modified[lhs_line]))
335 while (lhs_line < lhs_ctx.codes.length && (rhs_line >= rhs_ctx.codes.length || lhs_ctx.modified[lhs_line]))
304 336 lhs_line++;
305 337
306 while (rhs_line < rhs_data.length && (lhs_line >= lhs_data.length || rhs_data.modified[rhs_line]))
338 while (rhs_line < rhs_ctx.codes.length && (lhs_line >= lhs_ctx.codes.length || rhs_ctx.modified[rhs_line]))
307 339 rhs_line++;
308 340
309 341 if ((lhs_start < lhs_line) || (rhs_start < rhs_line)) {
310 342 // store a new difference-item
311 var aItem = new Object();
312 aItem.lhs_start = lhs_start;
313 aItem.rhs_start = rhs_start;
314 aItem.lhs_deleted_count = lhs_line - lhs_start;
315 aItem.rhs_inserted_count = rhs_line - rhs_start;
316 items.push(aItem);
343 items.push({
344 lhs_start: lhs_start,
345 rhs_start: rhs_start,
346 lhs_deleted_count: lhs_line - lhs_start,
347 rhs_inserted_count: rhs_line - rhs_start
348 });
317 349 }
318 350 }
319 351 }
320 352 return items;
321 353 }
322 354 });
323 355
324 356 Mgly.mergely = function(el, options) {
325 CodeMirror.defineExtension('centerOnCursor', function() {
326 var coords = this.cursorCoords(null, 'local');
327 this.scrollTo(null,
328 (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2));
329 });
330
331 357 if (el) {
332 358 this.init(el, options);
333 359 }
334 360 };
335 361
336 362 jQuery.extend(Mgly.mergely.prototype, {
337 363 name: 'mergely',
338 364 //http://jupiterjs.com/news/writing-the-perfect-jquery-plugin
339 365 init: function(el, options) {
366 this.diffView = new Mgly.CodeMirrorDiffView(el, options);
367 this.bind(el);
368 },
369 bind: function(el) {
370 this.diffView.bind(el);
371 }
372 });
373
374 Mgly.CodeMirrorDiffView = function(el, options) {
375 CodeMirror.defineExtension('centerOnCursor', function() {
376 var coords = this.cursorCoords(null, 'local');
377 this.scrollTo(null,
378 (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2));
379 });
380 this.init(el, options);
381 };
382
383 jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
384 init: function(el, options) {
340 385 this.settings = {
341 386 autoupdate: true,
342 387 autoresize: true,
343 388 rhs_margin: 'right',
344 389 lcs: true,
345 390 sidebar: true,
346 391 viewport: false,
347 392 ignorews: false,
348 393 fadein: 'fast',
349 394 editor_width: '400px',
350 395 editor_height: '400px',
351 396 resize_timeout: 500,
352 397 change_timeout: 150,
353 398 fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f'},
354 399 bgcolor: '#eee',
355 vpcolor: 'rgba(0, 0, 200, 0.2)',
400 vpcolor: 'rgba(0, 0, 200, 0.5)',
356 401 lhs: function(setValue) { },
357 402 rhs: function(setValue) { },
358 403 loaded: function() { },
359 404 //_auto_height: function(h) { return h - 20; },
360 405 _auto_width: function(w) { return w; },
361 406 resize: function(init) {
362 var scrollbar = init ? -15 : 0;
407 var scrollbar = init ? 16 : 0;
363 408 var w = jQuery(el).parent().width() + scrollbar;
364 409 if (this.width == 'auto') {
365 410 w = this._auto_width(w);
366 411 }
367 412 else {
368 413 w = this.width;
369 414 this.editor_width = w;
370 415 }
371 416 if (this.height == 'auto') {
372 417 //h = this._auto_height(h);
373 418 h = jQuery(el).parent().height();
374 419 }
375 420 else {
376 421 h = this.height;
377 422 this.editor_height = h;
378 423 }
379 424 var content_width = w / 2.0 - 2 * 8 - 8;
380 425 var content_height = h;
381 426 var self = jQuery(el);
382 427 self.find('.mergely-column').css({ width: content_width + 'px' });
383 428 self.find('.mergely-column, .mergely-canvas, .mergely-margin, .mergely-column textarea, .CodeMirror-scroll, .cm-s-default').css({ height: content_height + 'px' });
384 429 self.find('.mergely-canvas').css({ height: content_height + 'px' });
385 430 self.find('.mergely-column textarea').css({ width: content_width + 'px' });
386 431 self.css({ width: w, height: h, clear: 'both' });
387 432 if (self.css('display') == 'none') {
388 433 if (this.fadein != false) self.fadeIn(this.fadein);
389 434 else self.show();
390 435 if (this.loaded) this.loaded();
391 436 }
392 437 if (this.resized) this.resized();
393 438 },
394 439 _debug: '', //scroll,draw,calc,diff,markup,change
395 440 resized: function() { }
396 441 };
397 442 var cmsettings = {
398 443 mode: 'text/plain',
399 444 readOnly: false,
400 445 lineWrapping: false,
401 446 lineNumbers: true,
402 447 gutters: ['merge', 'CodeMirror-linenumbers']
403 448 }
404 449 this.lhs_cmsettings = {};
405 450 this.rhs_cmsettings = {};
406
451
407 452 // save this element for faster queries
408 453 this.element = jQuery(el);
409
454
410 455 // save options if there are any
411 456 if (options && options.cmsettings) jQuery.extend(this.lhs_cmsettings, cmsettings, options.cmsettings, options.lhs_cmsettings);
412 457 if (options && options.cmsettings) jQuery.extend(this.rhs_cmsettings, cmsettings, options.cmsettings, options.rhs_cmsettings);
413 458 if (options) jQuery.extend(this.settings, options);
414
459
415 460 // bind if the element is destroyed
416 461 this.element.bind('destroyed', jQuery.proxy(this.teardown, this));
417 462
418 // save this instance in jQuery data
419 jQuery.data(el, this.name, this);
420
421 this._setup(el);
422 },
423 // bind events to this instance's methods
424 bind: function() {
425 var rhstx = jQuery('#' + this.id + '-rhs').get(0);
426 if (!rhstx) {
427 console.error('rhs textarea not defined - Mergely not initialized properly');
428 return;
429 }
430 var lhstx = jQuery('#' + this.id + '-lhs').get(0);
431 if (!rhstx) {
432 console.error('lhs textarea not defined - Mergely not initialized properly');
433 return;
434 }
435 var self = this;
436 this.editor = [];
437
438 this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);
439 this.editor[this.id + '-rhs'] = CodeMirror.fromTextArea(rhstx, this.rhs_cmsettings);
440 this.editor[this.id + '-lhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
441 this.editor[this.id + '-lhs'].on('scroll', function(){ self._scrolling(self.id + '-lhs'); });
442 this.editor[this.id + '-rhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
443 this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); });
444
445 // resize
446 if (this.settings.autoresize) {
447 var sz_timeout1 = null;
448 var sz = function(init) {
449 //self.em_height = null; //recalculate
450 if (self.settings.resize) self.settings.resize(init);
451 self.editor[self.id + '-lhs'].refresh();
452 self.editor[self.id + '-rhs'].refresh();
453 if (self.settings.autoupdate) {
454 self._changing(self.id + '-lhs', self.id + '-rhs');
455 }
456 }
457 jQuery(window).resize(
458 function () {
459 if (sz_timeout1) clearTimeout(sz_timeout1);
460 sz_timeout1 = setTimeout(sz, self.settings.resize_timeout);
461 }
462 );
463 sz(true);
464 }
463 // save this instance in jQuery data, binding this view to the node
464 jQuery.data(el, 'mergely', this);
465 465 },
466 466 unbind: function() {
467 467 if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
468 468 this.editor[this.id + '-lhs'].toTextArea();
469 469 this.editor[this.id + '-rhs'].toTextArea();
470 470 },
471 471 destroy: function() {
472 472 this.element.unbind('destroyed', this.teardown);
473 473 this.teardown();
474 474 },
475 475 teardown: function() {
476 476 this.unbind();
477 477 },
478 478 lhs: function(text) {
479 479 this.editor[this.id + '-lhs'].setValue(text);
480 480 },
481 481 rhs: function(text) {
482 482 this.editor[this.id + '-rhs'].setValue(text);
483 483 },
484 484 update: function() {
485 485 this._changing(this.id + '-lhs', this.id + '-rhs');
486 486 },
487 487 unmarkup: function() {
488 488 this._clear();
489 489 },
490 490 scrollTo: function(side, num) {
491 491 var le = this.editor[this.id + '-lhs'];
492 492 var re = this.editor[this.id + '-rhs'];
493 493 if (side == 'lhs') {
494 494 le.setCursor(num);
495 495 le.centerOnCursor();
496 496 }
497 497 else {
498 498 re.setCursor(num);
499 499 re.centerOnCursor();
500 500 }
501 501 },
502 502 options: function(opts) {
503 503 if (opts) {
504 504 jQuery.extend(this.settings, opts);
505 if (opts.autoresize) this.resize();
506 if (opts.autoupdate) this.update();
507 if (opts.hasOwnProperty('rhs_margin')) {
505 if (this.settings.autoresize) this.resize();
506 if (this.settings.autoupdate) this.update();
507 if (this.settings.hasOwnProperty('rhs_margin')) {
508 508 // dynamically swap the margin
509 if (opts.rhs_margin == 'left') {
509 if (this.settings.rhs_margin == 'left') {
510 510 this.element.find('.mergely-margin:last-child').insertAfter(
511 511 this.element.find('.mergely-canvas'));
512 512 }
513 513 else {
514 514 var target = this.element.find('.mergely-margin').last();
515 515 target.appendTo(target.parent());
516 516 }
517 517 }
518 if (opts.hasOwnProperty('sidebar')) {
518 if (this.settings.hasOwnProperty('sidebar')) {
519 519 // dynamically enable sidebars
520 if (opts.sidebar) {
520 if (this.settings.sidebar) {
521 521 jQuery(this.element).find('.mergely-margin').css({display: 'block'});
522 522 }
523 523 else {
524 524 jQuery(this.element).find('.mergely-margin').css({display: 'none'});
525 525 }
526 526 }
527 527 }
528 528 else {
529 529 return this.settings;
530 530 }
531 531 },
532 532 swap: function() {
533 533 if (this.lhs_cmsettings.readOnly || this.rhs_cmsettings.readOnly) return;
534 534 var le = this.editor[this.id + '-lhs'];
535 535 var re = this.editor[this.id + '-rhs'];
536 536 var tmp = re.getValue();
537 537 re.setValue(le.getValue());
538 538 le.setValue(tmp);
539 539 },
540 540 merge: function(side) {
541 541 var le = this.editor[this.id + '-lhs'];
542 542 var re = this.editor[this.id + '-rhs'];
543 543 if (side == 'lhs' && !this.lhs_cmsettings.readOnly) le.setValue(re.getValue());
544 544 else if (!this.rhs_cmsettings.readOnly) re.setValue(le.getValue());
545 545 },
546 546 get: function(side) {
547 547 var ed = this.editor[this.id + '-' + side];
548 548 var t = ed.getValue();
549 549 if (t == undefined) return '';
550 550 return t;
551 551 },
552 552 clear: function(side) {
553 553 if (side == 'lhs' && this.lhs_cmsettings.readOnly) return;
554 554 if (side == 'rhs' && this.rhs_cmsettings.readOnly) return;
555 555 var ed = this.editor[this.id + '-' + side];
556 556 ed.setValue('');
557 557 },
558 558 cm: function(side) {
559 559 return this.editor[this.id + '-' + side];
560 560 },
561 561 search: function(side, query, direction) {
562 562 var le = this.editor[this.id + '-lhs'];
563 563 var re = this.editor[this.id + '-rhs'];
564 564 var editor;
565 565 if (side == 'lhs') editor = le;
566 566 else editor = re;
567 567 direction = (direction == 'prev') ? 'findPrevious' : 'findNext';
568 568 if ((editor.getSelection().length == 0) || (this.prev_query[side] != query)) {
569 569 this.cursor[this.id] = editor.getSearchCursor(query, { line: 0, ch: 0 }, false);
570 570 this.prev_query[side] = query;
571 571 }
572 572 var cursor = this.cursor[this.id];
573
573
574 574 if (cursor[direction]()) {
575 575 editor.setSelection(cursor.from(), cursor.to());
576 576 }
577 577 else {
578 578 cursor = editor.getSearchCursor(query, { line: 0, ch: 0 }, false);
579 579 }
580 580 },
581 581 resize: function() {
582 582 this.settings.resize();
583 583 this._changing(this.id + '-lhs', this.id + '-rhs');
584 this._set_top_offset(this.id + '-lhs');
584 585 },
585 586 diff: function() {
586 587 var lhs = this.editor[this.id + '-lhs'].getValue();
587 588 var rhs = this.editor[this.id + '-rhs'].getValue();
588 var d = new Mgly.diff(lhs, rhs, retain_lines = true, ignore_ws = this.settings.ignorews);
589 var d = new Mgly.diff(lhs, rhs, this.settings);
589 590 return d.normal_form();
590 591 },
591 _setup: function(el) {
592 bind: function(el) {
592 593 jQuery(this.element).hide();//hide
593 594 this.id = jQuery(el).attr('id');
594 595 var height = this.settings.editor_height;
595 596 var width = this.settings.editor_width;
596 597 this.changed_timeout = null;
597 598 this.chfns = {};
598 599 this.chfns[this.id + '-lhs'] = [];
599 600 this.chfns[this.id + '-rhs'] = [];
600 601 this.prev_query = [];
601 602 this.cursor = [];
602 603 this._skipscroll = {};
603 604 this.change_exp = new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);
604 605 var merge_lhs_button;
605 606 var merge_rhs_button;
606 607 if (jQuery.button != undefined) {
607 608 //jquery ui
608 609 merge_lhs_button = '<button title="Merge left"></button>';
609 610 merge_rhs_button = '<button title="Merge right"></button>';
610 611 }
611 612 else {
612 613 // homebrew
613 614 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;';
614 615 merge_lhs_button = '<div style="' + style + '" title="Merge left">&lt;</div>';
615 616 merge_rhs_button = '<div style="' + style + '" title="Merge right">&gt;</div>';
616 617 }
617 618 this.merge_rhs_button = jQuery(merge_rhs_button);
618 619 this.merge_lhs_button = jQuery(merge_lhs_button);
619
620
620 621 // create the textarea and canvas elements
621 622 jQuery(this.element).append(jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-margin" width="8px" height="' + height + '"></canvas></div>'));
622 623 jQuery(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>'));
623 624 jQuery(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>'));
624 625 var rmargin = jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-rhs-margin" width="8px" height="' + height + '"></canvas></div>');
625 626 if (!this.settings.sidebar) {
626 627 jQuery(this.element).find('.mergely-margin').css({display: 'none'});
627 628 }
628 629 if (this.settings.rhs_margin == 'left') {
629 630 jQuery(this.element).append(rmargin);
630 631 }
631 632 jQuery(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>'));
632 633 if (this.settings.rhs_margin != 'left') {
633 634 jQuery(this.element).append(rmargin);
634 635 }
635 636 //codemirror
636 637 var cmstyle = '#' + this.id + ' .CodeMirror-gutter-text { padding: 5px 0 0 0; }' +
637 638 '#' + this.id + ' .CodeMirror-lines pre, ' + '#' + this.id + ' .CodeMirror-gutter-text pre { line-height: 18px; }' +
638 639 '.CodeMirror-linewidget { overflow: hidden; };';
639 640 if (this.settings.autoresize) {
640 641 cmstyle += this.id + ' .CodeMirror-scroll { height: 100%; overflow: auto; }';
641 642 }
642 643 jQuery('<style type="text/css">' + cmstyle + '</style>').appendTo('head');
643 this.bind();
644
645 //bind
646 var rhstx = jQuery('#' + this.id + '-rhs').get(0);
647 if (!rhstx) {
648 console.error('rhs textarea not defined - Mergely not initialized properly');
649 return;
650 }
651 var lhstx = jQuery('#' + this.id + '-lhs').get(0);
652 if (!rhstx) {
653 console.error('lhs textarea not defined - Mergely not initialized properly');
654 return;
655 }
656 var self = this;
657 this.editor = [];
658 this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);
659 this.editor[this.id + '-rhs'] = CodeMirror.fromTextArea(rhstx, this.rhs_cmsettings);
660 this.editor[this.id + '-lhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
661 this.editor[this.id + '-lhs'].on('scroll', function(){ self._scrolling(self.id + '-lhs'); });
662 this.editor[this.id + '-rhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); });
663 this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); });
664 // resize
665 if (this.settings.autoresize) {
666 var sz_timeout1 = null;
667 var sz = function(init) {
668 //self.em_height = null; //recalculate
669 if (self.settings.resize) self.settings.resize(init);
670 self.editor[self.id + '-lhs'].refresh();
671 self.editor[self.id + '-rhs'].refresh();
672 if (self.settings.autoupdate) {
673 self._changing(self.id + '-lhs', self.id + '-rhs');
674 }
675 }
676 jQuery(window).resize(
677 function () {
678 if (sz_timeout1) clearTimeout(sz_timeout1);
679 sz_timeout1 = setTimeout(sz, self.settings.resize_timeout);
680 }
681 );
682 sz(true);
683 }
684 //bind
685
644 686 if (this.settings.lhs) {
645 687 var setv = this.editor[this.id + '-lhs'].getDoc().setValue;
646 688 this.settings.lhs(setv.bind(this.editor[this.id + '-lhs'].getDoc()));
647 689 }
648 690 if (this.settings.rhs) {
649 691 var setv = this.editor[this.id + '-rhs'].getDoc().setValue;
650 692 this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc()));
651 693 }
652 694 },
653
695
654 696 _scrolling: function(editor_name) {
655 697 if (this._skipscroll[editor_name] === true) {
656 698 // scrolling one side causes the other to event - ignore it
657 699 this._skipscroll[editor_name] = false;
658 700 return;
659 701 }
660 702 var scroller = jQuery(this.editor[editor_name].getScrollerElement());
661 703 if (this.midway == undefined) {
662 704 this.midway = (scroller.height() / 2.0 + scroller.offset().top).toFixed(2);
663 705 }
664 706 // balance-line
665 707 var midline = this.editor[editor_name].coordsChar({left:0, top:this.midway});
666 708 var top_to = scroller.scrollTop();
667 709 var left_to = scroller.scrollLeft();
668
710
669 711 this.trace('scroll', 'side', editor_name);
670 712 this.trace('scroll', 'midway', this.midway);
671 713 this.trace('scroll', 'midline', midline);
672 714 this.trace('scroll', 'top_to', top_to);
673 715 this.trace('scroll', 'left_to', left_to);
674
716
675 717 var editor_name1 = this.id + '-lhs';
676 718 var editor_name2 = this.id + '-rhs';
677
719
678 720 for (var name in this.editor) {
679 721 if (!this.editor.hasOwnProperty(name)) continue;
680 722 if (editor_name == name) continue; //same editor
681 723 var this_side = editor_name.replace(this.id + '-', '');
682 724 var other_side = name.replace(this.id + '-', '');
683 725 var top_adjust = 0;
684
726
685 727 // find the last change that is less than or within the midway point
686 728 // do not move the rhs until the lhs end point is >= the rhs end point.
687 729 var last_change = null;
688 730 var force_scroll = false;
689 731 for (var i = 0; i < this.changes.length; ++i) {
690 732 var change = this.changes[i];
691 733 if ((midline.line >= change[this_side+'-line-from'])) {
692 734 last_change = change;
693 735 if (midline.line >= last_change[this_side+'-line-to']) {
694 736 if (!change.hasOwnProperty(this_side+'-y-start') ||
695 737 !change.hasOwnProperty(this_side+'-y-end') ||
696 738 !change.hasOwnProperty(other_side+'-y-start') ||
697 739 !change.hasOwnProperty(other_side+'-y-end')){
698 740 // change outside of viewport
699 741 force_scroll = true;
700 742 }
701 743 else {
702 top_adjust +=
703 (change[this_side+'-y-end'] - change[this_side+'-y-start']) -
744 top_adjust +=
745 (change[this_side+'-y-end'] - change[this_side+'-y-start']) -
704 746 (change[other_side+'-y-end'] - change[other_side+'-y-start']);
705 747 }
706 748 }
707 749 }
708 750 }
709
751
710 752 var vp = this.editor[name].getViewport();
711 753 var scroll = true;
712 754 if (last_change) {
713 755 this.trace('scroll', 'last change before midline', last_change);
714 756 if (midline.line >= vp.from && midline <= vp.to) {
715 757 scroll = false;
716 758 }
717 759 }
718 760 this.trace('scroll', 'scroll', scroll);
719 761 if (scroll || force_scroll) {
720 762 // scroll the other side
721 763 this.trace('scroll', 'scrolling other side', top_to - top_adjust);
722 764 var scroller = jQuery(this.editor[name].getScrollerElement());
723 765 this._skipscroll[name] = true;//disable next event
724 766 scroller.scrollTop(top_to - top_adjust).scrollLeft(left_to);
725 767 }
726 768 else this.trace('scroll', 'not scrolling other side');
727
769
728 770 if (this.settings.autoupdate) {
729 771 var timer = new Mgly.Timer();
730 772 this._calculate_offsets(editor_name1, editor_name2, this.changes);
731 773 this.trace('change', 'offsets time', timer.stop());
732 774 this._markup_changes(editor_name1, editor_name2, this.changes);
733 775 this.trace('change', 'markup time', timer.stop());
734 776 this._draw_diff(editor_name1, editor_name2, this.changes);
735 777 this.trace('change', 'draw time', timer.stop());
736 778 }
737 779 this.trace('scroll', 'scrolled');
738 780 }
739 781 },
740 782 _changing: function(editor_name1, editor_name2) {
741 783 this.trace('change', 'changing-timeout', this.changed_timeout);
742 784 var self = this;
743 785 if (this.changed_timeout != null) clearTimeout(this.changed_timeout);
744 786 this.changed_timeout = setTimeout(function(){
745 787 var timer = new Mgly.Timer();
746 788 self._changed(editor_name1, editor_name2);
747 789 self.trace('change', 'total time', timer.stop());
748 790 }, this.settings.change_timeout);
749 791 },
750 792 _changed: function(editor_name1, editor_name2) {
751 793 this._clear();
752 794 this._diff(editor_name1, editor_name2);
753 795 },
754 796 _clear: function() {
755 797 var self = this;
756 798 for (var name in this.editor) {
757 799 if (!this.editor.hasOwnProperty(name)) continue;
758 800 var editor = this.editor[name];
759 801 var fns = self.chfns[name];
760 802 // clear editor changes
761 803 editor.operation(function() {
762 804 var timer = new Mgly.Timer();
763 805 for (var i = 0, l = editor.lineCount(); i < l; ++i) {
764 806 editor.removeLineClass(i, 'background');
765 807 }
766 808 for (var i = 0; i < fns.length; ++i) {
767 809 //var edid = editor.getDoc().id;
768 810 var change = fns[i];
769 811 //if (change.doc.id != edid) continue;
770 812 if (change.lines.length) {
771 813 self.trace('change', 'clear text', change.lines[0].text);
772 814 }
773 815 change.clear();
774 816 }
775 817 editor.clearGutter('merge');
776 818 self.trace('change', 'clear time', timer.stop());
777 819 });
778 820 }
779 821 self.chfns[name] = [];
780
822
781 823 var ex = this._draw_info(this.id + '-lhs', this.id + '-rhs');
782 824 var ctx_lhs = ex.clhs.get(0).getContext('2d');
783 825 var ctx_rhs = ex.crhs.get(0).getContext('2d');
784 826 var ctx = ex.dcanvas.getContext('2d');
785
827
786 828 ctx_lhs.beginPath();
787 829 ctx_lhs.fillStyle = this.settings.bgcolor;
788 830 ctx_lhs.strokeStyle = '#888';
789 831 ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height);
790 832 ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
791 833
792 834 ctx_rhs.beginPath();
793 835 ctx_rhs.fillStyle = this.settings.bgcolor;
794 836 ctx_rhs.strokeStyle = '#888';
795 837 ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height);
796 838 ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
797
839
798 840 ctx.beginPath();
799 841 ctx.fillStyle = '#fff';
800 842 ctx.fillRect(0, 0, this.draw_mid_width, ex.visible_page_height);
801 843 },
802 844 _diff: function(editor_name1, editor_name2) {
803 845 var lhs = this.editor[editor_name1].getValue();
804 846 var rhs = this.editor[editor_name2].getValue();
805 847 var timer = new Mgly.Timer();
806 var d = new Mgly.diff(lhs, rhs, false, this.settings.ignorews);
848 var d = new Mgly.diff(lhs, rhs, this.settings);
807 849 this.trace('change', 'diff time', timer.stop());
808 850 this.changes = Mgly.DiffParser(d.normal_form());
809 851 this.trace('change', 'parse time', timer.stop());
810 852 this._calculate_offsets(editor_name1, editor_name2, this.changes);
811 853 this.trace('change', 'offsets time', timer.stop());
812 854 this._markup_changes(editor_name1, editor_name2, this.changes);
813 855 this.trace('change', 'markup time', timer.stop());
814 856 this._draw_diff(editor_name1, editor_name2, this.changes);
815 857 this.trace('change', 'draw time', timer.stop());
816 858 },
817 859 _parse_diff: function (editor_name1, editor_name2, diff) {
818 860 this.trace('diff', 'diff results:\n', diff);
819 861 var changes = [];
820 862 var change_id = 0;
821 863 // parse diff
822 864 var diff_lines = diff.split(/\n/);
823 865 for (var i = 0; i < diff_lines.length; ++i) {
824 866 if (diff_lines[i].length == 0) continue;
825 867 var change = {};
826 868 var test = this.change_exp.exec(diff_lines[i]);
827 869 if (test == null) continue;
828 870 // lines are zero-based
829 871 var fr = test[1].split(',');
830 872 change['lhs-line-from'] = fr[0] - 1;
831 873 if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1;
832 874 else change['lhs-line-to'] = fr[1] - 1;
833 875 var to = test[3].split(',');
834 876 change['rhs-line-from'] = to[0] - 1;
835 877 if (to.length == 1) change['rhs-line-to'] = to[0] - 1;
836 878 else change['rhs-line-to'] = to[1] - 1;
837 879 // TODO: optimize for changes that are adds/removes
838 880 if (change['lhs-line-from'] < 0) change['lhs-line-from'] = 0;
839 881 if (change['lhs-line-to'] < 0) change['lhs-line-to'] = 0;
840 882 if (change['rhs-line-from'] < 0) change['rhs-line-from'] = 0;
841 883 if (change['rhs-line-to'] < 0) change['rhs-line-to'] = 0;
842 884 change['op'] = test[2];
843 885 changes[change_id++] = change;
844 886 this.trace('diff', 'change', change);
845 887 }
846 888 return changes;
847 889 },
848 890 _get_viewport: function(editor_name1, editor_name2) {
849 891 var lhsvp = this.editor[editor_name1].getViewport();
850 892 var rhsvp = this.editor[editor_name2].getViewport();
851 893 return {from: Math.min(lhsvp.from, rhsvp.from), to: Math.max(lhsvp.to, rhsvp.to)};
852 894 },
853 895 _is_change_in_view: function(vp, change) {
854 896 if (!this.settings.viewport) return true;
855 897 if ((change['lhs-line-from'] < vp.from && change['lhs-line-to'] < vp.to) ||
856 898 (change['lhs-line-from'] > vp.from && change['lhs-line-to'] > vp.to) ||
857 899 (change['rhs-line-from'] < vp.from && change['rhs-line-to'] < vp.to) ||
858 900 (change['rhs-line-from'] > vp.from && change['rhs-line-to'] > vp.to)) {
859 901 // if the change is outside the viewport, skip
860 902 return false;
861 903 }
862 904 return true;
863 905 },
906 _set_top_offset: function (editor_name1) {
907 // save the current scroll position of the editor
908 var saveY = this.editor[editor_name1].getScrollInfo().top;
909 // temporarily scroll to top
910 this.editor[editor_name1].scrollTo(null, 0);
911
912 // this is the distance from the top of the screen to the top of the
913 // content of the first codemirror editor
914 var topnode = jQuery('#' + this.id + ' .CodeMirror-measure').first();
915 var top_offset = topnode.offset().top - 4;
916 if(!top_offset) return false;
917
918 // restore editor's scroll position
919 this.editor[editor_name1].scrollTo(null, saveY);
920
921 this.draw_top_offset = 0.5 - top_offset;
922 return true;
923 },
864 924 _calculate_offsets: function (editor_name1, editor_name2, changes) {
865 925 if (this.em_height == null) {
866 // this is the distance from the top of the screen
867 var topnode = jQuery('#' + this.id + ' .CodeMirror-measure').first();
868 var top_offset = topnode.offset().top - 4;
869 if (!top_offset) return;//try again
870 this.draw_top_offset = 0.5 - top_offset;
926 if(!this._set_top_offset(editor_name1)) return; //try again
871 927 this.em_height = this.editor[editor_name1].defaultTextHeight();
872 928 if (!this.em_height) {
873 929 console.warn('Failed to calculate offsets, using 18 by default');
874 930 this.em_height = 18;
875 931 }
876 932 this.draw_lhs_min = 0.5;
877 933 var c = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas');
878 934 if (!c.length) {
879 935 console.error('failed to find canvas', '#' + editor_name1 + '-' + editor_name2 + '-canvas');
880 936 }
881 937 if (!c.width()) {
882 938 console.error('canvas width is 0');
883 939 return;
884 940 }
885 941 this.draw_mid_width = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas').width();
886 942 this.draw_rhs_max = this.draw_mid_width - 0.5; //24.5;
887 943 this.draw_lhs_width = 5;
888 944 this.draw_rhs_width = 5;
889 this.trace('calc', 'change offsets calculated', {top_offset: top_offset, lhs_min: this.draw_lhs_min, rhs_max: this.draw_rhs_max, lhs_width: this.draw_lhs_width, rhs_width: this.draw_rhs_width});
945 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});
890 946 }
891 947 var lhschc = this.editor[editor_name1].charCoords({line: 0});
892 948 var rhschc = this.editor[editor_name2].charCoords({line: 0});
893 949 var vp = this._get_viewport(editor_name1, editor_name2);
894
950
895 951 for (var i = 0; i < changes.length; ++i) {
896 952 var change = changes[i];
897
953
898 954 if (!this.settings.sidebar && !this._is_change_in_view(vp, change)) {
899 955 // if the change is outside the viewport, skip
900 956 delete change['lhs-y-start'];
901 957 delete change['lhs-y-end'];
902 958 delete change['rhs-y-start'];
903 959 delete change['rhs-y-end'];
904 960 continue;
905 961 }
906 962 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
907 963 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
908 964 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
909 965 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
910
966
911 967 var ls, le, rs, re;
912 968 if (this.editor[editor_name1].getOption('lineWrapping') || this.editor[editor_name1].getOption('lineWrapping')) {
913 969 // If using line-wrapping, we must get the height of the line
914 970 var tls = this.editor[editor_name1].cursorCoords({line: llf, ch: 0}, 'page');
915 971 var lhssh = this.editor[editor_name1].getLineHandle(llf);
916 972 ls = { top: tls.top, bottom: tls.top + lhssh.height };
917 973
918 974 var tle = this.editor[editor_name1].cursorCoords({line: llt, ch: 0}, 'page');
919 975 var lhseh = this.editor[editor_name1].getLineHandle(llt);
920 976 le = { top: tle.top, bottom: tle.top + lhseh.height };
921
977
922 978 var tls = this.editor[editor_name2].cursorCoords({line: rlf, ch: 0}, 'page');
923 979 var rhssh = this.editor[editor_name2].getLineHandle(rlf);
924 980 rs = { top: tls.top, bottom: tls.top + rhssh.height };
925 981
926 982 var tle = this.editor[editor_name2].cursorCoords({line: rlt, ch: 0}, 'page');
927 983 var rhseh = this.editor[editor_name2].getLineHandle(rlt);
928 984 re = { top: tle.top, bottom: tle.top + rhseh.height };
929 985 }
930 986 else {
931 987 // If not using line-wrapping, we can calculate the line position
932 ls = {
933 top: lhschc.top + llf * this.em_height,
988 ls = {
989 top: lhschc.top + llf * this.em_height,
934 990 bottom: lhschc.bottom + llf * this.em_height + 2
935 991 };
936 992 le = {
937 top: lhschc.top + llt * this.em_height,
993 top: lhschc.top + llt * this.em_height,
938 994 bottom: lhschc.bottom + llt * this.em_height + 2
939 995 };
940 996 rs = {
941 top: rhschc.top + rlf * this.em_height,
997 top: rhschc.top + rlf * this.em_height,
942 998 bottom: rhschc.bottom + rlf * this.em_height + 2
943 999 };
944 1000 re = {
945 top: rhschc.top + rlt * this.em_height,
1001 top: rhschc.top + rlt * this.em_height,
946 1002 bottom: rhschc.bottom + rlt * this.em_height + 2
947 1003 };
948 1004 }
949
1005
950 1006 if (change['op'] == 'a') {
951 1007 // adds (right), normally start from the end of the lhs,
952 1008 // except for the case when the start of the rhs is 0
953 1009 if (rlf > 0) {
954 1010 ls.top = ls.bottom;
955 1011 ls.bottom += this.em_height;
956 1012 le = ls;
957 1013 }
958 1014 }
959 1015 else if (change['op'] == 'd') {
960 1016 // deletes (left) normally finish from the end of the rhs,
961 1017 // except for the case when the start of the lhs is 0
962 1018 if (llf > 0) {
963 1019 rs.top = rs.bottom;
964 1020 rs.bottom += this.em_height;
965 1021 re = rs;
966 1022 }
967 1023 }
968 1024 change['lhs-y-start'] = this.draw_top_offset + ls.top;
969 1025 if (change['op'] == 'c' || change['op'] == 'd') {
970 1026 change['lhs-y-end'] = this.draw_top_offset + le.bottom;
971 1027 }
972 1028 else {
973 1029 change['lhs-y-end'] = this.draw_top_offset + le.top;
974 1030 }
975 1031 change['rhs-y-start'] = this.draw_top_offset + rs.top;
976 1032 if (change['op'] == 'c' || change['op'] == 'a') {
977 1033 change['rhs-y-end'] = this.draw_top_offset + re.bottom;
978 1034 }
979 1035 else {
980 1036 change['rhs-y-end'] = this.draw_top_offset + re.top;
981 1037 }
982 1038 this.trace('calc', 'change calculated', i, change);
983 1039 }
984 1040 return changes;
985 1041 },
986 1042 _markup_changes: function (editor_name1, editor_name2, changes) {
987 1043 jQuery('.merge-button').remove(); // clear
988
1044
989 1045 var self = this;
990 1046 var led = this.editor[editor_name1];
991 1047 var red = this.editor[editor_name2];
992 1048
993 1049 var timer = new Mgly.Timer();
994 1050 led.operation(function() {
995 1051 for (var i = 0; i < changes.length; ++i) {
996 1052 var change = changes[i];
997 1053 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
998 1054 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
999 1055 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1000 1056 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1001
1057
1002 1058 var clazz = ['mergely', 'lhs', change['op'], 'cid-' + i];
1003 1059 led.addLineClass(llf, 'background', 'start');
1004 1060 led.addLineClass(llt, 'background', 'end');
1005
1061
1006 1062 if (llf == 0 && llt == 0 && rlf == 0) {
1007 1063 led.addLineClass(llf, 'background', clazz.join(' '));
1008 1064 led.addLineClass(llf, 'background', 'first');
1009 1065 }
1010 1066 else {
1011 1067 // apply change for each line in-between the changed lines
1012 1068 for (var j = llf; j <= llt; ++j) {
1013 1069 led.addLineClass(j, 'background', clazz.join(' '));
1014 1070 led.addLineClass(j, 'background', clazz.join(' '));
1015 1071 }
1016 1072 }
1017
1073
1018 1074 if (!red.getOption('readOnly')) {
1019 1075 // add widgets to lhs, if rhs is not read only
1020 1076 var rhs_button = self.merge_rhs_button.clone();
1021 1077 if (rhs_button.button) {
1022 1078 //jquery-ui support
1023 1079 rhs_button.button({icons: {primary: 'ui-icon-triangle-1-e'}, text: false});
1024 1080 }
1025 1081 rhs_button.addClass('merge-button');
1026 1082 rhs_button.attr('id', 'merge-rhs-' + i);
1027 1083 led.setGutterMarker(llf, 'merge', rhs_button.get(0));
1028 1084 }
1029 1085 }
1030 1086 });
1031 1087
1032 1088 var vp = this._get_viewport(editor_name1, editor_name2);
1033
1089
1034 1090 this.trace('change', 'markup lhs-editor time', timer.stop());
1035 1091 red.operation(function() {
1036 1092 for (var i = 0; i < changes.length; ++i) {
1037 1093 var change = changes[i];
1038 1094 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1039 1095 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1040 1096 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1041 1097 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1042
1098
1043 1099 if (!self._is_change_in_view(vp, change)) {
1044 1100 // if the change is outside the viewport, skip
1045 1101 continue;
1046 1102 }
1047
1103
1048 1104 var clazz = ['mergely', 'rhs', change['op'], 'cid-' + i];
1049 1105 red.addLineClass(rlf, 'background', 'start');
1050 1106 red.addLineClass(rlt, 'background', 'end');
1051
1107
1052 1108 if (rlf == 0 && rlt == 0 && llf == 0) {
1053 1109 red.addLineClass(rlf, 'background', clazz.join(' '));
1054 1110 red.addLineClass(rlf, 'background', 'first');
1055 1111 }
1056 1112 else {
1057 1113 // apply change for each line in-between the changed lines
1058 1114 for (var j = rlf; j <= rlt; ++j) {
1059 1115 red.addLineClass(j, 'background', clazz.join(' '));
1060 1116 red.addLineClass(j, 'background', clazz.join(' '));
1061 1117 }
1062 1118 }
1063 1119
1064 1120 if (!led.getOption('readOnly')) {
1065 1121 // add widgets to rhs, if lhs is not read only
1066 1122 var lhs_button = self.merge_lhs_button.clone();
1067 1123 if (lhs_button.button) {
1068 1124 //jquery-ui support
1069 1125 lhs_button.button({icons: {primary: 'ui-icon-triangle-1-w'}, text: false});
1070 1126 }
1071 1127 lhs_button.addClass('merge-button');
1072 1128 lhs_button.attr('id', 'merge-lhs-' + i);
1073 1129 red.setGutterMarker(rlf, 'merge', lhs_button.get(0));
1074 1130 }
1075 1131 }
1076 1132 });
1077 1133 this.trace('change', 'markup rhs-editor time', timer.stop());
1078
1134
1079 1135 // mark text deleted, LCS changes
1080 1136 var marktext = [];
1081 1137 for (var i = 0; this.settings.lcs && i < changes.length; ++i) {
1082 1138 var change = changes[i];
1083 1139 var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0;
1084 1140 var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0;
1085 1141 var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0;
1086 1142 var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0;
1087
1143
1088 1144 if (!this._is_change_in_view(vp, change)) {
1089 1145 // if the change is outside the viewport, skip
1090 1146 continue;
1091 1147 }
1092 1148 if (change['op'] == 'd') {
1093 1149 // apply delete to cross-out (left-hand side only)
1094 1150 var from = llf;
1095 1151 var to = llt;
1096 1152 var to_ln = led.lineInfo(to);
1097 1153 if (to_ln) {
1098 1154 marktext.push([led, {line:from, ch:0}, {line:to, ch:to_ln.text.length}, {className: 'mergely ch d lhs'}]);
1099 1155 }
1100 1156 }
1101 1157 else if (change['op'] == 'c') {
1102 1158 // apply LCS changes to each line
1103 for (var j = llf, k = rlf, p = 0;
1159 for (var j = llf, k = rlf, p = 0;
1104 1160 ((j >= 0) && (j <= llt)) || ((k >= 0) && (k <= rlt));
1105 1161 ++j, ++k) {
1106 1162 if (k + p > rlt) {
1107 1163 // lhs continues past rhs, mark lhs as deleted
1108 1164 var lhs_line = led.getLine( j );
1109 1165 marktext.push([led, {line:j, ch:0}, {line:j, ch:lhs_line.length}, {className: 'mergely ch d lhs'}]);
1110 1166 continue;
1111 1167 }
1112 1168 if (j + p > llt) {
1113 1169 // rhs continues past lhs, mark rhs as added
1114 1170 var rhs_line = red.getLine( k );
1115 1171 marktext.push([red, {line:k, ch:0}, {line:k, ch:rhs_line.length}, {className: 'mergely ch a rhs'}]);
1116 1172 continue;
1117 1173 }
1118 1174 var lhs_line = led.getLine( j );
1119 1175 var rhs_line = red.getLine( k );
1120 1176 var lhs_start = { line: -1, ch: -1 };
1121 1177 var lhs_stop = { line: -1, ch: -1 };
1122 1178 var rhs_start = { line: -1, ch: -1 };
1123 1179 var rhs_stop = { line: -1, ch: -1 };
1124
1180
1125 1181 var lcs = new Mgly.LCS(lhs_line, rhs_line);
1126 1182 lcs.diff(
1127 1183 function (from, to) {//added
1128 1184 marktext.push([red, {line:k, ch:from}, {line:k, ch:to}, {className: 'mergely ch a rhs'}]);
1129 1185 },
1130 1186 removed = function (from, to) {//removed
1131 1187 marktext.push([led, {line:j, ch:from}, {line:j, ch:to}, {className: 'mergely ch d lhs'}]);
1132 1188 }
1133 1189 );
1134 1190 }
1135 1191 }
1136 1192 }
1137 1193 this.trace('change', 'LCS marktext time', timer.stop());
1138
1194
1139 1195 // mark changes outside closure
1140 1196 led.operation(function() {
1141 1197 // apply lhs markup
1142 1198 for (var i = 0; i < marktext.length; ++i) {
1143 1199 var m = marktext[i];
1144 1200 if (m[0].doc.id != led.getDoc().id) continue;
1145 1201 self.chfns[self.id + '-lhs'].push(m[0].markText(m[1], m[2], m[3]));
1146 1202 }
1147 1203 });
1148 1204 red.operation(function() {
1149 1205 // apply lhs markup
1150 1206 for (var i = 0; i < marktext.length; ++i) {
1151 1207 var m = marktext[i];
1152 1208 if (m[0].doc.id != red.getDoc().id) continue;
1153 1209 self.chfns[self.id + '-rhs'].push(m[0].markText(m[1], m[2], m[3]));
1154 1210 }
1155 1211 });
1156 1212 this.trace('change', 'LCS markup time', timer.stop());
1157
1213
1158 1214 // merge buttons
1159 1215 var ed = {lhs:led, rhs:red};
1160 1216 jQuery('.merge-button').on('click', function(ev){
1161 1217 // side of mouseenter
1162 1218 var side = 'rhs';
1163 1219 var oside = 'lhs';
1164 1220 var parent = jQuery(this).parents('#' + self.id + '-editor-lhs');
1165 1221 if (parent.length) {
1166 1222 side = 'lhs';
1167 1223 oside = 'rhs';
1168 1224 }
1169 1225 var pos = ed[side].coordsChar({left:ev.pageX, top:ev.pageY});
1170 1226
1171 1227 // get the change id
1172 1228 var cid = null;
1173 1229 var info = ed[side].lineInfo(pos.line);
1174 1230 jQuery.each(info.bgClass.split(' '), function(i, clazz) {
1175 1231 if (clazz.indexOf('cid-') == 0) {
1176 1232 cid = parseInt(clazz.split('-')[1], 10);
1177 1233 return false;
1178 1234 }
1179 1235 });
1180 1236 var change = self.changes[cid];
1181 1237
1182 1238 var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)};
1183
1239
1184 1240 var text = ed[side].getRange(
1185 1241 CodeMirror.Pos(change[side + '-line-from'], 0),
1186 1242 CodeMirror.Pos(change[side + '-line-to'] + 1, 0));
1187
1243
1188 1244 if (change['op'] == 'c') {
1189 1245 ed[oside].replaceRange(text,
1190 1246 CodeMirror.Pos(change[oside + '-line-from'], 0),
1191 1247 CodeMirror.Pos(change[oside + '-line-to'] + 1, 0));
1192 1248 }
1193 1249 else if (side == 'rhs') {
1194 1250 if (change['op'] == 'a') {
1195 1251 ed[oside].replaceRange(text,
1196 1252 CodeMirror.Pos(change[oside + '-line-from'] + 1, 0),
1197 1253 CodeMirror.Pos(change[oside + '-line-to'] + 1, 0));
1198 1254 }
1199 1255 else {// 'd'
1200 1256 var from = parseInt(change[oside + '-line-from']);
1201 1257 var to = parseInt(change[oside + '-line-to']);
1202 1258 for (var i = to; i >= from; --i) {
1203 1259 ed[oside].removeLine(i);
1204 1260 }
1205 1261 }
1206 1262 }
1207 1263 else if (side == 'lhs') {
1208 1264 if (change['op'] == 'a') {
1209 1265 var from = parseInt(change[oside + '-line-from']);
1210 1266 var to = parseInt(change[oside + '-line-to']);
1211 1267 for (var i = to; i >= from; --i) {
1212 1268 ed[oside].removeLine(i);
1213 1269 }
1214 1270 }
1215 1271 else {// 'd'
1216 1272 ed[oside].replaceRange( text,
1217 1273 CodeMirror.Pos(change[oside + '-line-from'] + 1, 0));
1218 1274 }
1219 1275 }
1220 1276 //reset
1221 1277 ed['lhs'].setValue(ed['lhs'].getValue());
1222 1278 ed['rhs'].setValue(ed['rhs'].getValue());
1223 1279 return false;
1224 1280 });
1225 1281 this.trace('change', 'markup buttons time', timer.stop());
1226 1282 },
1227 1283 _draw_info: function(editor_name1, editor_name2) {
1228 1284 var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height();
1229 1285 var gutter_height = jQuery(this.editor[editor_name1].getScrollerElement()).children(':first-child').height();
1230 1286 var dcanvas = document.getElementById(editor_name1 + '-' + editor_name2 + '-canvas');
1231 1287 if (dcanvas == undefined) throw 'Failed to find: ' + editor_name1 + '-' + editor_name2 + '-canvas';
1232 1288 var clhs = jQuery('#' + this.id + '-lhs-margin');
1233 1289 var crhs = jQuery('#' + this.id + '-rhs-margin');
1234 1290 return {
1235 1291 visible_page_height: visible_page_height,
1236 1292 gutter_height: gutter_height,
1237 1293 visible_page_ratio: (visible_page_height / gutter_height),
1238 1294 margin_ratio: (visible_page_height / gutter_height),
1239 1295 lhs_scroller: jQuery(this.editor[editor_name1].getScrollerElement()),
1240 1296 rhs_scroller: jQuery(this.editor[editor_name2].getScrollerElement()),
1241 1297 lhs_lines: this.editor[editor_name1].lineCount(),
1242 1298 rhs_lines: this.editor[editor_name2].lineCount(),
1243 1299 dcanvas: dcanvas,
1244 1300 clhs: clhs,
1245 1301 crhs: crhs,
1246 1302 lhs_xyoffset: jQuery(clhs).offset(),
1247 1303 rhs_xyoffset: jQuery(crhs).offset()
1248 1304 };
1249 1305 },
1250 1306 _draw_diff: function(editor_name1, editor_name2, changes) {
1251 1307 var ex = this._draw_info(editor_name1, editor_name2);
1252 1308 var mcanvas_lhs = ex.clhs.get(0);
1253 1309 var mcanvas_rhs = ex.crhs.get(0);
1254 1310 var ctx = ex.dcanvas.getContext('2d');
1255 1311 var ctx_lhs = mcanvas_lhs.getContext('2d');
1256 1312 var ctx_rhs = mcanvas_rhs.getContext('2d');
1257 1313
1258 1314 this.trace('draw', 'visible_page_height', ex.visible_page_height);
1259 1315 this.trace('draw', 'gutter_height', ex.gutter_height);
1260 1316 this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio);
1261 1317 this.trace('draw', 'lhs-scroller-top', ex.lhs_scroller.scrollTop());
1262 1318 this.trace('draw', 'rhs-scroller-top', ex.rhs_scroller.scrollTop());
1263
1319
1264 1320 jQuery.each(jQuery.find('#' + this.id + ' canvas'), function () {
1265 1321 jQuery(this).get(0).height = ex.visible_page_height;
1266 1322 });
1267
1323
1268 1324 ex.clhs.unbind('click');
1269 1325 ex.crhs.unbind('click');
1270
1326
1271 1327 ctx_lhs.beginPath();
1272 1328 ctx_lhs.fillStyle = this.settings.bgcolor;
1273 1329 ctx_lhs.strokeStyle = '#888';
1274 1330 ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height);
1275 1331 ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
1276 1332
1277 1333 ctx_rhs.beginPath();
1278 1334 ctx_rhs.fillStyle = this.settings.bgcolor;
1279 1335 ctx_rhs.strokeStyle = '#888';
1280 1336 ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height);
1281 1337 ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height);
1282 1338
1283 1339 var vp = this._get_viewport(editor_name1, editor_name2);
1284 1340 for (var i = 0; i < changes.length; ++i) {
1285 1341 var change = changes[i];
1286 1342
1287 1343 this.trace('draw', change);
1288 1344 // margin indicators
1289 1345 var lhs_y_start = ((change['lhs-y-start'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio);
1290 1346 var lhs_y_end = ((change['lhs-y-end'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1;
1291 1347 var rhs_y_start = ((change['rhs-y-start'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio);
1292 1348 var rhs_y_end = ((change['rhs-y-end'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1;
1293 1349 this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
1294 1350
1295 1351 ctx_lhs.beginPath();
1296 1352 ctx_lhs.fillStyle = this.settings.fgcolor[change['op']];
1297 1353 ctx_lhs.strokeStyle = '#000';
1298 ctx_lhs.lineWidth = 1.0;
1354 ctx_lhs.lineWidth = 0.5;
1299 1355 ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
1300 1356 ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
1301 1357
1302 1358 ctx_rhs.beginPath();
1303 1359 ctx_rhs.fillStyle = this.settings.fgcolor[change['op']];
1304 1360 ctx_rhs.strokeStyle = '#000';
1305 ctx_rhs.lineWidth = 1.0;
1361 ctx_rhs.lineWidth = 0.5;
1306 1362 ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
1307 1363 ctx_rhs.strokeRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
1308
1364
1309 1365 if (!this._is_change_in_view(vp, change)) {
1310 1366 continue;
1311 1367 }
1312
1368
1313 1369 lhs_y_start = change['lhs-y-start'];
1314 1370 lhs_y_end = change['lhs-y-end'];
1315 1371 rhs_y_start = change['rhs-y-start'];
1316 1372 rhs_y_end = change['rhs-y-end'];
1317
1373
1318 1374 var radius = 3;
1319
1375
1320 1376 // draw left box
1321 1377 ctx.beginPath();
1322 1378 ctx.strokeStyle = this.settings.fgcolor[change['op']];
1323 1379 ctx.lineWidth = 1;
1324
1380
1325 1381 var rectWidth = this.draw_lhs_width;
1326 1382 var rectHeight = lhs_y_end - lhs_y_start - 1;
1327 1383 var rectX = this.draw_lhs_min;
1328 1384 var rectY = lhs_y_start;
1329 1385 // top and top top-right corner
1330
1386
1331 1387 // draw left box
1332 1388 ctx.moveTo(rectX, rectY);
1333 1389 if (navigator.appName == 'Microsoft Internet Explorer') {
1334 1390 // IE arcs look awful
1335 1391 ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_start);
1336 1392 ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_end + 1);
1337 1393 ctx.lineTo(this.draw_lhs_min, lhs_y_end + 1);
1338 1394 }
1339 1395 else {
1340 1396 if (rectHeight <= 0) {
1341 1397 ctx.lineTo(rectX + rectWidth, rectY);
1342 1398 }
1343 1399 else {
1344 1400 ctx.arcTo(rectX + rectWidth, rectY, rectX + rectWidth, rectY + radius, radius);
1345 1401 ctx.arcTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth - radius, rectY + rectHeight, radius);
1346 1402 }
1347 1403 // bottom line
1348 1404 ctx.lineTo(rectX, rectY + rectHeight);
1349 1405 }
1350 1406 ctx.stroke();
1351
1407
1352 1408 rectWidth = this.draw_rhs_width;
1353 1409 rectHeight = rhs_y_end - rhs_y_start - 1;
1354 1410 rectX = this.draw_rhs_max;
1355 1411 rectY = rhs_y_start;
1356 1412
1357 1413 // draw right box
1358 1414 ctx.moveTo(rectX, rectY);
1359 1415 if (navigator.appName == 'Microsoft Internet Explorer') {
1360 1416 ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_start);
1361 1417 ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_end + 1);
1362 1418 ctx.lineTo(this.draw_rhs_max, rhs_y_end + 1);
1363 1419 }
1364 1420 else {
1365 1421 if (rectHeight <= 0) {
1366 1422 ctx.lineTo(rectX - rectWidth, rectY);
1367 1423 }
1368 1424 else {
1369 1425 ctx.arcTo(rectX - rectWidth, rectY, rectX - rectWidth, rectY + radius, radius);
1370 1426 ctx.arcTo(rectX - rectWidth, rectY + rectHeight, rectX - radius, rectY + rectHeight, radius);
1371 1427 }
1372 1428 ctx.lineTo(rectX, rectY + rectHeight);
1373 1429 }
1374 1430 ctx.stroke();
1375
1431
1376 1432 // connect boxes
1377 1433 var cx = this.draw_lhs_min + this.draw_lhs_width;
1378 1434 var cy = lhs_y_start + (lhs_y_end + 1 - lhs_y_start) / 2.0;
1379 1435 var dx = this.draw_rhs_max - this.draw_rhs_width;
1380 1436 var dy = rhs_y_start + (rhs_y_end + 1 - rhs_y_start) / 2.0;
1381 1437 ctx.moveTo(cx, cy);
1382 1438 if (cy == dy) {
1383 1439 ctx.lineTo(dx, dy);
1384 1440 }
1385 1441 else {
1386 1442 // fancy!
1387 1443 ctx.bezierCurveTo(
1388 1444 cx + 12, cy - 3, // control-1 X,Y
1389 1445 dx - 12, dy - 3, // control-2 X,Y
1390 1446 dx, dy);
1391 1447 }
1392 1448 ctx.stroke();
1393 1449 }
1394 1450
1395 1451 // visible window feedback
1396 1452 ctx_lhs.fillStyle = this.settings.vpcolor;
1397 1453 ctx_rhs.fillStyle = this.settings.vpcolor;
1398
1454
1399 1455 var lto = ex.clhs.height() * ex.visible_page_ratio;
1400 1456 var lfrom = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height();
1401 1457 var rto = ex.crhs.height() * ex.visible_page_ratio;
1402 1458 var rfrom = (ex.rhs_scroller.scrollTop() / ex.gutter_height) * ex.crhs.height();
1403 1459 this.trace('draw', 'cls.height', ex.clhs.height());
1404 1460 this.trace('draw', 'lhs_scroller.scrollTop()', ex.lhs_scroller.scrollTop());
1405 1461 this.trace('draw', 'gutter_height', ex.gutter_height);
1406 1462 this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio);
1407 1463 this.trace('draw', 'lhs from', lfrom, 'lhs to', lto);
1408 1464 this.trace('draw', 'rhs from', rfrom, 'rhs to', rto);
1409
1465
1410 1466 ctx_lhs.fillRect(1.5, lfrom, 4.5, lto);
1411 1467 ctx_rhs.fillRect(1.5, rfrom, 4.5, rto);
1412
1468
1413 1469 ex.clhs.click(function (ev) {
1414 1470 var y = ev.pageY - ex.lhs_xyoffset.top - (lto / 2);
1415 1471 var sto = Math.max(0, (y / mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight);
1416 1472 ex.lhs_scroller.scrollTop(sto);
1417 1473 });
1418 1474 ex.crhs.click(function (ev) {
1419 1475 var y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2);
1420 var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight);
1476 var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight);
1421 1477 ex.rhs_scroller.scrollTop(sto);
1422 1478 });
1423 1479 },
1424 1480 trace: function(name) {
1425 1481 if(this.settings._debug.indexOf(name) >= 0) {
1426 1482 arguments[0] = name+':';
1427 1483 console.log([].slice.apply(arguments));
1428 }
1484 }
1429 1485 }
1430 1486 });
1431 1487
1432 1488 jQuery.pluginMaker = function(plugin) {
1433 1489 // add the plugin function as a jQuery plugin
1434 1490 jQuery.fn[plugin.prototype.name] = function(options) {
1435 // get the arguments
1491 // get the arguments
1436 1492 var args = jQuery.makeArray(arguments),
1437 1493 after = args.slice(1);
1438 1494 var rc = undefined;
1439 1495 this.each(function() {
1440 1496 // see if we have an instance
1441 1497 var instance = jQuery.data(this, plugin.prototype.name);
1442 1498 if (instance) {
1443 1499 // call a method on the instance
1444 1500 if (typeof options == "string") {
1445 1501 rc = instance[options].apply(instance, after);
1446 1502 } else if (instance.update) {
1447 1503 // call update on the instance
1448 1504 return instance.update.apply(instance, args);
1449 1505 }
1450 1506 } else {
1451 1507 // create the plugin
1452 1508 new plugin(this, options);
1453 1509 }
1454 1510 });
1455 1511 if (rc != undefined) return rc;
1456 1512 };
1457 1513 };
1458 1514
1459 1515 // make the mergely widget
1460 1516 jQuery.pluginMaker(Mgly.mergely);
@@ -1,112 +1,112 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="js_extra()">
6 6 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
7 <script type="text/javascript" src="${h.url('/js/mergerly.js')}"></script>
7 <script type="text/javascript" src="${h.url('/js/mergely.js')}"></script>
8 8 </%def>
9 9 <%def name="css_extra()">
10 10 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/mergerly.css')}"/>
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/mergely.css')}"/>
12 12 </%def>
13 13
14 14 <%def name="title()">
15 15 ${_('%s File side-by-side diff') % c.repo_name}
16 16 %if c.rhodecode_name:
17 17 &middot; ${c.rhodecode_name}
18 18 %endif
19 19 </%def>
20 20
21 21 <%def name="breadcrumbs_links()">
22 22 ${_('File diff')} r${c.changeset_1.revision}:${h.short_id(c.changeset_1.raw_id)} &rarr; r${c.changeset_2.revision}:${h.short_id(c.changeset_2.raw_id)}
23 23 </%def>
24 24
25 25 <%def name="page_nav()">
26 26 ${self.menu('repositories')}
27 27 </%def>
28 28
29 29 <%def name="main()">
30 30 ${self.repo_context_bar('changelog')}
31 31 <div class="box">
32 32 <!-- box / title -->
33 33 <div class="title">
34 34 ${self.breadcrumbs()}
35 35 </div>
36 36
37 37 <div class="diff-container" style="overflow-x: hidden">
38 38 <div class="diffblock comm" style="margin:3px; padding:1px">
39 39 <div class="code-header">
40 40 <div class="changeset_header">
41 41 <div class="changeset_file">
42 42 ${h.link_to(h.safe_unicode(c.node1.path),h.url('files_home',repo_name=c.repo_name,
43 43 revision=c.cs2.raw_id,f_path=h.safe_unicode(c.node1.path)))}
44 44 </div>
45 45 <div class="diff-actions">
46 46 <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node1.path),diff2=c.cs2.raw_id,diff1=c.cs1.raw_id,diff='diff',fulldiff=1)}" class="tooltip" title="${h.tooltip(_('Show full diff for this file'))}">
47 47 <img class="icon" src="${h.url('/images/icons/page_white_go.png')}"/>
48 48 </a>
49 49 <a href="${h.url('files_diff_2way_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node1.path),diff2=c.cs2.raw_id,diff1=c.cs1.raw_id,diff='diff',fulldiff=1)}" class="tooltip" title="${h.tooltip(_('Show full side-by-side diff for this file'))}">
50 50 <img class="icon" src="${h.url('/images/icons/application_double.png')}"/>
51 51 </a>
52 52 <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node1.path),diff2=c.cs2.raw_id,diff1=c.cs1.raw_id,diff='raw')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
53 53 <img class="icon" src="${h.url('/images/icons/page_white.png')}"/>
54 54 </a>
55 55 <a href="${h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node1.path),diff2=c.cs2.raw_id,diff1=c.cs1.raw_id,diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
56 56 <img class="icon" src="${h.url('/images/icons/page_save.png')}"/>
57 57 </a>
58 58 ${h.checkbox('ignorews', label=_('ignore white space'))}
59 59 ${h.checkbox('edit_mode', label=_('turn on edit mode'))}
60 60 </div>
61 61 </div>
62 62 </div>
63 63 <div id="compare"></div>
64 64 </div>
65 65 </div>
66 66
67 67 <script>
68 68 var orig1_url = '${h.url('files_raw_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node1.path),revision=c.cs1.raw_id)}';
69 69 var orig2_url = '${h.url('files_raw_home',repo_name=c.repo_name,f_path=h.safe_unicode(c.node2.path),revision=c.cs2.raw_id)}';
70 70
71 71 $(document).ready(function () {
72 72 $('#compare').mergely({
73 73 width: 'auto',
74 74 height: '600',
75 75 fgcolor: {a:'#ddffdd',c:'#cccccc',d:'#ffdddd'},
76 76 bgcolor: '#fff',
77 77 viewport: true,
78 78 cmsettings: {mode: 'text/plain', readOnly: true, lineWrapping: false, lineNumbers: true},
79 79 lhs: function(setValue) {
80 80 if("${c.node1.is_binary}" == "True"){
81 81 setValue('Binary file')
82 82 }
83 83 else{
84 84 $.ajax(orig1_url, {dataType: 'text', success: setValue});
85 85 }
86 86
87 87 },
88 88 rhs: function(setValue) {
89 89 if("${c.node2.is_binary}" == "True"){
90 90 setValue('Binary file')
91 91 }
92 92 else{
93 93 $.ajax(orig2_url, {dataType: 'text', success: setValue});
94 94 }
95 95 },
96 96 });
97 97 $('#ignorews').change(function(e){
98 98 var val = e.currentTarget.checked;
99 99 $('#compare').mergely('options', {ignorews: val});
100 100 $('#compare').mergely('update');
101 101 })
102 102 $('#edit_mode').change(function(e){
103 103 var val = !e.currentTarget.checked;
104 104 $('#compare').mergely('cm', 'lhs').setOption('readOnly', val);
105 105 $('#compare').mergely('cm', 'rhs').setOption('readOnly', val);
106 106 $('#compare').mergely('update');
107 107 })
108 108 });
109 109 </script>
110 110
111 111 </div>
112 112 </%def>
General Comments 0
You need to be logged in to leave comments. Login now