changeset 53: |
d25f982fb8a6 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Sat, 20 Jul 2024 22:31:54 -0400 |
permissions: |
-rwxr-xr-x |
description: |
init vc |
1 // mercurial.js - JavaScript utility functions 3 // Rendering of branch DAGs on the client side 4 // Display of elapsed time 5 // Show or hide diffstat 7 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl> 8 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de> 10 // derived from code written by Scott James Remnant <scott@ubuntu.com> 11 // Copyright 2005 Canonical Ltd. 13 // This software may be used and distributed according to the terms 14 // of the GNU General Public License, incorporated herein by reference. 27 this.canvas = document.getElementById('graph'); 28 this.ctx = this.canvas.getContext('2d'); 29 this.ctx.strokeStyle = 'rgb(0, 0, 0)'; 30 this.ctx.fillStyle = 'rgb(0, 0, 0)'; 44 scale: function(height) { 45 this.bg_height = height; 46 this.box_size = Math.floor(this.bg_height / 1.2); 47 this.cell_height = this.box_size; 50 setColor: function(color, bg, fg) { 54 // If color is a string, expect an hexadecimal RGB 55 // value and apply it unchanged. If color is a number, 56 // pick a distinct colour based on an internal wheel; 57 // the bg parameter provides the value that should be 58 // assigned to the 'zero' colours and the fg parameter 59 // provides the multiplier that should be applied to 60 // the foreground colours. 62 if(typeof color === "string") { 64 } else { //typeof color === "number" 65 color %= colors.length; 66 var red = (colors[color][0] * fg) || bg; 67 var green = (colors[color][1] * fg) || bg; 68 var blue = (colors[color][2] * fg) || bg; 69 red = Math.round(red * 255); 70 green = Math.round(green * 255); 71 blue = Math.round(blue * 255); 72 s = 'rgb(' + red + ', ' + green + ', ' + blue + ')'; 74 this.ctx.strokeStyle = s; 75 this.ctx.fillStyle = s; 80 edge: function(x0, y0, x1, y1, color, width) { 82 this.setColor(color, 0.0, 0.65); 84 this.ctx.lineWidth = width; 86 this.ctx.moveTo(x0, y0); 87 this.ctx.lineTo(x1, y1); 92 graphNodeCurrent: function(x, y, radius) { 93 this.ctx.lineWidth = 2; 95 this.ctx.arc(x, y, radius * 1.75, 0, Math.PI * 2, true); 99 graphNodeClosing: function(x, y, radius) { 100 this.ctx.fillRect(x - radius, y - 1.5, radius * 2, 3); 103 graphNodeUnstable: function(x, y, radius) { 104 var x30 = radius * Math.cos(Math.PI / 6); 105 var y30 = radius * Math.sin(Math.PI / 6); 106 this.ctx.lineWidth = 2; 107 this.ctx.beginPath(); 108 this.ctx.moveTo(x, y - radius); 109 this.ctx.lineTo(x, y + radius); 110 this.ctx.moveTo(x - x30, y - y30); 111 this.ctx.lineTo(x + x30, y + y30); 112 this.ctx.moveTo(x - x30, y + y30); 113 this.ctx.lineTo(x + x30, y - y30); 117 graphNodeObsolete: function(x, y, radius) { 118 var p45 = radius * Math.cos(Math.PI / 4); 119 this.ctx.lineWidth = 3; 120 this.ctx.beginPath(); 121 this.ctx.moveTo(x - p45, y - p45); 122 this.ctx.lineTo(x + p45, y + p45); 123 this.ctx.moveTo(x - p45, y + p45); 124 this.ctx.lineTo(x + p45, y - p45); 128 graphNodeNormal: function(x, y, radius) { 129 this.ctx.beginPath(); 130 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true); 134 vertex: function(x, y, radius, color, parity, cur) { 136 this.setColor(color, 0.25, 0.75); 137 if (cur.graphnode[0] === '@') { 138 this.graphNodeCurrent(x, y, radius); 140 switch (cur.graphnode.substr(-1)) { 142 this.graphNodeClosing(x, y, radius); 145 this.graphNodeUnstable(x, y, radius); 148 this.graphNodeObsolete(x, y, radius); 151 this.graphNodeNormal(x, y, radius); 155 var left = (this.bg_height - this.box_size) + (this.columns + 1) * this.box_size; 156 var item = document.querySelector('[data-node="' + cur.node + '"]'); 158 item.style.paddingLeft = left + 'px'; 162 render: function(data) { 164 var i, j, cur, line, start, end, color, x, y, x0, y0, x1, y1, column, radius; 167 for (i = 0; i < data.length; i++) { 169 for (j = 0; j < cur.edges.length; j++) { 171 cols = Math.max(cols, line[0], line[1]); 174 this.canvas.width = (cols + 1) * this.bg_height; 175 this.canvas.height = (data.length + 1) * this.bg_height - 27; 177 for (i = 0; i < data.length; i++) { 180 this.cell[1] += this.bg_height; 181 this.bg[1] += this.bg_height; 186 var prevWidth = this.ctx.lineWidth; 187 for (j = 0; j < cur.edges.length; j++) { 196 var branchcolor = line[4]; 200 if (end > this.columns || start > this.columns) { 204 if (start === this.columns && start > end) { 208 x0 = this.cell[0] + this.box_size * start + this.box_size / 2; 209 y0 = this.bg[1] - this.bg_height / 2; 210 x1 = this.cell[0] + this.box_size * end + this.box_size / 2; 211 y1 = this.bg[1] + this.bg_height / 2; 213 this.edge(x0, y0, x1, y1, color, width); 216 this.ctx.lineWidth = prevWidth; 218 // Draw the revision node in the right column 220 column = cur.vertex[0]; 221 color = cur.vertex[1]; 223 radius = this.box_size / 8; 224 x = this.cell[0] + this.box_size * column + this.box_size / 2; 225 y = this.bg[1] - this.bg_height / 2; 226 this.vertex(x, y, radius, color, parity, cur); 228 if (fold) this.columns -= 1; 237 function process_dates(parentSelector){ 239 // derived from code from mercurial/templatefilter.py 242 'year': 365 * 24 * 60 * 60, 243 'month': 30 * 24 * 60 * 60, 244 'week': 7 * 24 * 60 * 60, 251 function format(count, string){ 252 var ret = count + ' ' + string; 259 function shortdate(date){ 260 var ret = date.getFullYear() + '-'; 261 // getMonth() gives a 0-11 result 262 var month = date.getMonth() + 1; 269 var day = date.getDate(); 278 function age(datestr){ 279 var now = new Date(); 280 var once = new Date(datestr); 281 if (isNaN(once.getTime())){ 286 var delta = Math.floor((now.getTime() - once.getTime()) / 1000); 292 if (delta > (30 * scales.year)){ 293 return "in the distant future"; 297 if (delta > (2 * scales.year)){ 298 return shortdate(once); 301 for (var unit in scales){ 302 if (!scales.hasOwnProperty(unit)) { continue; } 303 var s = scales[unit]; 304 var n = Math.floor(delta / s); 305 if ((n >= 2) || (s === 1)){ 307 return format(n, unit) + ' from now'; 309 return format(n, unit) + ' ago'; 315 var nodes = document.querySelectorAll((parentSelector || '') + ' .age'); 316 var dateclass = new RegExp('\\bdate\\b'); 317 for (var i=0; i<nodes.length; ++i){ 319 var classes = node.className; 320 var agevalue = age(node.textContent); 321 if (dateclass.test(classes)){ 322 // We want both: date + (age) 323 node.textContent += ' ('+agevalue+')'; 325 node.title = node.textContent; 326 node.textContent = agevalue; 331 function toggleDiffstat(event) { 332 var curdetails = document.getElementById('diffstatdetails').style.display; 333 var curexpand = curdetails === 'none' ? 'inline' : 'none'; 334 document.getElementById('diffstatdetails').style.display = curexpand; 335 document.getElementById('diffstatexpand').style.display = curdetails; 336 event.preventDefault(); 339 function toggleLinewrap(event) { 340 function getLinewrap() { 341 var nodes = document.getElementsByClassName('sourcelines'); 342 // if there are no such nodes, error is thrown here 343 return nodes[0].classList.contains('wrap'); 346 function setLinewrap(enable) { 347 var nodes = document.getElementsByClassName('sourcelines'); 349 for (i = 0; i < nodes.length; i++) { 351 nodes[i].classList.add('wrap'); 353 nodes[i].classList.remove('wrap'); 357 var links = document.getElementsByClassName('linewraplink'); 358 for (i = 0; i < links.length; i++) { 359 links[i].innerHTML = enable ? 'on' : 'off'; 363 setLinewrap(!getLinewrap()); 364 event.preventDefault(); 367 function format(str, replacements) { 368 return str.replace(/%(\w+)%/g, function(match, p1) { 369 return String(replacements[p1]); 373 function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) { 374 var xhr = new XMLHttpRequest(); 375 xhr.onreadystatechange = function() { 376 if (xhr.readyState === 4) { 378 if (xhr.status === 200) { 379 onsuccess(xhr.responseText); 381 throw 'server error'; 391 xhr.open(method, url); 392 xhr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase()); 398 function removeByClassName(className) { 399 var nodes = document.getElementsByClassName(className); 400 while (nodes.length) { 401 nodes[0].parentNode.removeChild(nodes[0]); 405 function docFromHTML(html) { 406 var doc = document.implementation.createHTMLDocument(''); 407 doc.documentElement.innerHTML = html; 411 function appendFormatHTML(element, formatStr, replacements) { 412 element.insertAdjacentHTML('beforeend', format(formatStr, replacements)); 415 function adoptChildren(from, to) { 416 var nodes = from.children; 417 var curClass = 'c' + Date.now(); 418 while (nodes.length) { 420 node = document.adoptNode(node); 421 node.classList.add(curClass); 422 to.appendChild(node); 424 process_dates('.' + curClass); 427 function ajaxScrollInit(urlFormat, 433 var updateInitiated = false; 434 var container = document.querySelector(containerSelector); 436 function scrollHandler() { 437 if (updateInitiated) { 441 var scrollHeight = document.documentElement.scrollHeight; 442 var clientHeight = document.documentElement.clientHeight; 443 var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; 445 if (scrollHeight - (scrollTop + clientHeight) < 50) { 446 updateInitiated = true; 447 removeByClassName('scroll-loading-error'); 448 container.lastElementChild.classList.add('scroll-separator'); 452 'class': 'scroll-loading-info', 453 text: 'No more entries' 455 appendFormatHTML(container, messageFormat, message); 460 format(urlFormat, {next: nextPageVar}), 464 'class': 'scroll-loading', 467 appendFormatHTML(container, messageFormat, message); 469 function onsuccess(htmlText) { 470 var doc = docFromHTML(htmlText); 472 if (mode === 'graph') { 473 var graph = window.graph; 474 var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1]; 475 var data = JSON.parse(dataStr); 477 adoptChildren(doc.querySelector('#graphnodes'), container.querySelector('#graphnodes')); 480 adoptChildren(doc.querySelector(containerSelector), container); 483 nextPageVar = nextPageVarGet(htmlText); 485 function onerror(errorText) { 487 'class': 'scroll-loading-error', 488 text: 'Error: ' + errorText 490 appendFormatHTML(container, messageFormat, message); 492 function oncomplete() { 493 removeByClassName('scroll-loading'); 494 updateInitiated = false; 501 window.addEventListener('scroll', scrollHandler); 502 window.addEventListener('resize', scrollHandler); 506 function renderDiffOptsForm() { 507 // We use URLSearchParams for query string manipulation. Old browsers don't 509 if (!("URLSearchParams" in window)) { 513 var form = document.getElementById("diffopts-form"); 522 var urlParams = new window.URLSearchParams(window.location.search); 524 function updateAndRefresh(e) { 525 var checkbox = e.target; 526 var name = checkbox.id.substr(0, checkbox.id.indexOf("-")); 527 urlParams.set(name, checkbox.checked ? "1" : "0"); 528 window.location.search = urlParams.toString(); 531 var allChecked = form.getAttribute("data-ignorews") === "1"; 533 for (var i = 0; i < KEYS.length; i++) { 536 var checkbox = document.getElementById(key + "-checkbox"); 541 var currentValue = form.getAttribute("data-" + key); 542 checkbox.checked = currentValue !== "0"; 544 // ignorews implies ignorewsamount and ignorewseol. 545 if (allChecked && (key === "ignorewsamount" || key === "ignorewseol")) { 546 checkbox.checked = true; 547 checkbox.disabled = true; 550 checkbox.addEventListener("change", updateAndRefresh, false); 553 form.style.display = 'block'; 556 function addDiffStatToggle() { 557 var els = document.getElementsByClassName("diffstattoggle"); 559 for (var i = 0; i < els.length; i++) { 560 els[i].addEventListener("click", toggleDiffstat, false); 564 function addLineWrapToggle() { 565 var els = document.getElementsByClassName("linewraptoggle"); 567 for (var i = 0; i < els.length; i++) { 568 var nodes = els[i].getElementsByClassName("linewraplink"); 570 for (var j = 0; j < nodes.length; j++) { 571 nodes[j].addEventListener("click", toggleLinewrap, false); 576 document.addEventListener('DOMContentLoaded', function() {