PageSourceSearch

https://moon-jam.me/js/utils.js

js moon-jam.me collected 2026-10-09 22:08:27 UTC 11,423 bytes, 61 lines download raw bytes

1/* global NexT, CONFIG */
2HTMLElement.prototype.wrap=function(e){this.parentNode.insertBefore(e,this),this.parentNode.removeChild(this),e.appendChild(this)},function(){const e=()=>document.dispatchEvent(new Event("page:loaded",{bubbles:!0}));"loading"===document.readyState?document.addEventListener("readystatechange",e,{once:!0}):e(),document.addEventListener("pjax:success",e)}(),NexT.utils={registerExtURL:function(){document.querySelectorAll("span.exturl").forEach(e=>{const t=document.createElement("a");
3// https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings
4t.href=decodeURIComponent(atob(e.dataset.url).split("").map(e=>"%"+("00"+e.charCodeAt(0).toString(16)).slice(-2)).join("")),t.rel="noopener external nofollow noreferrer",t.target="_blank",t.className=e.className,t.title=e.title,t.innerHTML=e.innerHTML,e.parentNode.replaceChild(t,e)})},
5/**
6   * One-click copy code support.
7   */
8registerCopyCode:function(){let e=document.querySelectorAll("figure.highlight");0===e.length&&(e=document.querySelectorAll("pre:not(.mermaid)")),e.forEach(e=>{if(e.querySelectorAll(".code .line span").forEach(e=>{e.classList.forEach(t=>{e.classList.replace(t,`hljs-${t}`)})}),!CONFIG.copycode.enable)return;let t=e;"mac"!==CONFIG.copycode.style&&(t=e.querySelector(".table-container")||e),t.insertAdjacentHTML("beforeend",'<div class="copy-btn"><i class="fa fa-copy fa-fw"></i></div>');const n=e.querySelector(".copy-btn");n.addEventListener("click",()=>{const t=(e.querySelector(".code")||e.querySelector("code")).innerText;if(navigator.clipboard)
9// https://caniuse.com/mdn-api_clipboard_writetext
10navigator.clipboard.writeText(t).then(()=>{n.querySelector("i").className="fa fa-check-circle fa-fw"},()=>{n.querySelector("i").className="fa fa-times-circle fa-fw"});else{const e=document.createElement("textarea");
10e.style.top=window.scrollY+"px",// Prevent page scrolling
11e.style.position="absolute",e.style.opacity="0",e.readOnly=!0,e.value=t,document.body.append(e),e.select(),e.setSelectionRange(0,t.length),e.readOnly=!1;const o=document.execCommand("copy");n.querySelector("i").className=o?"fa fa-check-circle fa-fw":"fa fa-times-circle fa-fw",e.blur(),// For iOS
12n.blur(),document.body.removeChild(e)}}),e.addEventListener("mouseleave",()=>{setTimeout(()=>{n.querySelector("i").className="fa fa-copy fa-fw"},300)})})},wrapTableWithBox:function(){document.querySelectorAll("table").forEach(e=>{const t=document.createElement("div");t.className="table-container",e.wrap(t)})},registerVideoIframe:function(){document.querySelectorAll("iframe").forEach(e=>{if(["www.youtube.com","player.vimeo.com","player.youku.com","player.bilibili.com","www.tudou.com"].some(t=>e.src.includes(t))&&!e.parentNode.matches(".video-container")){const t=document.createElement("div");t.className="video-container",e.wrap(t);const n=Number(e.width),o=Number(e.height);n&&o&&(t.style.paddingTop=o/n*100+"%")}})},updateActiveNav:function(){if(!Array.isArray(NexT.utils.sections))return;let e=NexT.utils.sections.findIndex(e=>e&&e.getBoundingClientRect().top>10);-1===e?e=NexT.utils.sections.length-1:e>0&&e--,this.activateNavByIndex(e)},registerScrollPercent:function(){const e=document.querySelector(".back-to-top"),t=document.querySelector(".reading-progress-bar");
13// For init back to top in sidebar if page was scrolled after page refresh.
14window.addEventListener("scroll",()=>{if(e||t){const n=document.body.scrollHeight-window.innerHeight,o=n>0?Math.min(100*window.scrollY/n,100):0;e&&(e.classList.toggle("back-to-top-on",Math.round(o)>=5),e.querySelector("span").innerText=Math.round(o)+"%"),t&&t.style.setProperty("--progress",o.toFixed(2)+"%")}this.updateActiveNav()},{passive:!0}),e&&e.addEventListener("click",()=>{window.anime({targets:document.scrollingElement,duration:500,easing:"linear",scrollTop:0})})},
15/**
16   * Tabs tag listener (without twitter bootstrap).
17   */
18registerTabsTag:function(){
19// Binding `nav-tabs` & `tab-content` by real time permalink changing.
20document.querySelectorAll(".tabs ul.nav-tabs .tab").forEach(e=>{e.addEventListener("click",t=>{
21// Prevent selected tab to select again.
22if(t.preventDefault(),e.classList.contains("active"))return;const n=e.parentNode,o=n.nextElementSibling;
23// Get the height of `tab-pane` which is activated before, and set it as the height of `tab-content` with extra margin / paddings.
24o.style.overflow="hidden",o.style.transition="height 1s";
25// Comment system selection tab does not contain .active class.
26const i=o.querySelector(".active")||o.firstElementChild,r=parseInt(window.getComputedStyle(i).height.replace("px",""),10)||0,c=parseInt(window.getComputedStyle(i).paddingTop.replace("px",""),10),a=parseInt(window.getComputedStyle(i.firstElementChild).marginBottom.replace("px",""),10);
27// Hight might be `auto`.
28o.style.height=r+c+a+"px",
29// Add & Remove active class on `nav-tabs` & `tab-content`.
30[...n.children].forEach(t=>{t.classList.toggle("active",t===e)});
31// https://stackoverflow.com/questions/20306204/using-queryselector-with-ids-that-are-numbers
32const s=document.getElementById(e.querySelector("a").getAttribute("href").replace("#",""));[...s.parentNode.children].forEach(e=>{e.classList.toggle("active",e===s)}),
33// Trigger event
34s.dispatchEvent(new Event("tabs:click",{bubbles:!0}));
35// Get the height of `tab-pane` which is activated now.
36const l=document.body.scrollHeight>(window.innerHeight||document.documentElement.clientHeight),d=parseInt(window.getComputedStyle(o.querySelector(".active")).height.replace("px",""),10);if(
37// Reset the height of `tab-content` and see the animation.
38o.style.height=d+c+a+"px",
39// Change the height of `tab-content` may cause scrollbar show / disappear, which may result in the change of the `tab-pane`'s height
40setTimeout(()=>{if(document.body.scrollHeight>(window.innerHeight||document.documentElement.clientHeight)!==l){o.style.transition="height 0.3s linear";
41// After the animation, we need reset the height of `tab-content` again.
42const e=parseInt(window.getComputedStyle(o.querySelector(".active")).height.replace("px",""),10);o.style.height=e+c+a+"px"}
43// Remove all the inline styles, and let the height be adaptive again.
44setTimeout(()=>{o.style.transition="",o.style.height=""},250)},1e3),!CONFIG.stickytabs)return;const u=n.parentNode.getBoundingClientRect().top+window.scrollY+10;window.anime({targets:document.scrollingElement,duration:500,easing:"linear",scrollTop:u})})}),window.dispatchEvent(new Event("tabs:register"))},registerCanIUseTag:function(){
45// Get responsive height passed from iframe.
46window.addEventListener("message",({data:e})=>
46{if("string"==typeof e&&e.includes("ciu_embed")){const t=e.split(":")[1],n=e.split(":")[2];document.querySelector(`iframe[data-feature=${t}]`).style.height=parseInt(n,10)+5+"px"}},!1)},registerActiveMenuItem:function(){document.querySelectorAll(".menu-item a[href]").forEach(e=>{const t=e.pathname===location.pathname||e.pathname===location.pathname.replace("index.html",""),n=!CONFIG.root.startsWith(e.pathname)&&location.pathname.startsWith(e.pathname);e.classList.toggle("menu-item-active",e.hostname===location.hostname&&(t||n))})},registerLangSelect:function(){document.querySelectorAll(".lang-select").forEach(e=>{e.value=CONFIG.page.lang,e.addEventListener("change",()=>{const t=e.options[e.selectedIndex];document.querySelectorAll(".lang-select-label span").forEach(e=>{e.innerText=t.text}),
47// Disable Pjax to force refresh translation of menu item
48window.location.href=t.dataset.href})})},registerSidebarTOC:function(){this.sections=[...document.querySelectorAll(".post-toc:not(.placeholder-toc) li a.nav-link")].map(e=>{const t=document.getElementById(decodeURI(e.getAttribute("href")).replace("#",""));
49// TOC item animation navigate.
50return e.addEventListener("click",n=>{n.preventDefault();const o=t.getBoundingClientRect().top+window.scrollY;window.anime({targets:document.scrollingElement,duration:500,easing:"linear",scrollTop:o,complete:()=>{history.pushState(null,document.title,e.href)}})}),t}),this.updateActiveNav()},registerPostReward:function(){const e=document.querySelector(".reward-container button");e&&e.addEventListener("click",()=>{document.querySelector(".post-reward").classList.toggle("active")})},activateNavByIndex:function(e){const t=document.querySelector(".post-toc:not(.placeholder-toc) .nav");if(!t)return;const n=t.querySelectorAll(".nav-item"),o=n[e];if(!o||o.classList.contains("active-current"))return;const i=n[n.length-1].offsetHeight;t.querySelectorAll(".active").forEach(e=>{e.classList.remove("active","active-current")}),o.classList.add("active","active-current");let r=o.querySelector(".nav-child")||o.parentElement,c=0;for(;t.contains(r);)r.classList.contains("nav-item")?r.classList.add("active"):(// .nav-child or .nav
51// scrollHeight isn't reliable for transitioning child items.
52// The last nav-item in a list has a margin-bottom of 5px.
53c+=i*r.childElementCount+5,r.style.setProperty("--height",`${c}px`)),r=r.parentElement;
54// Scrolling to center active TOC element if TOC content is taller then viewport.
55const a=document.querySelector("Pisces"===CONFIG.scheme||"Gemini"===CONFIG.scheme?".sidebar-panel-container":".sidebar");document.querySelector(".sidebar-toc-active")&&window.anime({targets:a,duration:200,easing:"linear",scrollTop:a.scrollTop-a.offsetHeight/2+o.getBoundingClientRect().top-a.getBoundingClientRect().top})},updateSidebarPosition:function(){if(window.innerWidth<1200||"Pisces"===CONFIG.scheme||"Gemini"===CONFIG.scheme)return;
56// Expand sidebar on post detail page by default, when post has a toc.
57const e=document.querySelector(".post-toc:not(.placeholder-toc)");let t=CONFIG.page.sidebar;"boolean"!=typeof t&&(
58// There's no definition sidebar in the page front-matter.
59t="always"===CONFIG.sidebar.display||"post"===CONFIG.sidebar.display&&e),t&&window.dispatchEvent(new Event("sidebar:show"))},activateSidebarPanel:function(e){const t=document.querySelector(".sidebar-inner"),n=["sidebar-toc-active","sidebar-overview-active"];if(t.classList.contains(n[e]))return;const o=t.querySelector(".sidebar-panel-container"),i=o.firstElementChild,r=o.lastElementChild;let c=i.scrollHeight;
60// For TOC activation, try to use the animated TOC height
61if(0===e){const e=i.querySelector(".nav");e&&(c=parseInt(e.style.getPropertyValue("--height"),10))}const a=[c,r.scrollHeight];o.style.setProperty("--inactive-panel-height",`${a[1-e]}px`),o.style.setProperty("--active-panel-height",`${a[e]}px`),t.classList.replace(n[1-e],n[e])},getScript:function(e,t={},n){if("function"==typeof t)return this.getScript(e,{condition:n}).then(t);const{condition:o=!1,attributes:{id:i="",async:r=!1,defer:c=!1,crossOrigin:a="",dataset:s={},...l}={},parentNode:d=null}=t;return new Promise((t,n)=>{if(o)t();else{const o=document.createElement("script");
61if(i&&(o.id=i),a&&(o.crossOrigin=a),o.async=r,o.defer=c,Object.assign(o.dataset,s),Object.entries(l).forEach(([e,t])=>{o.setAttribute(e,String(t))}),o.onload=t,o.onerror=n,"object"==typeof e){const{url:t,integrity:n}=e;o.src=t,n&&(o.integrity=n,o.crossOrigin="anonymous")}else o.src=e;(d||document.head).appendChild(o)}})},loadComments:function(e,t){return t?this.loadComments(e).then(t):new Promise(t=>{const n=document.querySelector(e);if(!CONFIG.comments.lazyload||!n)return void t();new IntersectionObserver((e,n)=>{e[0].isIntersecting&&(t(),n.disconnect())}).observe(n)})}};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.