##// END OF EJS Templates
export: split cmdutil.export() to single-file and maybe-multiple-files cases...
export: split cmdutil.export() to single-file and maybe-multiple-files cases Porting "hg export" to formatter is a bit hard because cmdutil.export() may append to files if the fntemplate is specified. This patch splits the hard part from the trivial case.

File last commit:

r37509:685ad41f default
r37618:7800ed2e default
Show More
followlines.js
286 lines | 10.6 KiB | application/javascript | JavascriptLexer
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 // followlines.js - JavaScript utilities for followlines UI
//
// Copyright 2017 Logilab SA <contact@logilab.fr>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
//** Install event listeners for line block selection and followlines action */
document.addEventListener('DOMContentLoaded', function() {
var sourcelines = document.getElementsByClassName('sourcelines')[0];
if (typeof sourcelines === 'undefined') {
return;
}
// URL to complement with "linerange" query parameter
var targetUri = sourcelines.dataset.logurl;
av6
hgweb: use strict equals, remove non-breaking space in followlines.js...
r35157 if (typeof targetUri === 'undefined') {
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 return;
}
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 // Tag of children of "sourcelines" element on which to add "line
// selection" style.
var selectableTag = sourcelines.dataset.selectabletag;
if (typeof selectableTag === 'undefined') {
return;
}
Denis Laxalde
hgweb: do not show "descending" link in followlines UI for filelog heads...
r32070 var isHead = parseInt(sourcelines.dataset.ishead || "0");
Denis Laxalde
hgweb: position the "followlines" box close to latest cursor position
r31849 //* position "element" on top-right of cursor */
function positionTopRight(element, event) {
var x = (event.clientX + 10) + 'px',
y = (event.clientY - 20) + 'px';
element.style.top = y;
element.style.left = x;
}
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 // retrieve all direct *selectable* children of class="sourcelines"
// element
var selectableElements = Array.prototype.filter.call(
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 sourcelines.children,
av6
hgweb: add missing semicolons to followlines.js...
r35027 function(x) { return x.tagName === selectableTag; });
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 var btnTitleStart = 'start following lines history from here';
var btnTitleEnd = 'terminate line block selection here';
//** return a <button> element with +/- spans */
function createButton() {
var btn = document.createElement('button');
btn.title = btnTitleStart;
btn.classList.add('btn-followlines');
var plusSpan = document.createElement('span');
plusSpan.classList.add('followlines-plus');
plusSpan.textContent = '+';
btn.appendChild(plusSpan);
var br = document.createElement('br');
btn.appendChild(br);
var minusSpan = document.createElement('span');
minusSpan.classList.add('followlines-minus');
minusSpan.textContent = '−';
btn.appendChild(minusSpan);
return btn;
}
// extend DOM with CSS class for selection highlight and action buttons
av6
hgweb: add missing semicolons to followlines.js...
r35027 var followlinesButtons = [];
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 for (var i = 0; i < selectableElements.length; i++) {
selectableElements[i].classList.add('followlines-select');
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 var btn = createButton();
followlinesButtons.push(btn);
// insert the <button> as child of `selectableElements[i]` unless the
// latter has itself a child with a "followlines-btn-parent" class
// (annotate view)
var btnSupportElm = selectableElements[i];
var childSupportElms = btnSupportElm.getElementsByClassName(
'followlines-btn-parent');
if ( childSupportElms.length > 0 ) {
btnSupportElm = childSupportElms[0];
}
av6
hgweb: insert followlines buttons before any children, including text nodes...
r37509 var refNode = btnSupportElm.childNodes[0]; // node to insert <button> before
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 btnSupportElm.insertBefore(btn, refNode);
}
// ** re-initialize followlines buttons */
function resetButtons() {
for (var i = 0; i < followlinesButtons.length; i++) {
var btn = followlinesButtons[i];
btn.title = btnTitleStart;
btn.classList.remove('btn-followlines-end');
btn.classList.remove('btn-followlines-hidden');
}
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 }
var lineSelectedCSSClass = 'followlines-selected';
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 //** add CSS class on selectable elements in `from`-`to` line range */
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 function addSelectedCSSClass(from, to) {
for (var i = from; i <= to; i++) {
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 selectableElements[i].classList.add(lineSelectedCSSClass);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 }
}
//** remove CSS class from previously selected lines */
function removeSelectedCSSClass() {
var elements = sourcelines.getElementsByClassName(
lineSelectedCSSClass);
while (elements.length) {
elements[0].classList.remove(lineSelectedCSSClass);
}
}
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 // ** return the element of type "selectableTag" parent of `element` */
function selectableParent(element) {
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 var parent = element.parentElement;
if (parent === null) {
return null;
}
av6
hgweb: use strict equals, remove non-breaking space in followlines.js...
r35157 if (element.tagName === selectableTag && parent.isSameNode(sourcelines)) {
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 return element;
}
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 return selectableParent(parent);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 }
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 // ** update buttons title and style upon first click */
function updateButtons(selectable) {
for (var i = 0; i < followlinesButtons.length; i++) {
var btn = followlinesButtons[i];
btn.title = btnTitleEnd;
btn.classList.add('btn-followlines-end');
}
// on clicked button, change title to "cancel"
var clicked = selectable.getElementsByClassName('btn-followlines')[0];
clicked.title = 'cancel';
clicked.classList.remove('btn-followlines-end');
}
//** add `listener` on "click" event for all `followlinesButtons` */
function buttonsAddEventListener(listener) {
for (var i = 0; i < followlinesButtons.length; i++) {
followlinesButtons[i].addEventListener('click', listener);
}
}
//** remove `listener` on "click" event for all `followlinesButtons` */
function buttonsRemoveEventListener(listener) {
for (var i = 0; i < followlinesButtons.length; i++) {
followlinesButtons[i].removeEventListener('click', listener);
}
}
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 //** event handler for "click" on the first line of a block */
function lineSelectStart(e) {
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 var startElement = selectableParent(e.target.parentElement);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 if (startElement === null) {
Denis Laxalde
hgweb: parameterize the tag name of elements holding followlines selection...
r32993 // not a "selectable" element (maybe <a>): abort, keeping event
// listener registered for other click with a "selectable" target
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 return;
}
Denis Laxalde
hgweb: add a floating tooltip to invite on followlines action...
r31848
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 // update button tooltip text and CSS
updateButtons(startElement);
Denis Laxalde
hgweb: add a floating tooltip to invite on followlines action...
r31848
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 var startId = parseInt(startElement.id.slice(1));
startElement.classList.add(lineSelectedCSSClass); // CSS
// remove this event listener
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsRemoveEventListener(lineSelectStart);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785
//** event handler for "click" on the last line of the block */
function lineSelectEnd(e) {
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 var endElement = selectableParent(e.target.parentElement);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 if (endElement === null) {
// not a <span> (maybe <a>): abort, keeping event listener
// registered for other click with <span> target
return;
}
// remove this event listener
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsRemoveEventListener(lineSelectEnd);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 // reset button tooltip text
resetButtons();
Denis Laxalde
hgweb: add a floating tooltip to invite on followlines action...
r31848
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 // compute line range (startId, endId)
var endId = parseInt(endElement.id.slice(1));
av6
hgweb: use strict equals, remove non-breaking space in followlines.js...
r35157 if (endId === startId) {
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 // clicked twice the same line, cancel and reset initial state
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 // (CSS, event listener for selection start)
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 removeSelectedCSSClass();
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsAddEventListener(lineSelectStart);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 return;
}
var inviteElement = endElement;
if (endId < startId) {
var tmp = endId;
endId = startId;
startId = tmp;
inviteElement = startElement;
}
addSelectedCSSClass(startId - 1, endId -1); // CSS
// append the <div id="followlines"> element to last line of the
// selection block
Denis Laxalde
hgweb: do not show "descending" link in followlines UI for filelog heads...
r32070 var divAndButton = followlinesBox(targetUri, startId, endId, isHead);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 var div = divAndButton[0],
button = divAndButton[1];
inviteElement.appendChild(div);
Denis Laxalde
hgweb: position the "followlines" box close to latest cursor position
r31849 // set position close to cursor (top-right)
positionTopRight(div, e);
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 // hide all buttons
for (var i = 0; i < followlinesButtons.length; i++) {
followlinesButtons[i].classList.add('btn-followlines-hidden');
}
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785
//** event handler for cancelling selection */
function cancel() {
// remove invite box
div.parentNode.removeChild(div);
// restore initial event listeners
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsAddEventListener(lineSelectStart);
buttonsRemoveEventListener(cancel);
for (var i = 0; i < followlinesButtons.length; i++) {
followlinesButtons[i].classList.remove('btn-followlines-hidden');
}
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 // remove styles on selected lines
removeSelectedCSSClass();
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 resetButtons();
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 }
// bind cancel event to click on <button>
button.addEventListener('click', cancel);
// as well as on an click on any source line
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsAddEventListener(cancel);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 }
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsAddEventListener(lineSelectEnd);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785
}
Denis Laxalde
hgweb: re-implement followlines UI selection using buttons...
r33390 buttonsAddEventListener(lineSelectStart);
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785
//** return a <div id="followlines"> and inner cancel <button> elements */
Denis Laxalde
hgweb: do not show "descending" link in followlines UI for filelog heads...
r32070 function followlinesBox(targetUri, fromline, toline, isHead) {
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 // <div id="followlines">
var div = document.createElement('div');
div.id = 'followlines';
// <div class="followlines-cancel">
var buttonDiv = document.createElement('div');
buttonDiv.classList.add('followlines-cancel');
// <button>x</button>
var button = document.createElement('button');
button.textContent = 'x';
buttonDiv.appendChild(button);
div.appendChild(buttonDiv);
// <div class="followlines-link">
var aDiv = document.createElement('div');
aDiv.classList.add('followlines-link');
Denis Laxalde
hgweb: add a link to followlines in descending direction...
r31940 aDiv.textContent = 'follow history of lines ' + fromline + ':' + toline + ':';
var linesep = document.createElement('br');
aDiv.appendChild(linesep);
// link to "ascending" followlines
var aAsc = document.createElement('a');
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 var url = targetUri + '?patch=&linerange=' + fromline + ':' + toline;
Denis Laxalde
hgweb: add a link to followlines in descending direction...
r31940 aAsc.setAttribute('href', url);
Denis Laxalde
hgweb: change text of followlines links to "older / newer"...
r32071 aAsc.textContent = 'older';
Denis Laxalde
hgweb: add a link to followlines in descending direction...
r31940 aDiv.appendChild(aAsc);
Denis Laxalde
hgweb: do not show "descending" link in followlines UI for filelog heads...
r32070
if (!isHead) {
var sep = document.createTextNode(' / ');
aDiv.appendChild(sep);
// link to "descending" followlines
var aDesc = document.createElement('a');
aDesc.setAttribute('href', url + '&descend=');
Denis Laxalde
hgweb: change text of followlines links to "older / newer"...
r32071 aDesc.textContent = 'newer';
Denis Laxalde
hgweb: do not show "descending" link in followlines UI for filelog heads...
r32070 aDiv.appendChild(aDesc);
}
Denis Laxalde
hgweb: add a link to followlines in descending direction...
r31940
Denis Laxalde
hgweb: rename linerangelog.js as followlines.js...
r31785 div.appendChild(aDiv);
return [div, button];
}
}, false);