OXYGENHUB
# 8d2caf122517 oxygenhub / assets / hub.js
5.0 KiB · JavaScript · last changed Web edits and browser conflict resolution; landing under a policy 1 month ago source blame history raw
1
/* Progressive enhancement only: every page works without this file. */
2
(function () {
3
  "use strict";
4
  /* The band's active tab: the longest link that prefixes the path. */
5
  var p = location.pathname, best = null;
6
  document.querySelectorAll(".band a").forEach(function (a) {
7
    var h = a.getAttribute("href");
8
    if (h !== "/" && (p === h || p.indexOf(h + "/") === 0) && (!best || h.length > best.getAttribute("href").length)) best = a;
9
  });
10
  if (best) best.classList.add("on");
11
12
  /* Off-the-shelf editors, loaded only where a page asks for them:
13
     CodeMirror 6 over textarea.code, EasyMDE over textarea.md. */
14
  function load(src, css) {
15
    return new Promise(function (ok) {
16
      if (css) { var l = document.createElement("link"); l.rel = "stylesheet"; l.href = css; document.head.appendChild(l); }
17
      var s = document.createElement("script"); s.src = src; s.onload = ok; document.head.appendChild(s);
18
    });
19
  }
20
  if (document.querySelector("textarea.code")) load("/static/vendor/editor.js");
21
  var mds = document.querySelectorAll("textarea.md");
22
  if (mds.length) load("/static/vendor/easymde.min.js", "/static/vendor/easymde.min.css").then(function () {
23
    var E = window.EasyMDE;
24
    function b(name, action, text, title) { return { name: name, action: action, text: text, title: title, className: "mde-btn" }; }
25
    var toolbar = [b("bold", E.toggleBold, "B", "Bold"), b("italic", E.toggleItalic, "I", "Italic"), b("heading", E.toggleHeadingSmaller, "H", "Heading"), "|",
26
      b("code", E.toggleCodeBlock, "</>", "Code"), b("quote", E.toggleBlockquote, "“", "Quote"), b("ul", E.toggleUnorderedList, "•", "List"), b("ol", E.toggleOrderedList, "1.", "Numbered list"), "|",
27
      b("link", E.drawLink, "link", "Link"), "|", b("preview", E.togglePreview, "preview", "Preview")];
28
    mds.forEach(function (t) { new E({ element: t, spellChecker: false, status: false, forceSync: true, autoDownloadFontAwesome: false, toolbar: toolbar }); });
29
  });
30
31
  /* Copy buttons. */
32
  document.querySelectorAll("[data-copy]").forEach(function (b) {
33
    b.addEventListener("click", function () {
34
      navigator.clipboard.writeText(b.getAttribute("data-copy")).then(function () {
35
        var t = b.textContent; b.textContent = "copied"; setTimeout(function () { b.textContent = t; }, 1200);
36
      });
37
    });
38
  });
39
40
  /* Tree rows: the last change per entry, fetched after the page paints. */
41
  var tree = document.querySelector("table.tree[data-last]");
42
  if (tree) {
43
    var paths = [].map.call(tree.querySelectorAll("tr[data-path]"), function (r) { return r.getAttribute("data-path"); });
44
    if (paths.length) {
45
      fetch(tree.getAttribute("data-last") + "?paths=" + encodeURIComponent(paths.join(",")), { credentials: "same-origin" })
46
        .then(function (r) { return r.json(); })
47
        .then(function (rows) {
48
          rows.forEach(function (c) {
49
            var tr = tree.querySelector('tr[data-path="' + CSS.escape(c.path) + '"]');
50
            if (!tr) return;
51
            var a = tr.querySelector("td.last a"), t = tr.querySelector("td.age");
52
            if (a) { a.textContent = c.title; a.href = tree.getAttribute("data-change") + c.hash; }
53
            if (t) { t.textContent = c.ago; t.title = c.when; }
54
          });
55
        }).catch(function () {});
56
    }
57
  }
58
59
  /* Line anchors: #L12 or #L12-L20 highlight a range and clicking a number selects. */
60
  function mark() {
61
    var m = location.hash.match(/^#L(\d+)(?:-L(\d+))?$/);
62
    document.querySelectorAll("tr.hl").forEach(function (r) { r.classList.remove("hl"); });
63
    if (!m) return;
64
    var a = +m[1], b = +(m[2] || m[1]);
65
    for (var i = Math.min(a, b); i <= Math.max(a, b); i++) {
66
      var row = document.getElementById("L" + i);
67
      if (row) row.classList.add("hl");
68
    }
69
    var first = document.getElementById("L" + Math.min(a, b));
70
    if (first && !mark.done) { first.scrollIntoView({ block: "center" }); mark.done = true; }
71
  }
72
  window.addEventListener("hashchange", mark);
73
  mark();
74
  document.querySelectorAll("table.code td.n").forEach(function (td) {
75
    td.addEventListener("click", function (e) {
76
      var n = td.parentNode.id;
77
      if (e.shiftKey && location.hash.match(/^#L(\d+)/)) location.hash = location.hash.replace(/^#(L\d+).*/, "$1-" + n);
78
      else location.hash = n;
79
    });
80
  });
81
82
  /* Keyboard: / focuses search; g c / g p / g i jump; ? shows the sheet. */
83
  var pending = null;
84
  document.addEventListener("keydown", function (e) {
85
    if (e.target && /^(INPUT|TEXTAREA|SELECT)$/.test(e.target.tagName)) return;
86
    if (e.key === "/") { var s = document.querySelector("input[name=q]"); if (s) { e.preventDefault(); s.focus(); } return; }
87
    if (e.key === "?") { var k = document.getElementById("keys"); if (k) k.hidden = !k.hidden; return; }
88
    var repo = document.body.getAttribute("data-repo");
89
    if (pending === "g" && repo) {
90
      var to = { c: "", p: "/proposals", i: "/issues", h: "/changes/" + (document.body.getAttribute("data-ref") || "main"), b: "/bookmarks", t: "/trust" }[e.key];
91
      if (to !== undefined) location.href = repo + to;
92
      pending = null; return;
93
    }
94
    pending = e.key === "g" ? "g" : null;
95
  });
96
})();