blob: bc2ba657d411d7f35a127dec6f5b38538b6ab4e6 [file] [log] [blame]
Andy Greenfe549182018-06-21 16:56:36 +08001/* cgit.css: javacript functions for cgit
2 *
3 * Copyright (C) 2006-2018 cgit Development Team <cgit@lists.zx2c4.com>
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9(function () {
10
Andy Greene96af4a2018-06-24 10:31:13 +080011var burger, menu_popup;
12
Andy Greenfe549182018-06-21 16:56:36 +080013function collect_offsetTop(e1)
14{
15 var t = 0;
16
17 while (e1) {
18 if (e1.offsetTop)
19 t += e1.offsetTop;
20 e1 = e1.offsetParent;
21 }
22
23 return t;
24}
25
26function find_parent_of_type(e, type)
27{
28 while (e.tagName.toLowerCase() != type)
29 e = e.parentNode;
30
31 return e;
32}
33
Andy Greencbec7752018-06-29 09:05:46 +080034function parse_hashurl_start(h)
35{
36 return parseInt(h.substring(2));
37}
38
39function parse_hashurl_end(h, start)
40{
41 var t = h.indexOf("-"), e = start;
42
43 if (t >= 1)
44 e = parseInt(h.substring(t + 1));
45
46 if (e < start)
47 e = start;
48
49 return e;
50}
51
52
Andy Greene96af4a2018-06-24 10:31:13 +080053/*
54 * This creates an absolute div as a child of the content table.
55 * It's horizontally and vertically aligned and sized according
56 * to the #URL information like #n123-456
57 *
58 * If the highlight div already exists, it's removed and remade.
59 */
60
61function line_range_highlight(do_burger)
Andy Greenfe549182018-06-21 16:56:36 +080062{
63 var h = window.location.hash, l1 = 0, l2 = 0, e, t;
64
Andy Green1915feb2018-06-23 06:07:45 +080065 e = document.getElementById("cgit-line-range");
66 if (e) {
67 l1 = e.l1;
68 while (l1 <= e.l2) {
69 var e1;
70 e1 = document.getElementById('n' + l1++);
Andy Greene96af4a2018-06-24 10:31:13 +080071 e1.classList.remove(
72 'selected-line-link-highlight');
Andy Green1915feb2018-06-23 06:07:45 +080073 }
74
75 e.remove();
76 }
77
Andy Greencbec7752018-06-29 09:05:46 +080078 l1 = parse_hashurl_start(h);
Andy Greenfe549182018-06-21 16:56:36 +080079 if (!l1)
80 return;
81
Andy Greencbec7752018-06-29 09:05:46 +080082 l2 = parse_hashurl_end(h, l1);
Andy Greenfe549182018-06-21 16:56:36 +080083
Andy Green0fcc4502018-06-23 06:41:47 +080084 var lh, etable, etr, de, n, hl, v;
Andy Greenfe549182018-06-21 16:56:36 +080085
86 e = document.getElementById('n' + l1);
87 if (!e)
88 return;
89
Andy Greene96af4a2018-06-24 10:31:13 +080090 if (do_burger)
91 burger_create(e);
92
Andy Greenfe549182018-06-21 16:56:36 +080093 de = document.createElement("DIV");
94
95 de.className = "selected-lines";
96 de.style.bottom = e.style.bottom;
97 de.style.top = collect_offsetTop(e) + 'px';
Andy Green1915feb2018-06-23 06:07:45 +080098 de.id = "cgit-line-range";
Andy Greenfe549182018-06-21 16:56:36 +080099 de.l1 = l1;
100 de.l2 = l2;
101
102 /* we will tack the highlight div at the parent tr */
103 etr = find_parent_of_type(e, "tr");
104
105 de.style.width = etr.offsetWidth + 'px';
106
Andy Greene96af4a2018-06-24 10:31:13 +0800107 /* the table is offset from the left, the highlight needs to follow it */
Andy Greenfe549182018-06-21 16:56:36 +0800108 etable = find_parent_of_type(etr, "table");
Andy Greenfe549182018-06-21 16:56:36 +0800109 de.style.left = etable.offsetLeft + 'px';
110 de.style.height = ((l2 - l1 + 1) * e.offsetHeight) + 'px';
111
112 etr.insertBefore(de, etr.firstChild);
113
114 setTimeout(function() {
115 de.style.backgroundColor = "rgba(255, 255, 0, 0.2)";
116 }, 1);
117
118 n = l1;
119 while (n <= l2)
Andy Greene96af4a2018-06-24 10:31:13 +0800120 document.getElementById('n' + n++).classList.add(
121 'selected-line-link-highlight');
Andy Greenfe549182018-06-21 16:56:36 +0800122
Andy Green0fcc4502018-06-23 06:41:47 +0800123 hl = (window.innerHeight / (e.offsetHeight + 1));
124 v = (l1 + ((l2 - l1) / 2)) - (hl / 2);
125 if (v > l1)
126 v = l1;
127 if (v < 1)
128 v = 1;
129
130 t = document.getElementById('n' + Math.round(v));
131 if (!t)
132 t = e;
133
134 t.scrollIntoView(true);
Andy Greenfe549182018-06-21 16:56:36 +0800135}
136
Andy Greene96af4a2018-06-24 10:31:13 +0800137function copy_clipboard(value)
138{
139 var inp = document.createElement("textarea");
140 var e = document.getElementById("linenumbers");
Andy Green91984092018-06-24 09:08:40 +0800141
Andy Greene96af4a2018-06-24 10:31:13 +0800142 inp.type = "text";
143 inp.value = value;
144 /* hidden style stops it working for clipboard */
145 inp.setAttribute('readonly', '');
146 inp.style.position = "absolute";
147 inp.style.left = "-1000px";
148
149 e.appendChild(inp);
150
151 inp.select();
152
153 document.execCommand("copy");
154
155 inp.remove();
156}
157
Andy Greencbec7752018-06-29 09:05:46 +0800158/* we want to copy plain text for a line range */
159
160function copy_text(elem, l1, l2)
161{
162 var tc = elem.textContent.split('\n'), s = "";
163
164 l1 --;
165
166 while (l1 < l2)
167 s += tc[l1++] + '\n';
168
169 copy_clipboard(s);
170}
171
172
Andy Greene96af4a2018-06-24 10:31:13 +0800173/*
174 * An element in the popup menu was clicked, perform the appropriate action
175 */
176function mi_click(e) {
Andy Greencbec7752018-06-29 09:05:46 +0800177 var u, n, l1, l2, el;
Andy Greene96af4a2018-06-24 10:31:13 +0800178
Andy Green91984092018-06-24 09:08:40 +0800179 e.stopPropagation();
180 e.preventDefault();
181
Andy Greene96af4a2018-06-24 10:31:13 +0800182 switch (e.target.id) {
183 case "mi-c-line":
Andy Greencbec7752018-06-29 09:05:46 +0800184 l1 = parse_hashurl_start(window.location.hash);
185 l2 = parse_hashurl_end(window.location.hash, l1);
186 el = document.getElementsByClassName("highlight")[0].firstChild;
187 copy_text(el, l1, l2);
Andy Greene96af4a2018-06-24 10:31:13 +0800188 break;
189 case "mi-c-link":
190 copy_clipboard(window.location.href);
191 break;
192 case "mi-c-blame":
193 u = window.location.href;
194 t = u.indexOf("/tree/");
195 if (t)
196 window.location = u.substring(0, t) + "/blame/" +
197 u.substring(t + 6);
198 break;
199 case "mi-c-tree":
200 u = window.location.href;
201 t = u.indexOf("/blame/");
202 if (t)
203 window.location = u.substring(0, t) + "/tree/" +
204 u.substring(t + 7);
205 break;
206 }
207
208 if (!menu_popup)
209 return;
210
211 menu_popup.remove();
212 menu_popup = null;
213}
214
215/* We got a click on the (***) burger menu */
216
217function burger_click(e) {
218 var e1 = e, etable, d = new Date, s = "", n, is_blame,
219 ar = new Array("mi-c-line", "mi-c-link", "mi-c-blame", "mi-c-tree"),
220 an = new Array("Copy Lines", "Copy Link",
221 "View Blame", /* 2: shown in /tree/ */
222 "Remove Blame" /* 3: shown in /blame/ */);
223
224 e.preventDefault();
225
226 if (menu_popup) {
227 menu_popup.remove();
228 menu_popup = null;
229
230 return;
231 }
232
233 /*
234 * Create the popup menu
235 */
236
237 is_blame = !!document.getElementsByClassName("hashes").length;
238
239 menu_popup = document.createElement("DIV");
240 menu_popup.className = "popup-menu";
241 menu_popup.style.top = collect_offsetTop(e1) + e.offsetHeight + "px";
242
243 s = "<ul id='menu-ul'>";
244 for (n = 0; n < an.length; n++)
245 if (n < 2 || is_blame == (n == 3))
246 s += "<li id='" + ar[n] + "' tabindex='" + n + "'>" +
247 an[n] + "</li>";
248
249 menu_popup.innerHTML = s;
250
251 burger.insertBefore(menu_popup, null);
252
253 document.getElementById(ar[0]).focus();
254 for (n = 0; n < an.length; n++)
255 if (n < 2 || is_blame == (n == 3))
256 document.getElementById(ar[n]).
257 addEventListener("click", mi_click);
258
259 setTimeout(function() {
260 menu_popup.style.opacity = "1";
261 }, 1);
262
263 /* detect loss of focus for popup menu */
264 menu_popup.addEventListener("focusout", function(e) {
265 /* if focus went to a child (menu item), ignore */
266 if (e.relatedTarget &&
267 e.relatedTarget.parentNode.id == "menu-ul")
268 return;
269
270 menu_popup.remove();
271 menu_popup = null;
272 });
273}
274
275function burger_create(e)
276{
277 var e1 = e, etable, d = new Date;
278
279 if (burger)
280 burger.remove();
281
282 burger = document.createElement("DIV");
283 burger.className = "selected-lines-popup";
284 burger.style.top = collect_offsetTop(e1) + "px";
285
286 /* event listener cannot override default browser #URL behaviour */
287 burger.onclick = burger_click;
288
289 etable = find_parent_of_type(e, "table");
290 etable.insertBefore(burger, etable.firstChild);
291 burger_time = d.getTime();
292
293 setTimeout(function() {
294 burger.style.opacity = "1";
295 }, 1);
296}
297
298/*
299 * We got a click on a line number #url
300 *
301 * Create the "burger" menu there.
302 *
303 * Redraw the line range highlight accordingly.
304 */
305
306function line_range_click(e) {
307 var t, elem, m, n = window.location.href.length -
308 window.location.hash.length;
309
310 /* disable passthru to stop scrolling by browser #URL handler */
311 e.stopPropagation();
312 e.preventDefault();
313
314 if (!e.target.id)
315 return;
316
317 if (menu_popup) {
318 menu_popup.remove();
319 menu_popup = null;
320
321 return;
322 }
323
324 elem = document.getElementById(e.target.id);
325 if (!elem)
326 return;
327
328 burger_create(elem);
329
Andy Green91984092018-06-24 09:08:40 +0800330 if (!window.location.hash ||
Andy Greene96af4a2018-06-24 10:31:13 +0800331 window.location.hash.indexOf("-") >= 0 ||
332 e.target.id.substring(1) == window.location.href.substring(n + 2))
Andy Green91984092018-06-24 09:08:40 +0800333 t = window.location.href.substring(0, n) +
334 '#n' + e.target.id.substring(1);
335 else {
336 if (parseInt(window.location.hash.substring(2)) <
337 parseInt(e.target.id.substring(1))) /* forwards */
338 t = window.location + '-' + e.target.id.substring(1);
339 else
340 t = window.location.href.substring(0, n) +
341 '#n' + e.target.id.substring(1) + '-' +
342 window.location.href.substring(n + 2);
343 }
344
345 window.history.replaceState(null, null, t);
346
Andy Greene96af4a2018-06-24 10:31:13 +0800347 line_range_highlight(0);
Andy Green91984092018-06-24 09:08:40 +0800348}
349
Andy Greenfe549182018-06-21 16:56:36 +0800350/* we have to use load, because header images can push the layout vertically */
351window.addEventListener("load", function() {
Andy Greene96af4a2018-06-24 10:31:13 +0800352 line_range_highlight(1);
Andy Greenfe549182018-06-21 16:56:36 +0800353}, false);
354
Andy Green91984092018-06-24 09:08:40 +0800355document.addEventListener("DOMContentLoaded", function() {
356 /* event listener cannot override default #URL browser processing,
357 * requires onclick */
358 var e = document.getElementById("linenumbers");
359 if (e)
360 e.onclick = line_range_click;
361}, false);
362
Andy Green1915feb2018-06-23 06:07:45 +0800363window.addEventListener("hashchange", function() {
Andy Greene96af4a2018-06-24 10:31:13 +0800364 line_range_highlight(1);
Andy Green1915feb2018-06-23 06:07:45 +0800365}, false);
366
Andy Greenfe549182018-06-21 16:56:36 +0800367})();