##// END OF EJS Templates
hgweb: change text of followlines links to "older / newer"...
Denis Laxalde -
r32071:1cf0b651 stable
parent child Browse files
Show More
@@ -1,234 +1,234 b''
1 // followlines.js - JavaScript utilities for followlines UI
1 // followlines.js - JavaScript utilities for followlines UI
2 //
2 //
3 // Copyright 2017 Logilab SA <contact@logilab.fr>
3 // Copyright 2017 Logilab SA <contact@logilab.fr>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //** Install event listeners for line block selection and followlines action */
8 //** Install event listeners for line block selection and followlines action */
9 document.addEventListener('DOMContentLoaded', function() {
9 document.addEventListener('DOMContentLoaded', function() {
10 var sourcelines = document.getElementsByClassName('sourcelines')[0];
10 var sourcelines = document.getElementsByClassName('sourcelines')[0];
11 if (typeof sourcelines === 'undefined') {
11 if (typeof sourcelines === 'undefined') {
12 return;
12 return;
13 }
13 }
14 // URL to complement with "linerange" query parameter
14 // URL to complement with "linerange" query parameter
15 var targetUri = sourcelines.dataset.logurl;
15 var targetUri = sourcelines.dataset.logurl;
16 if (typeof targetUri === 'undefined')Β {
16 if (typeof targetUri === 'undefined')Β {
17 return;
17 return;
18 }
18 }
19
19
20 var isHead = parseInt(sourcelines.dataset.ishead || "0");
20 var isHead = parseInt(sourcelines.dataset.ishead || "0");
21
21
22 // tooltip to invite on lines selection
22 // tooltip to invite on lines selection
23 var tooltip = document.createElement('div');
23 var tooltip = document.createElement('div');
24 tooltip.id = 'followlines-tooltip';
24 tooltip.id = 'followlines-tooltip';
25 tooltip.classList.add('hidden');
25 tooltip.classList.add('hidden');
26 var initTooltipText = 'click to start following lines history from here';
26 var initTooltipText = 'click to start following lines history from here';
27 tooltip.textContent = initTooltipText;
27 tooltip.textContent = initTooltipText;
28 sourcelines.appendChild(tooltip);
28 sourcelines.appendChild(tooltip);
29
29
30 //* position "element" on top-right of cursor */
30 //* position "element" on top-right of cursor */
31 function positionTopRight(element, event) {
31 function positionTopRight(element, event) {
32 var x = (event.clientX + 10) + 'px',
32 var x = (event.clientX + 10) + 'px',
33 y = (event.clientY - 20) + 'px';
33 y = (event.clientY - 20) + 'px';
34 element.style.top = y;
34 element.style.top = y;
35 element.style.left = x;
35 element.style.left = x;
36 }
36 }
37
37
38 var tooltipTimeoutID;
38 var tooltipTimeoutID;
39 //* move the "tooltip" with cursor (top-right) and show it after 1s */
39 //* move the "tooltip" with cursor (top-right) and show it after 1s */
40 function moveAndShowTooltip(e) {
40 function moveAndShowTooltip(e) {
41 if (typeof tooltipTimeoutID !== 'undefined') {
41 if (typeof tooltipTimeoutID !== 'undefined') {
42 // avoid accumulation of timeout callbacks (blinking)
42 // avoid accumulation of timeout callbacks (blinking)
43 window.clearTimeout(tooltipTimeoutID);
43 window.clearTimeout(tooltipTimeoutID);
44 }
44 }
45 tooltip.classList.add('hidden');
45 tooltip.classList.add('hidden');
46 positionTopRight(tooltip, e);
46 positionTopRight(tooltip, e);
47 tooltipTimeoutID = window.setTimeout(function() {
47 tooltipTimeoutID = window.setTimeout(function() {
48 tooltip.classList.remove('hidden');
48 tooltip.classList.remove('hidden');
49 }, 1000);
49 }, 1000);
50 }
50 }
51
51
52 // on mousemove, show tooltip close to cursor position
52 // on mousemove, show tooltip close to cursor position
53 sourcelines.addEventListener('mousemove', moveAndShowTooltip);
53 sourcelines.addEventListener('mousemove', moveAndShowTooltip);
54
54
55 // retrieve all direct <span> children of <pre class="sourcelines">
55 // retrieve all direct <span> children of <pre class="sourcelines">
56 var spans = Array.prototype.filter.call(
56 var spans = Array.prototype.filter.call(
57 sourcelines.children,
57 sourcelines.children,
58 function(x) { return x.tagName === 'SPAN' });
58 function(x) { return x.tagName === 'SPAN' });
59
59
60 // add a "followlines-select" class to change cursor type in CSS
60 // add a "followlines-select" class to change cursor type in CSS
61 for (var i = 0; i < spans.length; i++) {
61 for (var i = 0; i < spans.length; i++) {
62 spans[i].classList.add('followlines-select');
62 spans[i].classList.add('followlines-select');
63 }
63 }
64
64
65 var lineSelectedCSSClass = 'followlines-selected';
65 var lineSelectedCSSClass = 'followlines-selected';
66
66
67 //** add CSS class on <span> element in `from`-`to` line range */
67 //** add CSS class on <span> element in `from`-`to` line range */
68 function addSelectedCSSClass(from, to) {
68 function addSelectedCSSClass(from, to) {
69 for (var i = from; i <= to; i++) {
69 for (var i = from; i <= to; i++) {
70 spans[i].classList.add(lineSelectedCSSClass);
70 spans[i].classList.add(lineSelectedCSSClass);
71 }
71 }
72 }
72 }
73
73
74 //** remove CSS class from previously selected lines */
74 //** remove CSS class from previously selected lines */
75 function removeSelectedCSSClass() {
75 function removeSelectedCSSClass() {
76 var elements = sourcelines.getElementsByClassName(
76 var elements = sourcelines.getElementsByClassName(
77 lineSelectedCSSClass);
77 lineSelectedCSSClass);
78 while (elements.length) {
78 while (elements.length) {
79 elements[0].classList.remove(lineSelectedCSSClass);
79 elements[0].classList.remove(lineSelectedCSSClass);
80 }
80 }
81 }
81 }
82
82
83 // ** return the <span> element parent of `element` */
83 // ** return the <span> element parent of `element` */
84 function findParentSpan(element) {
84 function findParentSpan(element) {
85 var parent = element.parentElement;
85 var parent = element.parentElement;
86 if (parent === null) {
86 if (parent === null) {
87 return null;
87 return null;
88 }
88 }
89 if (element.tagName == 'SPAN' && parent.isSameNode(sourcelines)) {
89 if (element.tagName == 'SPAN' && parent.isSameNode(sourcelines)) {
90 return element;
90 return element;
91 }
91 }
92 return findParentSpan(parent);
92 return findParentSpan(parent);
93 }
93 }
94
94
95 //** event handler for "click" on the first line of a block */
95 //** event handler for "click" on the first line of a block */
96 function lineSelectStart(e) {
96 function lineSelectStart(e) {
97 var startElement = findParentSpan(e.target);
97 var startElement = findParentSpan(e.target);
98 if (startElement === null) {
98 if (startElement === null) {
99 // not a <span> (maybe <a>): abort, keeping event listener
99 // not a <span> (maybe <a>): abort, keeping event listener
100 // registered for other click with <span> target
100 // registered for other click with <span> target
101 return;
101 return;
102 }
102 }
103
103
104 // update tooltip text
104 // update tooltip text
105 tooltip.textContent = 'click again to terminate line block selection here';
105 tooltip.textContent = 'click again to terminate line block selection here';
106
106
107 var startId = parseInt(startElement.id.slice(1));
107 var startId = parseInt(startElement.id.slice(1));
108 startElement.classList.add(lineSelectedCSSClass); // CSS
108 startElement.classList.add(lineSelectedCSSClass); // CSS
109
109
110 // remove this event listener
110 // remove this event listener
111 sourcelines.removeEventListener('click', lineSelectStart);
111 sourcelines.removeEventListener('click', lineSelectStart);
112
112
113 //** event handler for "click" on the last line of the block */
113 //** event handler for "click" on the last line of the block */
114 function lineSelectEnd(e) {
114 function lineSelectEnd(e) {
115 var endElement = findParentSpan(e.target);
115 var endElement = findParentSpan(e.target);
116 if (endElement === null) {
116 if (endElement === null) {
117 // not a <span> (maybe <a>): abort, keeping event listener
117 // not a <span> (maybe <a>): abort, keeping event listener
118 // registered for other click with <span> target
118 // registered for other click with <span> target
119 return;
119 return;
120 }
120 }
121
121
122 // remove this event listener
122 // remove this event listener
123 sourcelines.removeEventListener('click', lineSelectEnd);
123 sourcelines.removeEventListener('click', lineSelectEnd);
124
124
125 // hide tooltip and disable motion tracking
125 // hide tooltip and disable motion tracking
126 tooltip.classList.add('hidden');
126 tooltip.classList.add('hidden');
127 sourcelines.removeEventListener('mousemove', moveAndShowTooltip);
127 sourcelines.removeEventListener('mousemove', moveAndShowTooltip);
128 window.clearTimeout(tooltipTimeoutID);
128 window.clearTimeout(tooltipTimeoutID);
129
129
130 //* restore initial "tooltip" state */
130 //* restore initial "tooltip" state */
131 function restoreTooltip() {
131 function restoreTooltip() {
132 tooltip.textContent = initTooltipText;
132 tooltip.textContent = initTooltipText;
133 sourcelines.addEventListener('mousemove', moveAndShowTooltip);
133 sourcelines.addEventListener('mousemove', moveAndShowTooltip);
134 }
134 }
135
135
136 // compute line range (startId, endId)
136 // compute line range (startId, endId)
137 var endId = parseInt(endElement.id.slice(1));
137 var endId = parseInt(endElement.id.slice(1));
138 if (endId == startId) {
138 if (endId == startId) {
139 // clicked twice the same line, cancel and reset initial state
139 // clicked twice the same line, cancel and reset initial state
140 // (CSS, event listener for selection start, tooltip)
140 // (CSS, event listener for selection start, tooltip)
141 removeSelectedCSSClass();
141 removeSelectedCSSClass();
142 sourcelines.addEventListener('click', lineSelectStart);
142 sourcelines.addEventListener('click', lineSelectStart);
143 restoreTooltip();
143 restoreTooltip();
144 return;
144 return;
145 }
145 }
146 var inviteElement = endElement;
146 var inviteElement = endElement;
147 if (endId < startId) {
147 if (endId < startId) {
148 var tmp = endId;
148 var tmp = endId;
149 endId = startId;
149 endId = startId;
150 startId = tmp;
150 startId = tmp;
151 inviteElement = startElement;
151 inviteElement = startElement;
152 }
152 }
153
153
154 addSelectedCSSClass(startId - 1, endId -1); // CSS
154 addSelectedCSSClass(startId - 1, endId -1); // CSS
155
155
156 // append the <div id="followlines"> element to last line of the
156 // append the <div id="followlines"> element to last line of the
157 // selection block
157 // selection block
158 var divAndButton = followlinesBox(targetUri, startId, endId, isHead);
158 var divAndButton = followlinesBox(targetUri, startId, endId, isHead);
159 var div = divAndButton[0],
159 var div = divAndButton[0],
160 button = divAndButton[1];
160 button = divAndButton[1];
161 inviteElement.appendChild(div);
161 inviteElement.appendChild(div);
162 // set position close to cursor (top-right)
162 // set position close to cursor (top-right)
163 positionTopRight(div, e);
163 positionTopRight(div, e);
164
164
165 //** event handler for cancelling selection */
165 //** event handler for cancelling selection */
166 function cancel() {
166 function cancel() {
167 // remove invite box
167 // remove invite box
168 div.parentNode.removeChild(div);
168 div.parentNode.removeChild(div);
169 // restore initial event listeners
169 // restore initial event listeners
170 sourcelines.addEventListener('click', lineSelectStart);
170 sourcelines.addEventListener('click', lineSelectStart);
171 sourcelines.removeEventListener('click', cancel);
171 sourcelines.removeEventListener('click', cancel);
172 // remove styles on selected lines
172 // remove styles on selected lines
173 removeSelectedCSSClass();
173 removeSelectedCSSClass();
174 // restore tooltip element
174 // restore tooltip element
175 restoreTooltip();
175 restoreTooltip();
176 }
176 }
177
177
178 // bind cancel event to click on <button>
178 // bind cancel event to click on <button>
179 button.addEventListener('click', cancel);
179 button.addEventListener('click', cancel);
180 // as well as on an click on any source line
180 // as well as on an click on any source line
181 sourcelines.addEventListener('click', cancel);
181 sourcelines.addEventListener('click', cancel);
182 }
182 }
183
183
184 sourcelines.addEventListener('click', lineSelectEnd);
184 sourcelines.addEventListener('click', lineSelectEnd);
185
185
186 }
186 }
187
187
188 sourcelines.addEventListener('click', lineSelectStart);
188 sourcelines.addEventListener('click', lineSelectStart);
189
189
190 //** return a <div id="followlines"> and inner cancel <button> elements */
190 //** return a <div id="followlines"> and inner cancel <button> elements */
191 function followlinesBox(targetUri, fromline, toline, isHead) {
191 function followlinesBox(targetUri, fromline, toline, isHead) {
192 // <div id="followlines">
192 // <div id="followlines">
193 var div = document.createElement('div');
193 var div = document.createElement('div');
194 div.id = 'followlines';
194 div.id = 'followlines';
195
195
196 // <div class="followlines-cancel">
196 // <div class="followlines-cancel">
197 var buttonDiv = document.createElement('div');
197 var buttonDiv = document.createElement('div');
198 buttonDiv.classList.add('followlines-cancel');
198 buttonDiv.classList.add('followlines-cancel');
199
199
200 // <button>x</button>
200 // <button>x</button>
201 var button = document.createElement('button');
201 var button = document.createElement('button');
202 button.textContent = 'x';
202 button.textContent = 'x';
203 buttonDiv.appendChild(button);
203 buttonDiv.appendChild(button);
204 div.appendChild(buttonDiv);
204 div.appendChild(buttonDiv);
205
205
206 // <div class="followlines-link">
206 // <div class="followlines-link">
207 var aDiv = document.createElement('div');
207 var aDiv = document.createElement('div');
208 aDiv.classList.add('followlines-link');
208 aDiv.classList.add('followlines-link');
209 aDiv.textContent = 'follow history of lines ' + fromline + ':' + toline + ':';
209 aDiv.textContent = 'follow history of lines ' + fromline + ':' + toline + ':';
210 var linesep = document.createElement('br');
210 var linesep = document.createElement('br');
211 aDiv.appendChild(linesep);
211 aDiv.appendChild(linesep);
212 // link to "ascending" followlines
212 // link to "ascending" followlines
213 var aAsc = document.createElement('a');
213 var aAsc = document.createElement('a');
214 var url = targetUri + '?patch=&linerange=' + fromline + ':' + toline;
214 var url = targetUri + '?patch=&linerange=' + fromline + ':' + toline;
215 aAsc.setAttribute('href', url);
215 aAsc.setAttribute('href', url);
216 aAsc.textContent = 'ascending';
216 aAsc.textContent = 'older';
217 aDiv.appendChild(aAsc);
217 aDiv.appendChild(aAsc);
218
218
219 if (!isHead) {
219 if (!isHead) {
220 var sep = document.createTextNode(' / ');
220 var sep = document.createTextNode(' / ');
221 aDiv.appendChild(sep);
221 aDiv.appendChild(sep);
222 // link to "descending" followlines
222 // link to "descending" followlines
223 var aDesc = document.createElement('a');
223 var aDesc = document.createElement('a');
224 aDesc.setAttribute('href', url + '&descend=');
224 aDesc.setAttribute('href', url + '&descend=');
225 aDesc.textContent = 'descending';
225 aDesc.textContent = 'newer';
226 aDiv.appendChild(aDesc);
226 aDiv.appendChild(aDesc);
227 }
227 }
228
228
229 div.appendChild(aDiv);
229 div.appendChild(aDiv);
230
230
231 return [div, button];
231 return [div, button];
232 }
232 }
233
233
234 }, false);
234 }, false);
General Comments 0
You need to be logged in to leave comments. Login now