diff templates/static/mercurial.js @ 0:dfc36e7ed22c

init
author Vadim Filimonov <fffilimonov@yandex.ru>
date Thu, 12 May 2022 13:51:59 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/static/mercurial.js	Thu May 12 13:51:59 2022 +0400
@@ -0,0 +1,580 @@
+// mercurial.js - JavaScript utility functions
+//
+// Rendering of branch DAGs on the client side
+// Display of elapsed time
+// Show or hide diffstat
+//
+// Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
+// Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
+//
+// derived from code written by Scott James Remnant <scott@ubuntu.com>
+// Copyright 2005 Canonical Ltd.
+//
+// This software may be used and distributed according to the terms
+// of the GNU General Public License, incorporated herein by reference.
+
+var colors = [
+	[ 1.0, 0.0, 0.0 ],
+	[ 1.0, 1.0, 0.0 ],
+	[ 0.0, 1.0, 0.0 ],
+	[ 0.0, 1.0, 1.0 ],
+	[ 0.0, 0.0, 1.0 ],
+	[ 1.0, 0.0, 1.0 ]
+];
+
+function Graph() {
+
+	this.canvas = document.getElementById('graph');
+	this.ctx = this.canvas.getContext('2d');
+	this.ctx.strokeStyle = 'rgb(0, 0, 0)';
+	this.ctx.fillStyle = 'rgb(0, 0, 0)';
+	this.bg = [0, 4];
+	this.cell = [2, 0];
+	this.columns = 0;
+
+}
+
+Graph.prototype = {
+	reset: function() {
+		this.bg = [0, 4];
+		this.cell = [2, 0];
+		this.columns = 0;
+	},
+
+	scale: function(height) {
+		this.bg_height = height;
+		this.box_size = Math.floor(this.bg_height / 1.2);
+		this.cell_height = this.box_size;
+	},
+
+	setColor: function(color, bg, fg) {
+
+		// Set the colour.
+		//
+		// If color is a string, expect an hexadecimal RGB
+		// value and apply it unchanged. If color is a number,
+		// pick a distinct colour based on an internal wheel;
+		// the bg parameter provides the value that should be
+		// assigned to the 'zero' colours and the fg parameter
+		// provides the multiplier that should be applied to
+		// the foreground colours.
+		var s;
+		if(typeof color === "string") {
+			s = "#" + color;
+		} else { //typeof color === "number"
+			color %= colors.length;
+			var red = (colors[color][0] * fg) || bg;
+			var green = (colors[color][1] * fg) || bg;
+			var blue = (colors[color][2] * fg) || bg;
+			red = Math.round(red * 255);
+			green = Math.round(green * 255);
+			blue = Math.round(blue * 255);
+			s = 'rgb(' + red + ', ' + green + ', ' + blue + ')';
+		}
+		this.ctx.strokeStyle = s;
+		this.ctx.fillStyle = s;
+		return s;
+
+	},
+
+	edge: function(x0, y0, x1, y1, color, width) {
+
+		this.setColor(color, 0.0, 0.65);
+		if(width >= 0)
+			 this.ctx.lineWidth = width;
+		this.ctx.beginPath();
+		this.ctx.moveTo(x0, y0);
+		this.ctx.lineTo(x1, y1);
+		this.ctx.stroke();
+
+	},
+
+	graphNodeCurrent: function(x, y, radius) {
+		this.ctx.lineWidth = 2;
+		this.ctx.beginPath();
+		this.ctx.arc(x, y, radius * 1.75, 0, Math.PI * 2, true);
+		this.ctx.stroke();
+	},
+
+	graphNodeClosing: function(x, y, radius) {
+		this.ctx.fillRect(x - radius, y - 1.5, radius * 2, 3);
+	},
+
+	graphNodeUnstable: function(x, y, radius) {
+		var x30 = radius * Math.cos(Math.PI / 6);
+		var y30 = radius * Math.sin(Math.PI / 6);
+		this.ctx.lineWidth = 2;
+		this.ctx.beginPath();
+		this.ctx.moveTo(x, y - radius);
+		this.ctx.lineTo(x, y + radius);
+		this.ctx.moveTo(x - x30, y - y30);
+		this.ctx.lineTo(x + x30, y + y30);
+		this.ctx.moveTo(x - x30, y + y30);
+		this.ctx.lineTo(x + x30, y - y30);
+		this.ctx.stroke();
+	},
+
+	graphNodeObsolete: function(x, y, radius) {
+		var p45 = radius * Math.cos(Math.PI / 4);
+		this.ctx.lineWidth = 3;
+		this.ctx.beginPath();
+		this.ctx.moveTo(x - p45, y - p45);
+		this.ctx.lineTo(x + p45, y + p45);
+		this.ctx.moveTo(x - p45, y + p45);
+		this.ctx.lineTo(x + p45, y - p45);
+		this.ctx.stroke();
+	},
+
+	graphNodeNormal: function(x, y, radius) {
+		this.ctx.beginPath();
+		this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
+		this.ctx.fill();
+	},
+
+	vertex: function(x, y, radius, color, parity, cur) {
+		this.ctx.save();
+		this.setColor(color, 0.25, 0.75);
+		if (cur.graphnode[0] === '@') {
+			this.graphNodeCurrent(x, y, radius);
+		}
+		switch (cur.graphnode.substr(-1)) {
+			case '_':
+				this.graphNodeClosing(x, y, radius);
+				break;
+			case '*':
+				this.graphNodeUnstable(x, y, radius);
+				break;
+			case 'x':
+				this.graphNodeObsolete(x, y, radius);
+				break;
+			default:
+				this.graphNodeNormal(x, y, radius);
+		}
+		this.ctx.restore();
+
+		var left = (this.bg_height - this.box_size) + (this.columns + 1) * this.box_size;
+		var item = document.querySelector('[data-node="' + cur.node + '"]');
+		if (item) {
+			item.style.paddingLeft = left + 'px';
+		}
+	},
+
+	render: function(data) {
+
+		var i, j, cur, line, start, end, color, x, y, x0, y0, x1, y1, column, radius;
+
+		var cols = 0;
+		for (i = 0; i < data.length; i++) {
+			cur = data[i];
+			for (j = 0; j < cur.edges.length; j++) {
+				line = cur.edges[j];
+				cols = Math.max(cols, line[0], line[1]);
+			}
+		}
+		this.canvas.width = (cols + 1) * this.bg_height;
+		this.canvas.height = (data.length + 1) * this.bg_height - 27;
+
+		for (i = 0; i < data.length; i++) {
+
+			var parity = i % 2;
+			this.cell[1] += this.bg_height;
+			this.bg[1] += this.bg_height;
+
+			cur = data[i];
+			var fold = false;
+
+			var prevWidth = this.ctx.lineWidth;
+			for (j = 0; j < cur.edges.length; j++) {
+
+				line = cur.edges[j];
+				start = line[0];
+				end = line[1];
+				color = line[2];
+				var width = line[3];
+				if(width < 0)
+					 width = prevWidth;
+				var branchcolor = line[4];
+				if(branchcolor)
+					color = branchcolor;
+
+				if (end > this.columns || start > this.columns) {
+					this.columns += 1;
+				}
+
+				if (start === this.columns && start > end) {
+					fold = true;
+				}
+
+				x0 = this.cell[0] + this.box_size * start + this.box_size / 2;
+				y0 = this.bg[1] - this.bg_height / 2;
+				x1 = this.cell[0] + this.box_size * end + this.box_size / 2;
+				y1 = this.bg[1] + this.bg_height / 2;
+
+				this.edge(x0, y0, x1, y1, color, width);
+
+			}
+			this.ctx.lineWidth = prevWidth;
+
+			// Draw the revision node in the right column
+
+			column = cur.vertex[0];
+			color = cur.vertex[1];
+
+			radius = this.box_size / 8;
+			x = this.cell[0] + this.box_size * column + this.box_size / 2;
+			y = this.bg[1] - this.bg_height / 2;
+			this.vertex(x, y, radius, color, parity, cur);
+
+			if (fold) this.columns -= 1;
+
+		}
+
+	}
+
+};
+
+
+function process_dates(parentSelector){
+
+	// derived from code from mercurial/templatefilter.py
+
+	var scales = {
+		'year':  365 * 24 * 60 * 60,
+		'month':  30 * 24 * 60 * 60,
+		'week':    7 * 24 * 60 * 60,
+		'day':    24 * 60 * 60,
+		'hour':   60 * 60,
+		'minute': 60,
+		'second': 1
+	};
+
+	function format(count, string){
+		var ret = count + ' ' + string;
+		if (count > 1){
+			ret = ret + 's';
+		}
+ 		return ret;
+ 	}
+
+	function shortdate(date){
+		var ret = date.getFullYear() + '-';
+		// getMonth() gives a 0-11 result
+		var month = date.getMonth() + 1;
+		if (month <= 9){
+			ret += '0' + month;
+		} else {
+			ret += month;
+		}
+		ret += '-';
+		var day = date.getDate();
+		if (day <= 9){
+			ret += '0' + day;
+		} else {
+			ret += day;
+		}
+		return ret;
+	}
+
+ 	function age(datestr){
+ 		var now = new Date();
+ 		var once = new Date(datestr);
+		if (isNaN(once.getTime())){
+			// parsing error
+			return datestr;
+		}
+
+		var delta = Math.floor((now.getTime() - once.getTime()) / 1000);
+
+		var future = false;
+		if (delta < 0){
+			future = true;
+			delta = -delta;
+			if (delta > (30 * scales.year)){
+				return "in the distant future";
+			}
+		}
+
+		if (delta > (2 * scales.year)){
+			return shortdate(once);
+		}
+
+		for (var unit in scales){
+			if (!scales.hasOwnProperty(unit)) { continue; }
+			var s = scales[unit];
+			var n = Math.floor(delta / s);
+			if ((n >= 2) || (s === 1)){
+				if (future){
+					return format(n, unit) + ' from now';
+				} else {
+					return format(n, unit) + ' ago';
+				}
+			}
+		}
+	}
+
+	var nodes = document.querySelectorAll((parentSelector || '') + ' .age');
+	var dateclass = new RegExp('\\bdate\\b');
+	for (var i=0; i<nodes.length; ++i){
+		var node = nodes[i];
+		var classes = node.className;
+		var agevalue = age(node.textContent);
+		if (dateclass.test(classes)){
+			// We want both: date + (age)
+			node.textContent += ' ('+agevalue+')';
+		} else {
+			node.title = node.textContent;
+			node.textContent = agevalue;
+		}
+	}
+}
+
+function toggleDiffstat(event) {
+    var curdetails = document.getElementById('diffstatdetails').style.display;
+    var curexpand = curdetails === 'none' ? 'inline' : 'none';
+    document.getElementById('diffstatdetails').style.display = curexpand;
+    document.getElementById('diffstatexpand').style.display = curdetails;
+    event.preventDefault();
+}
+
+function toggleLinewrap(event) {
+    function getLinewrap() {
+        var nodes = document.getElementsByClassName('sourcelines');
+        // if there are no such nodes, error is thrown here
+        return nodes[0].classList.contains('wrap');
+    }
+
+    function setLinewrap(enable) {
+        var nodes = document.getElementsByClassName('sourcelines');
+        var i;
+        for (i = 0; i < nodes.length; i++) {
+            if (enable) {
+                nodes[i].classList.add('wrap');
+            } else {
+                nodes[i].classList.remove('wrap');
+            }
+        }
+
+        var links = document.getElementsByClassName('linewraplink');
+        for (i = 0; i < links.length; i++) {
+            links[i].innerHTML = enable ? 'on' : 'off';
+        }
+    }
+
+    setLinewrap(!getLinewrap());
+    event.preventDefault();
+}
+
+function format(str, replacements) {
+    return str.replace(/%(\w+)%/g, function(match, p1) {
+        return String(replacements[p1]);
+    });
+}
+
+function makeRequest(url, method, onstart, onsuccess, onerror, oncomplete) {
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function() {
+        if (xhr.readyState === 4) {
+            try {
+                if (xhr.status === 200) {
+                    onsuccess(xhr.responseText);
+                } else {
+                    throw 'server error';
+                }
+            } catch (e) {
+                onerror(e);
+            } finally {
+                oncomplete();
+            }
+        }
+    };
+
+    xhr.open(method, url);
+    xhr.overrideMimeType("text/xhtml; charset=" + document.characterSet.toLowerCase());
+    xhr.send();
+    onstart();
+    return xhr;
+}
+
+function removeByClassName(className) {
+    var nodes = document.getElementsByClassName(className);
+    while (nodes.length) {
+        nodes[0].parentNode.removeChild(nodes[0]);
+    }
+}
+
+function docFromHTML(html) {
+    var doc = document.implementation.createHTMLDocument('');
+    doc.documentElement.innerHTML = html;
+    return doc;
+}
+
+function appendFormatHTML(element, formatStr, replacements) {
+    element.insertAdjacentHTML('beforeend', format(formatStr, replacements));
+}
+
+function adoptChildren(from, to) {
+    var nodes = from.children;
+    var curClass = 'c' + Date.now();
+    while (nodes.length) {
+        var node = nodes[0];
+        node = document.adoptNode(node);
+        node.classList.add(curClass);
+        to.appendChild(node);
+    }
+    process_dates('.' + curClass);
+}
+
+function ajaxScrollInit(urlFormat,
+                        nextPageVar,
+                        nextPageVarGet,
+                        containerSelector,
+                        messageFormat,
+                        mode) {
+    var updateInitiated = false;
+    var container = document.querySelector(containerSelector);
+
+    function scrollHandler() {
+        if (updateInitiated) {
+            return;
+        }
+
+        var scrollHeight = document.documentElement.scrollHeight;
+        var clientHeight = document.documentElement.clientHeight;
+        var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
+
+        if (scrollHeight - (scrollTop + clientHeight) < 50) {
+            updateInitiated = true;
+            removeByClassName('scroll-loading-error');
+            container.lastElementChild.classList.add('scroll-separator');
+
+            if (!nextPageVar) {
+                var message = {
+                    'class': 'scroll-loading-info',
+                    text: 'No more entries'
+                };
+                appendFormatHTML(container, messageFormat, message);
+                return;
+            }
+
+            makeRequest(
+                format(urlFormat, {next: nextPageVar}),
+                'GET',
+                function onstart() {
+                    var message = {
+                        'class': 'scroll-loading',
+                        text: 'Loading...'
+                    };
+                    appendFormatHTML(container, messageFormat, message);
+                },
+                function onsuccess(htmlText) {
+                    var doc = docFromHTML(htmlText);
+
+                    if (mode === 'graph') {
+                        var graph = window.graph;
+                        var dataStr = htmlText.match(/^\s*var data = (.*);$/m)[1];
+                        var data = JSON.parse(dataStr);
+                        graph.reset();
+                        adoptChildren(doc.querySelector('#graphnodes'), container.querySelector('#graphnodes'));
+                        graph.render(data);
+                    } else {
+                        adoptChildren(doc.querySelector(containerSelector), container);
+                    }
+
+                    nextPageVar = nextPageVarGet(htmlText);
+                },
+                function onerror(errorText) {
+                    var message = {
+                        'class': 'scroll-loading-error',
+                        text: 'Error: ' + errorText
+                    };
+                    appendFormatHTML(container, messageFormat, message);
+                },
+                function oncomplete() {
+                    removeByClassName('scroll-loading');
+                    updateInitiated = false;
+                    scrollHandler();
+                }
+            );
+        }
+    }
+
+    window.addEventListener('scroll', scrollHandler);
+    window.addEventListener('resize', scrollHandler);
+    scrollHandler();
+}
+
+function renderDiffOptsForm() {
+    // We use URLSearchParams for query string manipulation. Old browsers don't
+    // support this API.
+    if (!("URLSearchParams" in window)) {
+        return;
+    }
+
+    var form = document.getElementById("diffopts-form");
+
+    var KEYS = [
+        "ignorews",
+        "ignorewsamount",
+        "ignorewseol",
+        "ignoreblanklines",
+    ];
+
+    var urlParams = new window.URLSearchParams(window.location.search);
+
+    function updateAndRefresh(e) {
+        var checkbox = e.target;
+        var name = checkbox.id.substr(0, checkbox.id.indexOf("-"));
+        urlParams.set(name, checkbox.checked ? "1" : "0");
+        window.location.search = urlParams.toString();
+    }
+
+    var allChecked = form.getAttribute("data-ignorews") === "1";
+
+    for (var i = 0; i < KEYS.length; i++) {
+        var key = KEYS[i];
+
+        var checkbox = document.getElementById(key + "-checkbox");
+        if (!checkbox) {
+            continue;
+        }
+
+        var currentValue = form.getAttribute("data-" + key);
+        checkbox.checked = currentValue !== "0";
+
+        // ignorews implies ignorewsamount and ignorewseol.
+        if (allChecked && (key === "ignorewsamount" || key === "ignorewseol")) {
+            checkbox.checked = true;
+            checkbox.disabled = true;
+        }
+
+        checkbox.addEventListener("change", updateAndRefresh, false);
+    }
+
+    form.style.display = 'block';
+}
+
+function addDiffStatToggle() {
+    var els = document.getElementsByClassName("diffstattoggle");
+
+    for (var i = 0; i < els.length; i++) {
+        els[i].addEventListener("click", toggleDiffstat, false);
+    }
+}
+
+function addLineWrapToggle() {
+    var els = document.getElementsByClassName("linewraptoggle");
+
+    for (var i = 0; i < els.length; i++) {
+        var nodes = els[i].getElementsByClassName("linewraplink");
+
+        for (var j = 0; j < nodes.length; j++) {
+            nodes[j].addEventListener("click", toggleLinewrap, false);
+        }
+    }
+}
+
+document.addEventListener('DOMContentLoaded', function() {
+   process_dates();
+   addDiffStatToggle();
+   addLineWrapToggle();
+}, false);