Skip to content
quarry Artifact [ecf0a8712b]
Login

Artifact [ecf0a8712b]

Artifact ecf0a8712b8df1ea12f796dec55335c3c1d57e9a288171f4573bb401af251aaa:


// settings.js — the settings page's three enhancements. None of them
// adds a control the page lacks without script (RFC 0001 Principle 3):
// the rail marks the section in view, a destructive button asks once
// before its form posts, and copy buttons — hidden in the markup —
// are shown, because a button that cannot copy is a lie.
(function () {
  "use strict";

  // The rail follows the reader. Each section reports how much of it
  // is visible; the link of the most visible one carries aria-current.
  // A section taller than the viewport never reaches 100%, so the
  // ratio is compared, not thresholded.
  var rail = document.querySelector(".set-rail");
  if (rail && "IntersectionObserver" in window) {
    var seen = {};
    var mark = function () {
      var best = null;
      for (var id in seen) {
        if (best === null || seen[id] > seen[best]) best = id;
      }
      rail.querySelectorAll("a[aria-current]").forEach(function (a) {
        a.removeAttribute("aria-current");
      });
      var a = best && rail.querySelector('a[href="#' + best + '"]');
      if (a) a.setAttribute("aria-current", "true");
    };
    var io = new IntersectionObserver(function (entries) {
      entries.forEach(function (e) { seen[e.target.id] = e.intersectionRatio; });
      mark();
    }, { threshold: [0, 0.25, 0.5, 0.75, 1] });
    document.querySelectorAll("main section[id]").forEach(function (s) { io.observe(s); });
  }

  // One dialog, shared. The submit button that opened it keeps its own
  // label inside the dialog, so "Revoke" is confirmed by "Revoke".
  // Confirming re-submits the form with the same button, flagged so the
  // interception lets it through this once.
  var dlg = document.getElementById("confirm");
  if (dlg && typeof dlg.showModal === "function") {
    var pending = null;
    document.addEventListener("submit", function (ev) {
      var b = ev.submitter;
      if (!b || !b.hasAttribute("data-confirm") || ev.target._confirmed) return;
      ev.preventDefault();
      pending = { form: ev.target, button: b };
      dlg.querySelector(".confirm-why").textContent = b.getAttribute("data-confirm");
      dlg.querySelector(".confirm-do").textContent = b.textContent;
      dlg.showModal();
    });
    dlg.querySelector(".confirm-do").addEventListener("click", function () {
      var p = pending;
      dlg.close();
      if (!p) return;
      p.form._confirmed = true;
      p.form.requestSubmit(p.button);
    });
    dlg.querySelector(".confirm-no").addEventListener("click", function () {
      pending = null;
      dlg.close();
    });
  }

  // Copy buttons: data-copy names the element whose text is copied.
  // Selection first, so a phone without the Clipboard API still ends
  // up with the text selected for its native gesture.
  document.querySelectorAll("button[data-copy]").forEach(function (b) { b.hidden = false; });
  document.addEventListener("click", function (ev) {
    var b = ev.target.closest && ev.target.closest("button[data-copy]");
    if (!b) return;
    var el = document.getElementById(b.getAttribute("data-copy"));
    if (!el) return;
    var range = document.createRange();
    range.selectNodeContents(el);
    var sel = getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
    var done = function () {
      b.classList.add("done");
      clearTimeout(b._t);
      b._t = setTimeout(function () { b.classList.remove("done"); }, 4000);
    };
    if (navigator.clipboard) {
      navigator.clipboard.writeText(el.textContent).then(done, function () {});
    }
  });
})();