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