Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 1 | /* 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 Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 11 | var burger, menu_popup; |
| 12 | |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 13 | function 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 | |
| 26 | function find_parent_of_type(e, type) |
| 27 | { |
| 28 | while (e.tagName.toLowerCase() != type) |
| 29 | e = e.parentNode; |
| 30 | |
| 31 | return e; |
| 32 | } |
| 33 | |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 34 | /* |
| 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 | |
| 42 | function line_range_highlight(do_burger) |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 43 | { |
| 44 | var h = window.location.hash, l1 = 0, l2 = 0, e, t; |
| 45 | |
Andy Green | 1915feb | 2018-06-23 06:07:45 +0800 | [diff] [blame] | 46 | 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 Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 52 | e1.classList.remove( |
| 53 | 'selected-line-link-highlight'); |
Andy Green | 1915feb | 2018-06-23 06:07:45 +0800 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | e.remove(); |
| 57 | } |
| 58 | |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 59 | 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 Green | 0fcc450 | 2018-06-23 06:41:47 +0800 | [diff] [blame] | 71 | var lh, etable, etr, de, n, hl, v; |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 72 | |
| 73 | e = document.getElementById('n' + l1); |
| 74 | if (!e) |
| 75 | return; |
| 76 | |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 77 | if (do_burger) |
| 78 | burger_create(e); |
| 79 | |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 80 | 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 Green | 1915feb | 2018-06-23 06:07:45 +0800 | [diff] [blame] | 85 | de.id = "cgit-line-range"; |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 86 | 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 Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 94 | /* the table is offset from the left, the highlight needs to follow it */ |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 95 | etable = find_parent_of_type(etr, "table"); |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 96 | 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 Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 107 | document.getElementById('n' + n++).classList.add( |
| 108 | 'selected-line-link-highlight'); |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 109 | |
Andy Green | 0fcc450 | 2018-06-23 06:41:47 +0800 | [diff] [blame] | 110 | 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 Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 122 | } |
| 123 | |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 124 | function copy_clipboard(value) |
| 125 | { |
| 126 | var inp = document.createElement("textarea"); |
| 127 | var e = document.getElementById("linenumbers"); |
Andy Green | 9198409 | 2018-06-24 09:08:40 +0800 | [diff] [blame] | 128 | |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 129 | 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 | */ |
| 148 | function mi_click(e) { |
| 149 | var u, n; |
| 150 | |
Andy Green | 9198409 | 2018-06-24 09:08:40 +0800 | [diff] [blame] | 151 | e.stopPropagation(); |
| 152 | e.preventDefault(); |
| 153 | |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 154 | 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 | |
| 186 | function 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 | |
| 244 | function 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 | |
| 275 | function 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 Green | 9198409 | 2018-06-24 09:08:40 +0800 | [diff] [blame] | 299 | if (!window.location.hash || |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 300 | window.location.hash.indexOf("-") >= 0 || |
| 301 | e.target.id.substring(1) == window.location.href.substring(n + 2)) |
Andy Green | 9198409 | 2018-06-24 09:08:40 +0800 | [diff] [blame] | 302 | 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 Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 316 | line_range_highlight(0); |
Andy Green | 9198409 | 2018-06-24 09:08:40 +0800 | [diff] [blame] | 317 | } |
| 318 | |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 319 | /* we have to use load, because header images can push the layout vertically */ |
| 320 | window.addEventListener("load", function() { |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 321 | line_range_highlight(1); |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 322 | }, false); |
| 323 | |
Andy Green | 9198409 | 2018-06-24 09:08:40 +0800 | [diff] [blame] | 324 | document.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 Green | 1915feb | 2018-06-23 06:07:45 +0800 | [diff] [blame] | 332 | window.addEventListener("hashchange", function() { |
Andy Green | e96af4a | 2018-06-24 10:31:13 +0800 | [diff] [blame^] | 333 | line_range_highlight(1); |
Andy Green | 1915feb | 2018-06-23 06:07:45 +0800 | [diff] [blame] | 334 | }, false); |
| 335 | |
Andy Green | fe54918 | 2018-06-21 16:56:36 +0800 | [diff] [blame] | 336 | })(); |