Krolyxon
Tag v0.1.0

ferreniumassetsjsapp.js

129 lines · 4.6 KB · JavaScript

1// Progressive enhancements. Everything works without this file.
2(function () {
3 "use strict";
4
5 // Index: instant filter; "/" focuses it, Escape clears it.
6 var input = document.querySelector("[data-filter]");
7 if (input) {
8 var rows = Array.prototype.slice.call(document.querySelectorAll("[data-search]"));
9 var sections = Array.prototype.slice.call(document.querySelectorAll("[data-section]"));
10 var empty = document.querySelector("[data-filter-empty]");
11
12 var apply = function () {
13 var terms = input.value.toLowerCase().split(/\s+/).filter(Boolean);
14 var shown = 0;
15 rows.forEach(function (row) {
16 var text = row.getAttribute("data-search");
17 var match = terms.every(function (t) { return text.indexOf(t) !== -1; });
18 row.hidden = !match;
19 if (match) shown++;
20 });
21 sections.forEach(function (section) {
22 section.hidden = !section.querySelector("[data-search]:not([hidden])");
23 });
24 if (empty) empty.hidden = shown > 0;
25 };
26
27 input.addEventListener("input", apply);
28 input.addEventListener("keydown", function (e) {
29 if (e.key === "Escape") {
30 input.value = "";
31 apply();
32 input.blur();
33 }
34 });
35 document.addEventListener("keydown", function (e) {
36 if (e.key !== "/" || e.ctrlKey || e.metaKey || e.altKey) return;
37 var t = e.target;
38 if (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName)) return;
39 e.preventDefault();
40 input.focus();
41 input.select();
42 });
43 }
44 // Blob: line ranges. #L12 works without JS (:target); here shift-click
45 // extends to #L12-L20 and the range is highlighted on load.
46 var code = document.querySelector("[data-lines]");
47 if (code) {
48 var anchor = null;
49 var select = function (from, to) {
50 Array.prototype.forEach.call(code.querySelectorAll("tr.selected"), function (tr) {
51 tr.classList.remove("selected");
52 });
53 if (!from) return;
54 if (to < from) { var t = from; from = to; to = t; }
55 for (var n = from; n <= to; n++) {
56 var row = document.getElementById("L" + n);
57 if (row) row.classList.add("selected");
58 }
59 };
60 var fromHash = function () {
61 var m = /^#L(\d+)(?:-L(\d+))?$/.exec(location.hash);
62 if (!m) return select(0);
63 anchor = +m[1];
64 select(anchor, +(m[2] || m[1]));
65 if (m[2]) {
66 var row = document.getElementById("L" + Math.min(+m[1], +m[2]));
67 if (row) row.scrollIntoView({ block: "center" });
68 }
69 };
70 code.addEventListener("click", function (e) {
71 var a = e.target.closest && e.target.closest(".ln a");
72 if (!a) return;
73 var n = +a.textContent;
74 e.preventDefault();
75 var hash = "#L" + n;
76 if (e.shiftKey && anchor && anchor !== n) {
77 hash = "#L" + Math.min(anchor, n) + "-L" + Math.max(anchor, n);
78 } else {
79 anchor = n;
80 }
81 history.replaceState(null, "", hash);
82 fromHash();
83 });
84 window.addEventListener("hashchange", fromHash);
85 fromHash();
86 }
87
88 // Summary: clone protocol switch and copy button.
89 var clone = document.querySelector("[data-clone]");
90 if (clone) {
91 var KEY = "ferrenium-clone";
92 var status = clone.querySelector("[data-copy-status]");
93 var choose = function (i) {
94 var rows = clone.querySelectorAll("[data-clone-url]");
95 if (!rows[i]) i = 0;
96 Array.prototype.forEach.call(rows, function (row, j) { row.hidden = j !== i; });
97 Array.prototype.forEach.call(clone.querySelectorAll("[data-clone-choice]"), function (b, j) {
98 b.setAttribute("aria-pressed", j === i ? "true" : "false");
99 });
100 };
101 var saved = 0;
102 try { saved = +(localStorage.getItem(KEY) || 0); } catch (e) {}
103 choose(saved);
104 clone.addEventListener("click", function (e) {
105 var choice = e.target.closest("[data-clone-choice]");
106 if (choice) {
107 var i = +choice.getAttribute("data-clone-choice");
108 choose(i);
109 try { localStorage.setItem(KEY, i); } catch (err) {}
110 return;
111 }
112 var copy = e.target.closest("[data-copy]");
113 if (!copy) return;
114 var input = copy.parentNode.querySelector("input");
115 var done = function (ok) {
116 status.textContent = ok ? "[Copied]" : "[Error: copy failed]";
117 status.classList.toggle("status-error", !ok);
118 clearTimeout(done.t);
119 done.t = setTimeout(function () { status.textContent = ""; }, 2000);
120 };
121 if (navigator.clipboard) {
122 navigator.clipboard.writeText(input.value).then(function () { done(true); }, function () { done(false); });
123 } else {
124 input.select();
125 done(document.execCommand("copy"));
126 }
127 });
128 }
129})();