blob: 7fae9aabbb91d725221135b18aec9d569003ea0a [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 Greene96af4a2018-06-24 10:31:13 +080034/*
35 * This creates an absolute div as a child of the content table.
36 * It's horizontally and vertically aligned and sized according
37 * to the #URL information like #n123-456
38 *
39 * If the highlight div already exists, it's removed and remade.
40 */
41
42function line_range_highlight(do_burger)
Andy Greenfe549182018-06-21 16:56:36 +080043{
44 var h = window.location.hash, l1 = 0, l2 = 0, e, t;
45
Andy Green1915feb2018-06-23 06:07:45 +080046 e = document.getElementById("cgit-line-range");
47 if (e) {
48 l1 = e.l1;
49 while (l1 <= e.l2) {
50 var e1;
51 e1 = document.getElementById('n' + l1++);
Andy Greene96af4a2018-06-24 10:31:13 +080052 e1.classList.remove(
53 'selected-line-link-highlight');
Andy Green1915feb2018-06-23 06:07:45 +080054 }
55
56 e.remove();
57 }
58
Andy Greenfe549182018-06-21 16:56:36 +080059 l1 = parseInt(h.substring(2));
60 if (!l1)
61 return;
62
63 t = h.indexOf("-");
64 l2 = l1;
65 if (t >= 1)
66 l2 = parseInt(h.substring(t + 1));
67
68 if (l2 < l1)
69 l2 = l1;
70
Andy Green0fcc4502018-06-23 06:41:47 +080071 var lh, etable, etr, de, n, hl, v;
Andy Greenfe549182018-06-21 16:56:36 +080072
73 e = document.getElementById('n' + l1);
74 if (!e)
75 return;
76
Andy Greene96af4a2018-06-24 10:31:13 +080077 if (do_burger)
78 burger_create(e);
79
Andy Greenfe549182018-06-21 16:56:36 +080080 de = document.createElement("DIV");
81
82 de.className = "selected-lines";
83 de.style.bottom = e.style.bottom;
84 de.style.top = collect_offsetTop(e) + 'px';
Andy Green1915feb2018-06-23 06:07:45 +080085 de.id = "cgit-line-range";
Andy Greenfe549182018-06-21 16:56:36 +080086 de.l1 = l1;
87 de.l2 = l2;
88
89 /* we will tack the highlight div at the parent tr */
90 etr = find_parent_of_type(e, "tr");
91
92 de.style.width = etr.offsetWidth + 'px';
93
Andy Greene96af4a2018-06-24 10:31:13 +080094 /* the table is offset from the left, the highlight needs to follow it */
Andy Greenfe549182018-06-21 16:56:36 +080095 etable = find_parent_of_type(etr, "table");
Andy Greenfe549182018-06-21 16:56:36 +080096 de.style.left = etable.offsetLeft + 'px';
97 de.style.height = ((l2 - l1 + 1) * e.offsetHeight) + 'px';
98
99 etr.insertBefore(de, etr.firstChild);
100
101 setTimeout(function() {
102 de.style.backgroundColor = "rgba(255, 255, 0, 0.2)";
103 }, 1);
104
105 n = l1;
106 while (n <= l2)
Andy Greene96af4a2018-06-24 10:31:13 +0800107 document.getElementById('n' + n++).classList.add(
108 'selected-line-link-highlight');
Andy Greenfe549182018-06-21 16:56:36 +0800109
Andy Green0fcc4502018-06-23 06:41:47 +0800110 hl = (window.innerHeight / (e.offsetHeight + 1));
111 v = (l1 + ((l2 - l1) / 2)) - (hl / 2);
112 if (v > l1)
113 v = l1;
114 if (v < 1)
115 v = 1;
116
117 t = document.getElementById('n' + Math.round(v));
118 if (!t)
119 t = e;
120
121 t.scrollIntoView(true);
Andy Greenfe549182018-06-21 16:56:36 +0800122}
123
Andy Greene96af4a2018-06-24 10:31:13 +0800124function copy_clipboard(value)
125{
126 var inp = document.createElement("textarea");
127 var e = document.getElementById("linenumbers");
Andy Green91984092018-06-24 09:08:40 +0800128
Andy Greene96af4a2018-06-24 10:31:13 +0800129 inp.type = "text";
130 inp.value = value;
131 /* hidden style stops it working for clipboard */
132 inp.setAttribute('readonly', '');
133 inp.style.position = "absolute";
134 inp.style.left = "-1000px";
135
136 e.appendChild(inp);
137
138 inp.select();
139
140 document.execCommand("copy");
141
142 inp.remove();
143}
144
145/*
146 * An element in the popup menu was clicked, perform the appropriate action
147 */
148function mi_click(e) {
149 var u, n;
150
Andy Green91984092018-06-24 09:08:40 +0800151 e.stopPropagation();
152 e.preventDefault();
153
Andy Greene96af4a2018-06-24 10:31:13 +0800154 switch (e.target.id) {
155 case "mi-c-line":
156 /* implemented in next patch */
157 break;
158 case "mi-c-link":
159 copy_clipboard(window.location.href);
160 break;
161 case "mi-c-blame":
162 u = window.location.href;
163 t = u.indexOf("/tree/");
164 if (t)
165 window.location = u.substring(0, t) + "/blame/" +
166 u.substring(t + 6);
167 break;
168 case "mi-c-tree":
169 u = window.location.href;
170 t = u.indexOf("/blame/");
171 if (t)
172 window.location = u.substring(0, t) + "/tree/" +
173 u.substring(t + 7);
174 break;
175 }
176
177 if (!menu_popup)
178 return;
179
180 menu_popup.remove();
181 menu_popup = null;
182}
183
184/* We got a click on the (***) burger menu */
185
186function burger_click(e) {
187 var e1 = e, etable, d = new Date, s = "", n, is_blame,
188 ar = new Array("mi-c-line", "mi-c-link", "mi-c-blame", "mi-c-tree"),
189 an = new Array("Copy Lines", "Copy Link",
190 "View Blame", /* 2: shown in /tree/ */
191 "Remove Blame" /* 3: shown in /blame/ */);
192
193 e.preventDefault();
194
195 if (menu_popup) {
196 menu_popup.remove();
197 menu_popup = null;
198
199 return;
200 }
201
202 /*
203 * Create the popup menu
204 */
205
206 is_blame = !!document.getElementsByClassName("hashes").length;
207
208 menu_popup = document.createElement("DIV");
209 menu_popup.className = "popup-menu";
210 menu_popup.style.top = collect_offsetTop(e1) + e.offsetHeight + "px";
211
212 s = "<ul id='menu-ul'>";
213 for (n = 0; n < an.length; n++)
214 if (n < 2 || is_blame == (n == 3))
215 s += "<li id='" + ar[n] + "' tabindex='" + n + "'>" +
216 an[n] + "</li>";
217
218 menu_popup.innerHTML = s;
219
220 burger.insertBefore(menu_popup, null);
221
222 document.getElementById(ar[0]).focus();
223 for (n = 0; n < an.length; n++)
224 if (n < 2 || is_blame == (n == 3))
225 document.getElementById(ar[n]).
226 addEventListener("click", mi_click);
227
228 setTimeout(function() {
229 menu_popup.style.opacity = "1";
230 }, 1);
231
232 /* detect loss of focus for popup menu */
233 menu_popup.addEventListener("focusout", function(e) {
234 /* if focus went to a child (menu item), ignore */
235 if (e.relatedTarget &&
236 e.relatedTarget.parentNode.id == "menu-ul")
237 return;
238
239 menu_popup.remove();
240 menu_popup = null;
241 });
242}
243
244function burger_create(e)
245{
246 var e1 = e, etable, d = new Date;
247
248 if (burger)
249 burger.remove();
250
251 burger = document.createElement("DIV");
252 burger.className = "selected-lines-popup";
253 burger.style.top = collect_offsetTop(e1) + "px";
254
255 /* event listener cannot override default browser #URL behaviour */
256 burger.onclick = burger_click;
257
258 etable = find_parent_of_type(e, "table");
259 etable.insertBefore(burger, etable.firstChild);
260 burger_time = d.getTime();
261
262 setTimeout(function() {
263 burger.style.opacity = "1";
264 }, 1);
265}
266
267/*
268 * We got a click on a line number #url
269 *
270 * Create the "burger" menu there.
271 *
272 * Redraw the line range highlight accordingly.
273 */
274
275function line_range_click(e) {
276 var t, elem, m, n = window.location.href.length -
277 window.location.hash.length;
278
279 /* disable passthru to stop scrolling by browser #URL handler */
280 e.stopPropagation();
281 e.preventDefault();
282
283 if (!e.target.id)
284 return;
285
286 if (menu_popup) {
287 menu_popup.remove();
288 menu_popup = null;
289
290 return;
291 }
292
293 elem = document.getElementById(e.target.id);
294 if (!elem)
295 return;
296
297 burger_create(elem);
298
Andy Green91984092018-06-24 09:08:40 +0800299 if (!window.location.hash ||
Andy Greene96af4a2018-06-24 10:31:13 +0800300 window.location.hash.indexOf("-") >= 0 ||
301 e.target.id.substring(1) == window.location.href.substring(n + 2))
Andy Green91984092018-06-24 09:08:40 +0800302 t = window.location.href.substring(0, n) +
303 '#n' + e.target.id.substring(1);
304 else {
305 if (parseInt(window.location.hash.substring(2)) <
306 parseInt(e.target.id.substring(1))) /* forwards */
307 t = window.location + '-' + e.target.id.substring(1);
308 else
309 t = window.location.href.substring(0, n) +
310 '#n' + e.target.id.substring(1) + '-' +
311 window.location.href.substring(n + 2);
312 }
313
314 window.history.replaceState(null, null, t);
315
Andy Greene96af4a2018-06-24 10:31:13 +0800316 line_range_highlight(0);
Andy Green91984092018-06-24 09:08:40 +0800317}
318
Andy Greenfe549182018-06-21 16:56:36 +0800319/* we have to use load, because header images can push the layout vertically */
320window.addEventListener("load", function() {
Andy Greene96af4a2018-06-24 10:31:13 +0800321 line_range_highlight(1);
Andy Greenfe549182018-06-21 16:56:36 +0800322}, false);
323
Andy Green91984092018-06-24 09:08:40 +0800324document.addEventListener("DOMContentLoaded", function() {
325 /* event listener cannot override default #URL browser processing,
326 * requires onclick */
327 var e = document.getElementById("linenumbers");
328 if (e)
329 e.onclick = line_range_click;
330}, false);
331
Andy Green1915feb2018-06-23 06:07:45 +0800332window.addEventListener("hashchange", function() {
Andy Greene96af4a2018-06-24 10:31:13 +0800333 line_range_highlight(1);
Andy Green1915feb2018-06-23 06:07:45 +0800334}, false);
335
Andy Greenfe549182018-06-21 16:56:36 +0800336})();