##// END OF EJS Templates
hgweb: update canvas.width before dynamically redrawing graph (issue2683)...
av6 -
r27934:1779ff74 stable
parent child Browse files
Show More
@@ -1,431 +1,435 b''
1 1 // mercurial.js - JavaScript utility functions
2 2 //
3 3 // Rendering of branch DAGs on the client side
4 4 // Display of elapsed time
5 5 // Show or hide diffstat
6 6 //
7 7 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
8 8 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
9 9 //
10 10 // derived from code written by Scott James Remnant <scott@ubuntu.com>
11 11 // Copyright 2005 Canonical Ltd.
12 12 //
13 13 // This software may be used and distributed according to the terms
14 14 // of the GNU General Public License, incorporated herein by reference.
15 15
16 16 var colors = [
17 17 [ 1.0, 0.0, 0.0 ],
18 18 [ 1.0, 1.0, 0.0 ],
19 19 [ 0.0, 1.0, 0.0 ],
20 20 [ 0.0, 1.0, 1.0 ],
21 21 [ 0.0, 0.0, 1.0 ],
22 22 [ 1.0, 0.0, 1.0 ]
23 23 ];
24 24
25 25 function Graph() {
26 26
27 27 this.canvas = document.getElementById('graph');
28 28 if (window.G_vmlCanvasManager) this.canvas = window.G_vmlCanvasManager.initElement(this.canvas);
29 29 this.ctx = this.canvas.getContext('2d');
30 30 this.ctx.strokeStyle = 'rgb(0, 0, 0)';
31 31 this.ctx.fillStyle = 'rgb(0, 0, 0)';
32 32 this.cur = [0, 0];
33 33 this.line_width = 3;
34 34 this.bg = [0, 4];
35 35 this.cell = [2, 0];
36 36 this.columns = 0;
37 37 this.revlink = '';
38 38
39 39 this.reset = function() {
40 40 this.bg = [0, 4];
41 41 this.cell = [2, 0];
42 42 this.columns = 0;
43 43 document.getElementById('nodebgs').innerHTML = '';
44 44 document.getElementById('graphnodes').innerHTML = '';
45 45 }
46 46
47 47 this.scale = function(height) {
48 48 this.bg_height = height;
49 49 this.box_size = Math.floor(this.bg_height / 1.2);
50 50 this.cell_height = this.box_size;
51 51 }
52 52
53 53 this.setColor = function(color, bg, fg) {
54 54
55 55 // Set the colour.
56 56 //
57 57 // If color is a string, expect an hexadecimal RGB
58 58 // value and apply it unchanged. If color is a number,
59 59 // pick a distinct colour based on an internal wheel;
60 60 // the bg parameter provides the value that should be
61 61 // assigned to the 'zero' colours and the fg parameter
62 62 // provides the multiplier that should be applied to
63 63 // the foreground colours.
64 64 var s;
65 65 if(typeof color == "string") {
66 66 s = "#" + color;
67 67 } else { //typeof color == "number"
68 68 color %= colors.length;
69 69 var red = (colors[color][0] * fg) || bg;
70 70 var green = (colors[color][1] * fg) || bg;
71 71 var blue = (colors[color][2] * fg) || bg;
72 72 red = Math.round(red * 255);
73 73 green = Math.round(green * 255);
74 74 blue = Math.round(blue * 255);
75 75 s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
76 76 }
77 77 this.ctx.strokeStyle = s;
78 78 this.ctx.fillStyle = s;
79 79 return s;
80 80
81 81 }
82 82
83 83 this.edge = function(x0, y0, x1, y1, color, width) {
84 84
85 85 this.setColor(color, 0.0, 0.65);
86 86 if(width >= 0)
87 87 this.ctx.lineWidth = width;
88 88 this.ctx.beginPath();
89 89 this.ctx.moveTo(x0, y0);
90 90 this.ctx.lineTo(x1, y1);
91 91 this.ctx.stroke();
92 92
93 93 }
94 94
95 95 this.render = function(data) {
96 96
97 97 var backgrounds = '';
98 98 var nodedata = '';
99 99
100 100 for (var i in data) {
101 101
102 102 var parity = i % 2;
103 103 this.cell[1] += this.bg_height;
104 104 this.bg[1] += this.bg_height;
105 105
106 106 var cur = data[i];
107 107 var node = cur[1];
108 108 var edges = cur[2];
109 109 var fold = false;
110 110
111 111 var prevWidth = this.ctx.lineWidth;
112 112 for (var j in edges) {
113 113
114 114 line = edges[j];
115 115 start = line[0];
116 116 end = line[1];
117 117 color = line[2];
118 118 var width = line[3];
119 119 if(width < 0)
120 120 width = prevWidth;
121 121 var branchcolor = line[4];
122 122 if(branchcolor)
123 123 color = branchcolor;
124 124
125 125 if (end > this.columns || start > this.columns) {
126 126 this.columns += 1;
127 127 }
128 128
129 129 if (start == this.columns && start > end) {
130 130 var fold = true;
131 131 }
132 132
133 133 x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
134 134 y0 = this.bg[1] - this.bg_height / 2;
135 135 x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
136 136 y1 = this.bg[1] + this.bg_height / 2;
137 137
138 138 this.edge(x0, y0, x1, y1, color, width);
139 139
140 140 }
141 141 this.ctx.lineWidth = prevWidth;
142 142
143 143 // Draw the revision node in the right column
144 144
145 145 column = node[0]
146 146 color = node[1]
147 147
148 148 radius = this.box_size / 8;
149 149 x = this.cell[0] + this.box_size * column + this.box_size / 2;
150 150 y = this.bg[1] - this.bg_height / 2;
151 151 var add = this.vertex(x, y, color, parity, cur);
152 152 backgrounds += add[0];
153 153 nodedata += add[1];
154 154
155 155 if (fold) this.columns -= 1;
156 156
157 157 }
158 158
159 159 document.getElementById('nodebgs').innerHTML += backgrounds;
160 160 document.getElementById('graphnodes').innerHTML += nodedata;
161 161
162 162 }
163 163
164 164 }
165 165
166 166
167 167 function process_dates(parentSelector){
168 168
169 169 // derived from code from mercurial/templatefilter.py
170 170
171 171 var scales = {
172 172 'year': 365 * 24 * 60 * 60,
173 173 'month': 30 * 24 * 60 * 60,
174 174 'week': 7 * 24 * 60 * 60,
175 175 'day': 24 * 60 * 60,
176 176 'hour': 60 * 60,
177 177 'minute': 60,
178 178 'second': 1
179 179 };
180 180
181 181 function format(count, string){
182 182 var ret = count + ' ' + string;
183 183 if (count > 1){
184 184 ret = ret + 's';
185 185 }
186 186 return ret;
187 187 }
188 188
189 189 function shortdate(date){
190 190 var ret = date.getFullYear() + '-';
191 191 // getMonth() gives a 0-11 result
192 192 var month = date.getMonth() + 1;
193 193 if (month <= 9){
194 194 ret += '0' + month;
195 195 } else {
196 196 ret += month;
197 197 }
198 198 ret += '-';
199 199 var day = date.getDate();
200 200 if (day <= 9){
201 201 ret += '0' + day;
202 202 } else {
203 203 ret += day;
204 204 }
205 205 return ret;
206 206 }
207 207
208 208 function age(datestr){
209 209 var now = new Date();
210 210 var once = new Date(datestr);
211 211 if (isNaN(once.getTime())){
212 212 // parsing error
213 213 return datestr;
214 214 }
215 215
216 216 var delta = Math.floor((now.getTime() - once.getTime()) / 1000);
217 217
218 218 var future = false;
219 219 if (delta < 0){
220 220 future = true;
221 221 delta = -delta;
222 222 if (delta > (30 * scales.year)){
223 223 return "in the distant future";
224 224 }
225 225 }
226 226
227 227 if (delta > (2 * scales.year)){
228 228 return shortdate(once);
229 229 }
230 230
231 231 for (unit in scales){
232 232 var s = scales[unit];
233 233 var n = Math.floor(delta / s);
234 234 if ((n >= 2) || (s == 1)){
235 235 if (future){
236 236 return format(n, unit) + ' from now';
237 237 } else {
238 238 return format(n, unit) + ' ago';
239 239 }
240 240 }
241 241 }
242 242 }
243 243
244 244 var nodes = document.querySelectorAll((parentSelector || '') + ' .age');
245 245 var dateclass = new RegExp('\\bdate\\b');
246 246 for (var i=0; i<nodes.length; ++i){
247 247 var node = nodes[i];
248 248 var classes = node.className;
249 249 var agevalue = age(node.textContent);
250 250 if (dateclass.test(classes)){
251 251 // We want both: date + (age)
252 252 node.textContent += ' ('+agevalue+')';
253 253 } else {
254 254 node.title = node.textContent;
255 255 node.textContent = agevalue;
256 256 }
257 257 }
258 258 }
259 259
260 260 function toggleDiffstat() {
261 261 var curdetails = document.getElementById('diffstatdetails').style.display;
262 262 var curexpand = curdetails == 'none' ? 'inline' : 'none';
263 263 document.getElementById('diffstatdetails').style.display = curexpand;
264 264 document.getElementById('diffstatexpand').style.display = curdetails;
265 265 }
266 266
267 267 function toggleLinewrap() {
268 268 function getLinewrap() {
269 269 var nodes = document.getElementsByClassName('sourcelines');
270 270 // if there are no such nodes, error is thrown here
271 271 return nodes[0].classList.contains('wrap');
272 272 }
273 273
274 274 function setLinewrap(enable) {
275 275 var nodes = document.getElementsByClassName('sourcelines');
276 276 for (var i = 0; i < nodes.length; i++) {
277 277 if (enable) {
278 278 nodes[i].classList.add('wrap');
279 279 } else {
280 280 nodes[i].classList.remove('wrap');
281 281 }
282 282 }
283 283
284 284 var links = document.getElementsByClassName('linewraplink');
285 285 for (var i = 0; i < links.length; i++) {
286 286 links[i].innerHTML = enable ? 'on' : 'off';
287 287 }
288 288 }
289 289
290 290 setLinewrap(!getLinewrap());
291 291 }
292 292
293 293 function format(str, replacements) {
294 294 return str.replace(/%(\w+)%/g, function(match, p1) {
295 295 return String(replacements[p1]);
296 296 });
297 297 }
298 298
299 299 function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) {
300 300 xfr = new XMLHttpRequest();
301 301 xfr.onreadystatechange = function() {
302 302 if (xfr.readyState === 4) {
303 303 try {
304 304 if (xfr.status === 200) {
305 305 onsuccess(xfr.responseText);
306 306 } else {
307 307 throw 'server error';
308 308 }
309 309 } catch (e) {
310 310 onerror(e);
311 311 } finally {
312 312 oncomplete();
313 313 }
314 314 }
315 315 };
316 316
317 317 xfr.open(method, url);
318 318 xfr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase());
319 319 xfr.send();
320 320 onstart();
321 321 return xfr;
322 322 }
323 323
324 324 function removeByClassName(className) {
325 325 var nodes = document.getElementsByClassName(className);
326 326 while (nodes.length) {
327 327 nodes[0].parentNode.removeChild(nodes[0]);
328 328 }
329 329 }
330 330
331 331 function docFromHTML(html) {
332 332 var doc = document.implementation.createHTMLDocument('');
333 333 doc.documentElement.innerHTML = html;
334 334 return doc;
335 335 }
336 336
337 337 function appendFormatHTML(element, formatStr, replacements) {
338 338 element.insertAdjacentHTML('beforeend', format(formatStr, replacements));
339 339 }
340 340
341 341 function ajaxScrollInit(urlFormat,
342 342 nextPageVar,
343 343 nextPageVarGet,
344 344 containerSelector,
345 345 messageFormat,
346 346 mode) {
347 347 updateInitiated = false;
348 348 container = document.querySelector(containerSelector);
349 349
350 350 function scrollHandler() {
351 351 if (updateInitiated) {
352 352 return;
353 353 }
354 354
355 355 var scrollHeight = document.documentElement.scrollHeight;
356 356 var clientHeight = document.documentElement.clientHeight;
357 357 var scrollTop = document.body.scrollTop
358 358 || document.documentElement.scrollTop;
359 359
360 360 if (scrollHeight - (scrollTop + clientHeight) < 50) {
361 361 updateInitiated = true;
362 362 removeByClassName('scroll-loading-error');
363 363 container.lastElementChild.classList.add('scroll-separator');
364 364
365 365 if (!nextPageVar) {
366 366 var message = {
367 367 'class': 'scroll-loading-info',
368 368 text: 'No more entries'
369 369 };
370 370 appendFormatHTML(container, messageFormat, message);
371 371 return;
372 372 }
373 373
374 374 makeRequest(
375 375 format(urlFormat, {next: nextPageVar}),
376 376 'GET',
377 377 function onstart() {
378 378 var message = {
379 379 'class': 'scroll-loading',
380 380 text: 'Loading...'
381 381 };
382 382 appendFormatHTML(container, messageFormat, message);
383 383 },
384 384 function onsuccess(htmlText) {
385 385 if (mode == 'graph') {
386 var addHeight = htmlText.match(/^\s*<canvas id="graph".*height="(\d+)"><\/canvas>$/m)[1];
386 var sizes = htmlText.match(/^\s*<canvas id="graph" width="(\d+)" height="(\d+)"><\/canvas>$/m);
387 var addWidth = sizes[1];
388 var addHeight = sizes[2];
389 addWidth = parseInt(addWidth);
387 390 addHeight = parseInt(addHeight);
391 graph.canvas.width = addWidth;
388 392 graph.canvas.height = addHeight;
389 393
390 394 var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1];
391 395 var data = JSON.parse(dataStr);
392 396 if (data.length < nextPageVar) {
393 397 nextPageVar = undefined;
394 398 }
395 399 graph.reset();
396 400 graph.render(data);
397 401 } else {
398 402 var doc = docFromHTML(htmlText);
399 403 var nodes = doc.querySelector(containerSelector).children;
400 404 var curClass = 'c' + Date.now();
401 405 while (nodes.length) {
402 406 var node = nodes[0];
403 407 node = document.adoptNode(node);
404 408 node.classList.add(curClass);
405 409 container.appendChild(node);
406 410 }
407 411 process_dates('.' + curClass);
408 412 }
409 413
410 414 nextPageVar = nextPageVarGet(htmlText, nextPageVar);
411 415 },
412 416 function onerror(errorText) {
413 417 var message = {
414 418 'class': 'scroll-loading-error',
415 419 text: 'Error: ' + errorText
416 420 };
417 421 appendFormatHTML(container, messageFormat, message);
418 422 },
419 423 function oncomplete() {
420 424 removeByClassName('scroll-loading');
421 425 updateInitiated = false;
422 426 scrollHandler();
423 427 }
424 428 );
425 429 }
426 430 }
427 431
428 432 window.addEventListener('scroll', scrollHandler);
429 433 window.addEventListener('resize', scrollHandler);
430 434 scrollHandler();
431 435 }
General Comments 0
You need to be logged in to leave comments. Login now