##// END OF EJS Templates
hgweb: prevent triggering dummy href="#" handler...
Yuya Nishihara -
r37850:387af9e5 @23 stable
parent child Browse files
Show More
@@ -1,578 +1,580 b''
1 // mercurial.js - JavaScript utility functions
1 // mercurial.js - JavaScript utility functions
2 //
2 //
3 // Rendering of branch DAGs on the client side
3 // Rendering of branch DAGs on the client side
4 // Display of elapsed time
4 // Display of elapsed time
5 // Show or hide diffstat
5 // Show or hide diffstat
6 //
6 //
7 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
7 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
8 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
8 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
9 //
9 //
10 // derived from code written by Scott James Remnant <scott@ubuntu.com>
10 // derived from code written by Scott James Remnant <scott@ubuntu.com>
11 // Copyright 2005 Canonical Ltd.
11 // Copyright 2005 Canonical Ltd.
12 //
12 //
13 // This software may be used and distributed according to the terms
13 // This software may be used and distributed according to the terms
14 // of the GNU General Public License, incorporated herein by reference.
14 // of the GNU General Public License, incorporated herein by reference.
15
15
16 var colors = [
16 var colors = [
17 [ 1.0, 0.0, 0.0 ],
17 [ 1.0, 0.0, 0.0 ],
18 [ 1.0, 1.0, 0.0 ],
18 [ 1.0, 1.0, 0.0 ],
19 [ 0.0, 1.0, 0.0 ],
19 [ 0.0, 1.0, 0.0 ],
20 [ 0.0, 1.0, 1.0 ],
20 [ 0.0, 1.0, 1.0 ],
21 [ 0.0, 0.0, 1.0 ],
21 [ 0.0, 0.0, 1.0 ],
22 [ 1.0, 0.0, 1.0 ]
22 [ 1.0, 0.0, 1.0 ]
23 ];
23 ];
24
24
25 function Graph() {
25 function Graph() {
26
26
27 this.canvas = document.getElementById('graph');
27 this.canvas = document.getElementById('graph');
28 this.ctx = this.canvas.getContext('2d');
28 this.ctx = this.canvas.getContext('2d');
29 this.ctx.strokeStyle = 'rgb(0, 0, 0)';
29 this.ctx.strokeStyle = 'rgb(0, 0, 0)';
30 this.ctx.fillStyle = 'rgb(0, 0, 0)';
30 this.ctx.fillStyle = 'rgb(0, 0, 0)';
31 this.bg = [0, 4];
31 this.bg = [0, 4];
32 this.cell = [2, 0];
32 this.cell = [2, 0];
33 this.columns = 0;
33 this.columns = 0;
34
34
35 }
35 }
36
36
37 Graph.prototype = {
37 Graph.prototype = {
38 reset: function() {
38 reset: function() {
39 this.bg = [0, 4];
39 this.bg = [0, 4];
40 this.cell = [2, 0];
40 this.cell = [2, 0];
41 this.columns = 0;
41 this.columns = 0;
42 },
42 },
43
43
44 scale: function(height) {
44 scale: function(height) {
45 this.bg_height = height;
45 this.bg_height = height;
46 this.box_size = Math.floor(this.bg_height / 1.2);
46 this.box_size = Math.floor(this.bg_height / 1.2);
47 this.cell_height = this.box_size;
47 this.cell_height = this.box_size;
48 },
48 },
49
49
50 setColor: function(color, bg, fg) {
50 setColor: function(color, bg, fg) {
51
51
52 // Set the colour.
52 // Set the colour.
53 //
53 //
54 // If color is a string, expect an hexadecimal RGB
54 // If color is a string, expect an hexadecimal RGB
55 // value and apply it unchanged. If color is a number,
55 // value and apply it unchanged. If color is a number,
56 // pick a distinct colour based on an internal wheel;
56 // pick a distinct colour based on an internal wheel;
57 // the bg parameter provides the value that should be
57 // the bg parameter provides the value that should be
58 // assigned to the 'zero' colours and the fg parameter
58 // assigned to the 'zero' colours and the fg parameter
59 // provides the multiplier that should be applied to
59 // provides the multiplier that should be applied to
60 // the foreground colours.
60 // the foreground colours.
61 var s;
61 var s;
62 if(typeof color === "string") {
62 if(typeof color === "string") {
63 s = "#" + color;
63 s = "#" + color;
64 } else { //typeof color === "number"
64 } else { //typeof color === "number"
65 color %= colors.length;
65 color %= colors.length;
66 var red = (colors[color][0] * fg) || bg;
66 var red = (colors[color][0] * fg) || bg;
67 var green = (colors[color][1] * fg) || bg;
67 var green = (colors[color][1] * fg) || bg;
68 var blue = (colors[color][2] * fg) || bg;
68 var blue = (colors[color][2] * fg) || bg;
69 red = Math.round(red * 255);
69 red = Math.round(red * 255);
70 green = Math.round(green * 255);
70 green = Math.round(green * 255);
71 blue = Math.round(blue * 255);
71 blue = Math.round(blue * 255);
72 s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
72 s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
73 }
73 }
74 this.ctx.strokeStyle = s;
74 this.ctx.strokeStyle = s;
75 this.ctx.fillStyle = s;
75 this.ctx.fillStyle = s;
76 return s;
76 return s;
77
77
78 },
78 },
79
79
80 edge: function(x0, y0, x1, y1, color, width) {
80 edge: function(x0, y0, x1, y1, color, width) {
81
81
82 this.setColor(color, 0.0, 0.65);
82 this.setColor(color, 0.0, 0.65);
83 if(width >= 0)
83 if(width >= 0)
84 this.ctx.lineWidth = width;
84 this.ctx.lineWidth = width;
85 this.ctx.beginPath();
85 this.ctx.beginPath();
86 this.ctx.moveTo(x0, y0);
86 this.ctx.moveTo(x0, y0);
87 this.ctx.lineTo(x1, y1);
87 this.ctx.lineTo(x1, y1);
88 this.ctx.stroke();
88 this.ctx.stroke();
89
89
90 },
90 },
91
91
92 graphNodeCurrent: function(x, y, radius) {
92 graphNodeCurrent: function(x, y, radius) {
93 this.ctx.lineWidth = 2;
93 this.ctx.lineWidth = 2;
94 this.ctx.beginPath();
94 this.ctx.beginPath();
95 this.ctx.arc(x, y, radius * 1.75, 0, Math.PI * 2, true);
95 this.ctx.arc(x, y, radius * 1.75, 0, Math.PI * 2, true);
96 this.ctx.stroke();
96 this.ctx.stroke();
97 },
97 },
98
98
99 graphNodeClosing: function(x, y, radius) {
99 graphNodeClosing: function(x, y, radius) {
100 this.ctx.fillRect(x - radius, y - 1.5, radius * 2, 3);
100 this.ctx.fillRect(x - radius, y - 1.5, radius * 2, 3);
101 },
101 },
102
102
103 graphNodeUnstable: function(x, y, radius) {
103 graphNodeUnstable: function(x, y, radius) {
104 var x30 = radius * Math.cos(Math.PI / 6);
104 var x30 = radius * Math.cos(Math.PI / 6);
105 var y30 = radius * Math.sin(Math.PI / 6);
105 var y30 = radius * Math.sin(Math.PI / 6);
106 this.ctx.lineWidth = 2;
106 this.ctx.lineWidth = 2;
107 this.ctx.beginPath();
107 this.ctx.beginPath();
108 this.ctx.moveTo(x, y - radius);
108 this.ctx.moveTo(x, y - radius);
109 this.ctx.lineTo(x, y + radius);
109 this.ctx.lineTo(x, y + radius);
110 this.ctx.moveTo(x - x30, y - y30);
110 this.ctx.moveTo(x - x30, y - y30);
111 this.ctx.lineTo(x + x30, y + y30);
111 this.ctx.lineTo(x + x30, y + y30);
112 this.ctx.moveTo(x - x30, y + y30);
112 this.ctx.moveTo(x - x30, y + y30);
113 this.ctx.lineTo(x + x30, y - y30);
113 this.ctx.lineTo(x + x30, y - y30);
114 this.ctx.stroke();
114 this.ctx.stroke();
115 },
115 },
116
116
117 graphNodeObsolete: function(x, y, radius) {
117 graphNodeObsolete: function(x, y, radius) {
118 var p45 = radius * Math.cos(Math.PI / 4);
118 var p45 = radius * Math.cos(Math.PI / 4);
119 this.ctx.lineWidth = 3;
119 this.ctx.lineWidth = 3;
120 this.ctx.beginPath();
120 this.ctx.beginPath();
121 this.ctx.moveTo(x - p45, y - p45);
121 this.ctx.moveTo(x - p45, y - p45);
122 this.ctx.lineTo(x + p45, y + p45);
122 this.ctx.lineTo(x + p45, y + p45);
123 this.ctx.moveTo(x - p45, y + p45);
123 this.ctx.moveTo(x - p45, y + p45);
124 this.ctx.lineTo(x + p45, y - p45);
124 this.ctx.lineTo(x + p45, y - p45);
125 this.ctx.stroke();
125 this.ctx.stroke();
126 },
126 },
127
127
128 graphNodeNormal: function(x, y, radius) {
128 graphNodeNormal: function(x, y, radius) {
129 this.ctx.beginPath();
129 this.ctx.beginPath();
130 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
130 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
131 this.ctx.fill();
131 this.ctx.fill();
132 },
132 },
133
133
134 vertex: function(x, y, radius, color, parity, cur) {
134 vertex: function(x, y, radius, color, parity, cur) {
135 this.ctx.save();
135 this.ctx.save();
136 this.setColor(color, 0.25, 0.75);
136 this.setColor(color, 0.25, 0.75);
137 if (cur.graphnode[0] === '@') {
137 if (cur.graphnode[0] === '@') {
138 this.graphNodeCurrent(x, y, radius);
138 this.graphNodeCurrent(x, y, radius);
139 }
139 }
140 switch (cur.graphnode.substr(-1)) {
140 switch (cur.graphnode.substr(-1)) {
141 case '_':
141 case '_':
142 this.graphNodeClosing(x, y, radius);
142 this.graphNodeClosing(x, y, radius);
143 break;
143 break;
144 case '*':
144 case '*':
145 this.graphNodeUnstable(x, y, radius);
145 this.graphNodeUnstable(x, y, radius);
146 break;
146 break;
147 case 'x':
147 case 'x':
148 this.graphNodeObsolete(x, y, radius);
148 this.graphNodeObsolete(x, y, radius);
149 break;
149 break;
150 default:
150 default:
151 this.graphNodeNormal(x, y, radius);
151 this.graphNodeNormal(x, y, radius);
152 }
152 }
153 this.ctx.restore();
153 this.ctx.restore();
154
154
155 var left = (this.bg_height - this.box_size) + (this.columns + 1) * this.box_size;
155 var left = (this.bg_height - this.box_size) + (this.columns + 1) * this.box_size;
156 var item = document.querySelector('[data-node="' + cur.node + '"]');
156 var item = document.querySelector('[data-node="' + cur.node + '"]');
157 if (item) {
157 if (item) {
158 item.style.paddingLeft = left + 'px';
158 item.style.paddingLeft = left + 'px';
159 }
159 }
160 },
160 },
161
161
162 render: function(data) {
162 render: function(data) {
163
163
164 var i, j, cur, line, start, end, color, x, y, x0, y0, x1, y1, column, radius;
164 var i, j, cur, line, start, end, color, x, y, x0, y0, x1, y1, column, radius;
165
165
166 var cols = 0;
166 var cols = 0;
167 for (i = 0; i < data.length; i++) {
167 for (i = 0; i < data.length; i++) {
168 cur = data[i];
168 cur = data[i];
169 for (j = 0; j < cur.edges.length; j++) {
169 for (j = 0; j < cur.edges.length; j++) {
170 line = cur.edges[j];
170 line = cur.edges[j];
171 cols = Math.max(cols, line[0], line[1]);
171 cols = Math.max(cols, line[0], line[1]);
172 }
172 }
173 }
173 }
174 this.canvas.width = (cols + 1) * this.bg_height;
174 this.canvas.width = (cols + 1) * this.bg_height;
175 this.canvas.height = (data.length + 1) * this.bg_height - 27;
175 this.canvas.height = (data.length + 1) * this.bg_height - 27;
176
176
177 for (i = 0; i < data.length; i++) {
177 for (i = 0; i < data.length; i++) {
178
178
179 var parity = i % 2;
179 var parity = i % 2;
180 this.cell[1] += this.bg_height;
180 this.cell[1] += this.bg_height;
181 this.bg[1] += this.bg_height;
181 this.bg[1] += this.bg_height;
182
182
183 cur = data[i];
183 cur = data[i];
184 var fold = false;
184 var fold = false;
185
185
186 var prevWidth = this.ctx.lineWidth;
186 var prevWidth = this.ctx.lineWidth;
187 for (j = 0; j < cur.edges.length; j++) {
187 for (j = 0; j < cur.edges.length; j++) {
188
188
189 line = cur.edges[j];
189 line = cur.edges[j];
190 start = line[0];
190 start = line[0];
191 end = line[1];
191 end = line[1];
192 color = line[2];
192 color = line[2];
193 var width = line[3];
193 var width = line[3];
194 if(width < 0)
194 if(width < 0)
195 width = prevWidth;
195 width = prevWidth;
196 var branchcolor = line[4];
196 var branchcolor = line[4];
197 if(branchcolor)
197 if(branchcolor)
198 color = branchcolor;
198 color = branchcolor;
199
199
200 if (end > this.columns || start > this.columns) {
200 if (end > this.columns || start > this.columns) {
201 this.columns += 1;
201 this.columns += 1;
202 }
202 }
203
203
204 if (start === this.columns && start > end) {
204 if (start === this.columns && start > end) {
205 fold = true;
205 fold = true;
206 }
206 }
207
207
208 x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
208 x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
209 y0 = this.bg[1] - this.bg_height / 2;
209 y0 = this.bg[1] - this.bg_height / 2;
210 x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
210 x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
211 y1 = this.bg[1] + this.bg_height / 2;
211 y1 = this.bg[1] + this.bg_height / 2;
212
212
213 this.edge(x0, y0, x1, y1, color, width);
213 this.edge(x0, y0, x1, y1, color, width);
214
214
215 }
215 }
216 this.ctx.lineWidth = prevWidth;
216 this.ctx.lineWidth = prevWidth;
217
217
218 // Draw the revision node in the right column
218 // Draw the revision node in the right column
219
219
220 column = cur.vertex[0];
220 column = cur.vertex[0];
221 color = cur.vertex[1];
221 color = cur.vertex[1];
222
222
223 radius = this.box_size / 8;
223 radius = this.box_size / 8;
224 x = this.cell[0] + this.box_size * column + this.box_size / 2;
224 x = this.cell[0] + this.box_size * column + this.box_size / 2;
225 y = this.bg[1] - this.bg_height / 2;
225 y = this.bg[1] - this.bg_height / 2;
226 this.vertex(x, y, radius, color, parity, cur);
226 this.vertex(x, y, radius, color, parity, cur);
227
227
228 if (fold) this.columns -= 1;
228 if (fold) this.columns -= 1;
229
229
230 }
230 }
231
231
232 }
232 }
233
233
234 };
234 };
235
235
236
236
237 function process_dates(parentSelector){
237 function process_dates(parentSelector){
238
238
239 // derived from code from mercurial/templatefilter.py
239 // derived from code from mercurial/templatefilter.py
240
240
241 var scales = {
241 var scales = {
242 'year': 365 * 24 * 60 * 60,
242 'year': 365 * 24 * 60 * 60,
243 'month': 30 * 24 * 60 * 60,
243 'month': 30 * 24 * 60 * 60,
244 'week': 7 * 24 * 60 * 60,
244 'week': 7 * 24 * 60 * 60,
245 'day': 24 * 60 * 60,
245 'day': 24 * 60 * 60,
246 'hour': 60 * 60,
246 'hour': 60 * 60,
247 'minute': 60,
247 'minute': 60,
248 'second': 1
248 'second': 1
249 };
249 };
250
250
251 function format(count, string){
251 function format(count, string){
252 var ret = count + ' ' + string;
252 var ret = count + ' ' + string;
253 if (count > 1){
253 if (count > 1){
254 ret = ret + 's';
254 ret = ret + 's';
255 }
255 }
256 return ret;
256 return ret;
257 }
257 }
258
258
259 function shortdate(date){
259 function shortdate(date){
260 var ret = date.getFullYear() + '-';
260 var ret = date.getFullYear() + '-';
261 // getMonth() gives a 0-11 result
261 // getMonth() gives a 0-11 result
262 var month = date.getMonth() + 1;
262 var month = date.getMonth() + 1;
263 if (month <= 9){
263 if (month <= 9){
264 ret += '0' + month;
264 ret += '0' + month;
265 } else {
265 } else {
266 ret += month;
266 ret += month;
267 }
267 }
268 ret += '-';
268 ret += '-';
269 var day = date.getDate();
269 var day = date.getDate();
270 if (day <= 9){
270 if (day <= 9){
271 ret += '0' + day;
271 ret += '0' + day;
272 } else {
272 } else {
273 ret += day;
273 ret += day;
274 }
274 }
275 return ret;
275 return ret;
276 }
276 }
277
277
278 function age(datestr){
278 function age(datestr){
279 var now = new Date();
279 var now = new Date();
280 var once = new Date(datestr);
280 var once = new Date(datestr);
281 if (isNaN(once.getTime())){
281 if (isNaN(once.getTime())){
282 // parsing error
282 // parsing error
283 return datestr;
283 return datestr;
284 }
284 }
285
285
286 var delta = Math.floor((now.getTime() - once.getTime()) / 1000);
286 var delta = Math.floor((now.getTime() - once.getTime()) / 1000);
287
287
288 var future = false;
288 var future = false;
289 if (delta < 0){
289 if (delta < 0){
290 future = true;
290 future = true;
291 delta = -delta;
291 delta = -delta;
292 if (delta > (30 * scales.year)){
292 if (delta > (30 * scales.year)){
293 return "in the distant future";
293 return "in the distant future";
294 }
294 }
295 }
295 }
296
296
297 if (delta > (2 * scales.year)){
297 if (delta > (2 * scales.year)){
298 return shortdate(once);
298 return shortdate(once);
299 }
299 }
300
300
301 for (var unit in scales){
301 for (var unit in scales){
302 if (!scales.hasOwnProperty(unit)) { continue; }
302 if (!scales.hasOwnProperty(unit)) { continue; }
303 var s = scales[unit];
303 var s = scales[unit];
304 var n = Math.floor(delta / s);
304 var n = Math.floor(delta / s);
305 if ((n >= 2) || (s === 1)){
305 if ((n >= 2) || (s === 1)){
306 if (future){
306 if (future){
307 return format(n, unit) + ' from now';
307 return format(n, unit) + ' from now';
308 } else {
308 } else {
309 return format(n, unit) + ' ago';
309 return format(n, unit) + ' ago';
310 }
310 }
311 }
311 }
312 }
312 }
313 }
313 }
314
314
315 var nodes = document.querySelectorAll((parentSelector || '') + ' .age');
315 var nodes = document.querySelectorAll((parentSelector || '') + ' .age');
316 var dateclass = new RegExp('\\bdate\\b');
316 var dateclass = new RegExp('\\bdate\\b');
317 for (var i=0; i<nodes.length; ++i){
317 for (var i=0; i<nodes.length; ++i){
318 var node = nodes[i];
318 var node = nodes[i];
319 var classes = node.className;
319 var classes = node.className;
320 var agevalue = age(node.textContent);
320 var agevalue = age(node.textContent);
321 if (dateclass.test(classes)){
321 if (dateclass.test(classes)){
322 // We want both: date + (age)
322 // We want both: date + (age)
323 node.textContent += ' ('+agevalue+')';
323 node.textContent += ' ('+agevalue+')';
324 } else {
324 } else {
325 node.title = node.textContent;
325 node.title = node.textContent;
326 node.textContent = agevalue;
326 node.textContent = agevalue;
327 }
327 }
328 }
328 }
329 }
329 }
330
330
331 function toggleDiffstat() {
331 function toggleDiffstat(event) {
332 var curdetails = document.getElementById('diffstatdetails').style.display;
332 var curdetails = document.getElementById('diffstatdetails').style.display;
333 var curexpand = curdetails === 'none' ? 'inline' : 'none';
333 var curexpand = curdetails === 'none' ? 'inline' : 'none';
334 document.getElementById('diffstatdetails').style.display = curexpand;
334 document.getElementById('diffstatdetails').style.display = curexpand;
335 document.getElementById('diffstatexpand').style.display = curdetails;
335 document.getElementById('diffstatexpand').style.display = curdetails;
336 event.preventDefault();
336 }
337 }
337
338
338 function toggleLinewrap() {
339 function toggleLinewrap(event) {
339 function getLinewrap() {
340 function getLinewrap() {
340 var nodes = document.getElementsByClassName('sourcelines');
341 var nodes = document.getElementsByClassName('sourcelines');
341 // if there are no such nodes, error is thrown here
342 // if there are no such nodes, error is thrown here
342 return nodes[0].classList.contains('wrap');
343 return nodes[0].classList.contains('wrap');
343 }
344 }
344
345
345 function setLinewrap(enable) {
346 function setLinewrap(enable) {
346 var nodes = document.getElementsByClassName('sourcelines');
347 var nodes = document.getElementsByClassName('sourcelines');
347 var i;
348 var i;
348 for (i = 0; i < nodes.length; i++) {
349 for (i = 0; i < nodes.length; i++) {
349 if (enable) {
350 if (enable) {
350 nodes[i].classList.add('wrap');
351 nodes[i].classList.add('wrap');
351 } else {
352 } else {
352 nodes[i].classList.remove('wrap');
353 nodes[i].classList.remove('wrap');
353 }
354 }
354 }
355 }
355
356
356 var links = document.getElementsByClassName('linewraplink');
357 var links = document.getElementsByClassName('linewraplink');
357 for (i = 0; i < links.length; i++) {
358 for (i = 0; i < links.length; i++) {
358 links[i].innerHTML = enable ? 'on' : 'off';
359 links[i].innerHTML = enable ? 'on' : 'off';
359 }
360 }
360 }
361 }
361
362
362 setLinewrap(!getLinewrap());
363 setLinewrap(!getLinewrap());
364 event.preventDefault();
363 }
365 }
364
366
365 function format(str, replacements) {
367 function format(str, replacements) {
366 return str.replace(/%(\w+)%/g, function(match, p1) {
368 return str.replace(/%(\w+)%/g, function(match, p1) {
367 return String(replacements[p1]);
369 return String(replacements[p1]);
368 });
370 });
369 }
371 }
370
372
371 function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) {
373 function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) {
372 var xhr = new XMLHttpRequest();
374 var xhr = new XMLHttpRequest();
373 xhr.onreadystatechange = function() {
375 xhr.onreadystatechange = function() {
374 if (xhr.readyState === 4) {
376 if (xhr.readyState === 4) {
375 try {
377 try {
376 if (xhr.status === 200) {
378 if (xhr.status === 200) {
377 onsuccess(xhr.responseText);
379 onsuccess(xhr.responseText);
378 } else {
380 } else {
379 throw 'server error';
381 throw 'server error';
380 }
382 }
381 } catch (e) {
383 } catch (e) {
382 onerror(e);
384 onerror(e);
383 } finally {
385 } finally {
384 oncomplete();
386 oncomplete();
385 }
387 }
386 }
388 }
387 };
389 };
388
390
389 xhr.open(method, url);
391 xhr.open(method, url);
390 xhr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase());
392 xhr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase());
391 xhr.send();
393 xhr.send();
392 onstart();
394 onstart();
393 return xhr;
395 return xhr;
394 }
396 }
395
397
396 function removeByClassName(className) {
398 function removeByClassName(className) {
397 var nodes = document.getElementsByClassName(className);
399 var nodes = document.getElementsByClassName(className);
398 while (nodes.length) {
400 while (nodes.length) {
399 nodes[0].parentNode.removeChild(nodes[0]);
401 nodes[0].parentNode.removeChild(nodes[0]);
400 }
402 }
401 }
403 }
402
404
403 function docFromHTML(html) {
405 function docFromHTML(html) {
404 var doc = document.implementation.createHTMLDocument('');
406 var doc = document.implementation.createHTMLDocument('');
405 doc.documentElement.innerHTML = html;
407 doc.documentElement.innerHTML = html;
406 return doc;
408 return doc;
407 }
409 }
408
410
409 function appendFormatHTML(element, formatStr, replacements) {
411 function appendFormatHTML(element, formatStr, replacements) {
410 element.insertAdjacentHTML('beforeend', format(formatStr, replacements));
412 element.insertAdjacentHTML('beforeend', format(formatStr, replacements));
411 }
413 }
412
414
413 function adoptChildren(from, to) {
415 function adoptChildren(from, to) {
414 var nodes = from.children;
416 var nodes = from.children;
415 var curClass = 'c' + Date.now();
417 var curClass = 'c' + Date.now();
416 while (nodes.length) {
418 while (nodes.length) {
417 var node = nodes[0];
419 var node = nodes[0];
418 node = document.adoptNode(node);
420 node = document.adoptNode(node);
419 node.classList.add(curClass);
421 node.classList.add(curClass);
420 to.appendChild(node);
422 to.appendChild(node);
421 }
423 }
422 process_dates('.' + curClass);
424 process_dates('.' + curClass);
423 }
425 }
424
426
425 function ajaxScrollInit(urlFormat,
427 function ajaxScrollInit(urlFormat,
426 nextPageVar,
428 nextPageVar,
427 nextPageVarGet,
429 nextPageVarGet,
428 containerSelector,
430 containerSelector,
429 messageFormat,
431 messageFormat,
430 mode) {
432 mode) {
431 var updateInitiated = false;
433 var updateInitiated = false;
432 var container = document.querySelector(containerSelector);
434 var container = document.querySelector(containerSelector);
433
435
434 function scrollHandler() {
436 function scrollHandler() {
435 if (updateInitiated) {
437 if (updateInitiated) {
436 return;
438 return;
437 }
439 }
438
440
439 var scrollHeight = document.documentElement.scrollHeight;
441 var scrollHeight = document.documentElement.scrollHeight;
440 var clientHeight = document.documentElement.clientHeight;
442 var clientHeight = document.documentElement.clientHeight;
441 var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
443 var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
442
444
443 if (scrollHeight - (scrollTop + clientHeight) < 50) {
445 if (scrollHeight - (scrollTop + clientHeight) < 50) {
444 updateInitiated = true;
446 updateInitiated = true;
445 removeByClassName('scroll-loading-error');
447 removeByClassName('scroll-loading-error');
446 container.lastElementChild.classList.add('scroll-separator');
448 container.lastElementChild.classList.add('scroll-separator');
447
449
448 if (!nextPageVar) {
450 if (!nextPageVar) {
449 var message = {
451 var message = {
450 'class': 'scroll-loading-info',
452 'class': 'scroll-loading-info',
451 text: 'No more entries'
453 text: 'No more entries'
452 };
454 };
453 appendFormatHTML(container, messageFormat, message);
455 appendFormatHTML(container, messageFormat, message);
454 return;
456 return;
455 }
457 }
456
458
457 makeRequest(
459 makeRequest(
458 format(urlFormat, {next: nextPageVar}),
460 format(urlFormat, {next: nextPageVar}),
459 'GET',
461 'GET',
460 function onstart() {
462 function onstart() {
461 var message = {
463 var message = {
462 'class': 'scroll-loading',
464 'class': 'scroll-loading',
463 text: 'Loading...'
465 text: 'Loading...'
464 };
466 };
465 appendFormatHTML(container, messageFormat, message);
467 appendFormatHTML(container, messageFormat, message);
466 },
468 },
467 function onsuccess(htmlText) {
469 function onsuccess(htmlText) {
468 var doc = docFromHTML(htmlText);
470 var doc = docFromHTML(htmlText);
469
471
470 if (mode === 'graph') {
472 if (mode === 'graph') {
471 var graph = window.graph;
473 var graph = window.graph;
472 var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1];
474 var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1];
473 var data = JSON.parse(dataStr);
475 var data = JSON.parse(dataStr);
474 graph.reset();
476 graph.reset();
475 adoptChildren(doc.querySelector('#graphnodes'), container.querySelector('#graphnodes'));
477 adoptChildren(doc.querySelector('#graphnodes'), container.querySelector('#graphnodes'));
476 graph.render(data);
478 graph.render(data);
477 } else {
479 } else {
478 adoptChildren(doc.querySelector(containerSelector), container);
480 adoptChildren(doc.querySelector(containerSelector), container);
479 }
481 }
480
482
481 nextPageVar = nextPageVarGet(htmlText);
483 nextPageVar = nextPageVarGet(htmlText);
482 },
484 },
483 function onerror(errorText) {
485 function onerror(errorText) {
484 var message = {
486 var message = {
485 'class': 'scroll-loading-error',
487 'class': 'scroll-loading-error',
486 text: 'Error: ' + errorText
488 text: 'Error: ' + errorText
487 };
489 };
488 appendFormatHTML(container, messageFormat, message);
490 appendFormatHTML(container, messageFormat, message);
489 },
491 },
490 function oncomplete() {
492 function oncomplete() {
491 removeByClassName('scroll-loading');
493 removeByClassName('scroll-loading');
492 updateInitiated = false;
494 updateInitiated = false;
493 scrollHandler();
495 scrollHandler();
494 }
496 }
495 );
497 );
496 }
498 }
497 }
499 }
498
500
499 window.addEventListener('scroll', scrollHandler);
501 window.addEventListener('scroll', scrollHandler);
500 window.addEventListener('resize', scrollHandler);
502 window.addEventListener('resize', scrollHandler);
501 scrollHandler();
503 scrollHandler();
502 }
504 }
503
505
504 function renderDiffOptsForm() {
506 function renderDiffOptsForm() {
505 // We use URLSearchParams for query string manipulation. Old browsers don't
507 // We use URLSearchParams for query string manipulation. Old browsers don't
506 // support this API.
508 // support this API.
507 if (!("URLSearchParams" in window)) {
509 if (!("URLSearchParams" in window)) {
508 return;
510 return;
509 }
511 }
510
512
511 var form = document.getElementById("diffopts-form");
513 var form = document.getElementById("diffopts-form");
512
514
513 var KEYS = [
515 var KEYS = [
514 "ignorews",
516 "ignorews",
515 "ignorewsamount",
517 "ignorewsamount",
516 "ignorewseol",
518 "ignorewseol",
517 "ignoreblanklines",
519 "ignoreblanklines",
518 ];
520 ];
519
521
520 var urlParams = new window.URLSearchParams(window.location.search);
522 var urlParams = new window.URLSearchParams(window.location.search);
521
523
522 function updateAndRefresh(e) {
524 function updateAndRefresh(e) {
523 var checkbox = e.target;
525 var checkbox = e.target;
524 var name = checkbox.id.substr(0, checkbox.id.indexOf("-"));
526 var name = checkbox.id.substr(0, checkbox.id.indexOf("-"));
525 urlParams.set(name, checkbox.checked ? "1" : "0");
527 urlParams.set(name, checkbox.checked ? "1" : "0");
526 window.location.search = urlParams.toString();
528 window.location.search = urlParams.toString();
527 }
529 }
528
530
529 var allChecked = form.getAttribute("data-ignorews") === "1";
531 var allChecked = form.getAttribute("data-ignorews") === "1";
530
532
531 for (var i = 0; i < KEYS.length; i++) {
533 for (var i = 0; i < KEYS.length; i++) {
532 var key = KEYS[i];
534 var key = KEYS[i];
533
535
534 var checkbox = document.getElementById(key + "-checkbox");
536 var checkbox = document.getElementById(key + "-checkbox");
535 if (!checkbox) {
537 if (!checkbox) {
536 continue;
538 continue;
537 }
539 }
538
540
539 var currentValue = form.getAttribute("data-" + key);
541 var currentValue = form.getAttribute("data-" + key);
540 checkbox.checked = currentValue !== "0";
542 checkbox.checked = currentValue !== "0";
541
543
542 // ignorews implies ignorewsamount and ignorewseol.
544 // ignorews implies ignorewsamount and ignorewseol.
543 if (allChecked && (key === "ignorewsamount" || key === "ignorewseol")) {
545 if (allChecked && (key === "ignorewsamount" || key === "ignorewseol")) {
544 checkbox.checked = true;
546 checkbox.checked = true;
545 checkbox.disabled = true;
547 checkbox.disabled = true;
546 }
548 }
547
549
548 checkbox.addEventListener("change", updateAndRefresh, false);
550 checkbox.addEventListener("change", updateAndRefresh, false);
549 }
551 }
550
552
551 form.style.display = 'block';
553 form.style.display = 'block';
552 }
554 }
553
555
554 function addDiffStatToggle() {
556 function addDiffStatToggle() {
555 var els = document.getElementsByClassName("diffstattoggle");
557 var els = document.getElementsByClassName("diffstattoggle");
556
558
557 for (var i = 0; i < els.length; i++) {
559 for (var i = 0; i < els.length; i++) {
558 els[i].addEventListener("click", toggleDiffstat, false);
560 els[i].addEventListener("click", toggleDiffstat, false);
559 }
561 }
560 }
562 }
561
563
562 function addLineWrapToggle() {
564 function addLineWrapToggle() {
563 var els = document.getElementsByClassName("linewraptoggle");
565 var els = document.getElementsByClassName("linewraptoggle");
564
566
565 for (var i = 0; i < els.length; i++) {
567 for (var i = 0; i < els.length; i++) {
566 var nodes = els[i].getElementsByClassName("linewraplink");
568 var nodes = els[i].getElementsByClassName("linewraplink");
567
569
568 for (var j = 0; j < nodes.length; j++) {
570 for (var j = 0; j < nodes.length; j++) {
569 nodes[j].addEventListener("click", toggleLinewrap, false);
571 nodes[j].addEventListener("click", toggleLinewrap, false);
570 }
572 }
571 }
573 }
572 }
574 }
573
575
574 document.addEventListener('DOMContentLoaded', function() {
576 document.addEventListener('DOMContentLoaded', function() {
575 process_dates();
577 process_dates();
576 addDiffStatToggle();
578 addDiffStatToggle();
577 addLineWrapToggle();
579 addLineWrapToggle();
578 }, false);
580 }, false);
General Comments 0
You need to be logged in to leave comments. Login now