sqzass.git

sqzass

~ A static site generator written in Rust
git clone https://github.com/debuman/sqzass.git

docs/static/js/ui.js (3135B)

      1 /* Chrome that needs the viewport or the scroll position to decide.
      2  *
      3  * Everything here degrades to a usable page when it does not run: the sidebar
      4  * stays open, and the table of contents stays a plain list of links.
      5  */
      6 (function () {
      7   /* The sidebar ships open so that a page without JavaScript is complete. On a
      8      narrow screen that means a long list before the content, so close it —
      9      but only there, and only once, before anything is painted. */
     10   var drawer = document.getElementById("nav-drawer");
     11   if (drawer && matchMedia("(max-width: 60rem)").matches) {
     12     drawer.open = false;
     13   }
     14 
     15   /* Mark the heading the reader is currently under.
     16      rootMargin pins the trigger line near the top of the viewport: without it
     17      a heading counts as "visible" while it is still at the bottom of the
     18      screen, and the marker runs ahead of the reader. */
     19   var links = document.querySelectorAll(".toc a[href^='#']");
     20   if (!links.length || !("IntersectionObserver" in window)) return;
     21 
     22   var byId = {};
     23   var headings = [];
     24   links.forEach(function (a) {
     25     var id = decodeURIComponent(a.getAttribute("href").slice(1));
     26     var h = document.getElementById(id);
     27     if (!h) return;
     28     byId[id] = a;
     29     headings.push(h);
     30   });
     31 
     32   var current = null;
     33   var seen = {};
     34 
     35   function mark() {
     36     // 가장 위에 있는, 이미 지나온 제목이 지금 읽는 곳이다.
     37     var best = null;
     38     headings.forEach(function (h) {
     39       if (seen[h.id]) best = h;
     40     });
     41     if (!best || best === current) return;
     42     if (current && byId[current.id]) byId[current.id].removeAttribute("aria-current");
     43     byId[best.id].setAttribute("aria-current", "true");
     44     current = best;
     45   }
     46 
     47   var io = new IntersectionObserver(
     48     function (entries) {
     49       entries.forEach(function (e) {
     50         seen[e.target.id] = e.boundingClientRect.top < 0 || e.isIntersecting;
     51       });
     52       mark();
     53     },
     54     { rootMargin: "-10% 0px -70% 0px", threshold: 0 }
     55   );
     56   headings.forEach(function (h) { io.observe(h); });
     57 })();
     58 
     59 /* Copy buttons on code blocks.
     60  *
     61  * The documentation says line numbers and a copy button are the site's own CSS
     62  * and JavaScript rather than configuration keys. This is that claim, kept.
     63  */
     64 (function () {
     65   var s = document.currentScript || document.querySelector("script[data-copy]");
     66   var COPY = (s && s.dataset.copy) || "Copy";
     67   var DONE = (s && s.dataset.copied) || "Copied";
     68   if (!navigator.clipboard) return;
     69 
     70   document.querySelectorAll(".prose pre.highlight").forEach(function (pre) {
     71     var code = pre.querySelector("code");
     72     if (!code) return;
     73     var b = document.createElement("button");
     74     b.type = "button";
     75     b.className = "copy";
     76     b.textContent = COPY;
     77     b.addEventListener("click", function () {
     78       // pre.textContent를 쓰면 버튼 자신의 라벨까지 복사된다.
     79       navigator.clipboard.writeText(code.textContent).then(function () {
     80         b.textContent = DONE;
     81         b.dataset.done = "";
     82         setTimeout(function () {
     83           b.textContent = COPY;
     84           delete b.dataset.done;
     85         }, 1200);
     86       });
     87     });
     88     pre.appendChild(b);
     89   });
     90 })();