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}));document.addEventListener("DOMContentLoaded",e),document.addEventListener("pjax:success",e)}(),NexT.utils={scrollTo(e,t){window.CSS?.supports("scroll-behavior","smooth")?e.scrollTo({top:t,behavior:"smooth"}):e.scrollTo(0,t)},registerExtURL(){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)})},registerCopyButton(e,t,n=""){ 5// One-click copy code support. 6e.insertAdjacentHTML("beforeend",'<div class="copy-btn"><i class="fa fa-copy fa-fw"></i></div>');const o=e.querySelector(".copy-btn");o.addEventListener("click",async()=>{if(!n){const e=t.querySelector(".code")||t.querySelector("code");n=e.innerText}if(navigator.clipboard) 7// https://caniuse.com/mdn-api_clipboard_writetext 8try{await navigator.clipboard.writeText(n),o.querySelector("i").className="fa fa-check-circle fa-fw"}catch{o.querySelector("i").className="fa fa-times-circle fa-fw"}else o.querySelector("i").className="fa fa-times-circle fa-fw"}), 9// If copycode.style is not mac, element is larger than target 10// So we need to accept both of them as parameters 11t.addEventListener("mouseleave",()=>{setTimeout(()=>{o.querySelector("i").className="fa fa-copy fa-fw"},300)})},registerCodeblock(e){const t=!!e;let n;n=CONFIG.hljswrap?(t?e:document).querySelectorAll("figure.highlight"):document.querySelectorAll("pre"),n.forEach(e=>{ 12// Skip pre > .mermaid for folding and copy button 13if(e.querySelector(".mermaid"))return;const n=[...e.classList].find(e=>"highlight"!==e);if(!t){let t=e.querySelectorAll(".code .line span");0===t.length&&( 14// Hljs without line_number and wrap 15t=e.querySelectorAll("code.highlight span")),t.forEach(e=>{e.classList.forEach(t=>{e.classList.replace(t,`hljs-${t}`)})})}const o=parseInt(window.getComputedStyle(e).height,10),i=CONFIG.codeblock.fold.enable&&o>CONFIG.codeblock.fold.height;if(!i&&!CONFIG.codeblock.copy_button.enable&&!CONFIG.codeblock.language)return;let r;if(CONFIG.hljswrap&&"mac"===CONFIG.codeblock.copy_button.style)r=e;else{let t=e.querySelector(".code-container");if(!t){ 16// https://github.com/next-theme/hexo-theme-next/issues/98 17// https://github.com/next-theme/hexo-theme-next/pull/508 18const n=e.querySelector(".table-container")||e;t=document.createElement("div"),t.className="code-container",n.wrap(t), 19// add "notranslate" to prevent Google Translate from translating it, which also completely messes up the layout 20t.classList.add("notranslate")}r=t}if(i&&!r.classList.contains("unfold")&&(r.classList.add("highlight-fold"),r.insertAdjacentHTML("beforeend",'<div class="fold-cover"></div><div class="expand-btn"><i class="fa fa-angle-down fa-fw"></i></div>'),r.querySelector(".expand-btn").addEventListener("click",()=>{r.classList.remove("highlight-fold"),r.classList.add("unfold")})),!t&&CONFIG.codeblock.copy_button.enable&&this.registerCopyButton(r,e),!t&&CONFIG.codeblock.language&&n){const e=document.createElement("div");e.className="code-lang",e.innerText=n.toUpperCase(),r.insertAdjacentElement("afterbegin",e)}})},wrapTableWithBox(){document.querySelectorAll("table").forEach(e=>{const t=document.createElement("div");t.className="table-container",e.wrap(t)})},registerVideoIframe(){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(){if(!Array.isArray(this.sections))return;let e=this.sections.findIndex(e=>e?.getBoundingClientRect().top>10);-1===e?e=this.sections.length-1:e>0&&e--,this.activateNavByIndex(e)},registerScrollPercent(){const e=document.querySelector(".back-to-top"),t=document.querySelector(".reading-progress-bar"); 21// For init back to top in sidebar if page was scrolled after page refresh. 22window.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?.addEventListener("click",()=>{NexT.utils.scrollTo(window,0)})}, 23/** 24 * Tabs tag listener (without twitter bootstrap). 25 */ 26registerTabsTag(){
27// Binding `nav-tabs` & `tab-content` by real time permalink changing. 28document.querySelectorAll(".tabs ul.nav-tabs .tab").forEach(e=>{e.addEventListener("click",t=>{ 29// Prevent selected tab to select again. 30if(t.preventDefault(),e.classList.contains("active"))return;const n=e.parentNode,o=n.nextElementSibling; 31// Get the height of `tab-pane` which is activated before, and set it as the height of `tab-content` with extra margin / paddings. 32o.style.overflow="hidden",o.style.transition="height 1s"; 33// Comment system selection tab does not contain .active class. 34const i=o.querySelector(".active")||o.firstElementChild,r=parseInt(window.getComputedStyle(i).height,10)||0,c=parseInt(window.getComputedStyle(i).paddingTop,10),s=parseInt(window.getComputedStyle(i.firstElementChild).marginBottom,10); 35// Hight might be `auto`. 36o.style.height=r+c+s+"px", 37// Add & Remove active class on `nav-tabs` & `tab-content`. 38[...n.children].forEach(t=>{t.classList.toggle("active",t===e)}); 39// https://stackoverflow.com/questions/20306204/using-queryselector-with-ids-that-are-numbers 40const a=document.getElementById(e.querySelector("a").getAttribute("href").replace("#",""));[...a.parentNode.children].forEach(e=>{e.classList.toggle("active",e===a)}), 41// Trigger event 42a.dispatchEvent(new Event("tabs:click",{bubbles:!0})); 43// Get the height of `tab-pane` which is activated now. 44const l=document.body.scrollHeight>(window.innerHeight||document.documentElement.clientHeight),d=parseInt(window.getComputedStyle(o.querySelector(".active")).height,10);if( 45// Reset the height of `tab-content` and see the animation. 46o.style.height=d+c+s+"px", 47// Change the height of `tab-content` may cause scrollbar show / disappear, which may result in the change of the `tab-pane`'s height 48setTimeout(()=>{if(document.body.scrollHeight>(window.innerHeight||document.documentElement.clientHeight)!==l){o.style.transition="height 0.3s linear"; 49// After the animation, we need reset the height of `tab-content` again. 50const e=parseInt(window.getComputedStyle(o.querySelector(".active")).height,10);o.style.height=e+c+s+"px"} 51// Remove all the inline styles, and let the height be adaptive again. 52setTimeout(()=>{o.style.transition="",o.style.height=""},250)},1e3),!CONFIG.stickytabs)return;const u=n.parentNode.getBoundingClientRect().top+window.scrollY+10;NexT.utils.scrollTo(window,u)})}),window.dispatchEvent(new Event("tabs:register"))},registerCanIUseTag(){ 53// Get responsive height passed from iframe. 54window.addEventListener("message",({data:e})=>{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(){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(){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}), 55// Disable Pjax to force refresh translation of menu item 56window.location.href=t.dataset.href})})},registerSidebarTOC(){this.sections=[...document.querySelectorAll(".post-toc:not(.placeholder-toc) li a.nav-link")].map(e=>{const t=document.getElementById(decodeURI(e.getAttribute("href")).replace("#","")); 57// TOC item animation navigate. 58return e.addEventListener("click",n=>{n.preventDefault();const o=t.getBoundingClientRect().top+window.scrollY;NexT.utils.scrollTo(window,o),history.pushState(null,document.title,e.href)}),t}),this.updateActiveNav()},registerPostReward(){const e=document.querySelector(".reward-container button");e&&e.addEventListener("click",()=>{document.querySelector(".post-reward").classList.toggle("active")})},activateNavByIndex(e){const t=document.querySelector(".post-toc:not(.placeholder-toc) .nav");if(!t)return;const n=t.querySelectorAll(".nav-item")[e];if(!n||n.classList.contains("active-current"))return;
58t.querySelectorAll(".active").forEach(e=>{e.classList.remove("active","active-current")}),n.classList.add("active","active-current");let o=n.querySelector(".nav-child")||n.parentElement,i=0;for(;t.contains(o);){if(o.classList.contains("nav-item"))o.classList.add("active");else{ 59// The last nav-item in a list has a margin-bottom of 5px. 60i+=[...o.children].reduce((e,t)=>{const n=[...t.children].find(e=>e.classList.contains("nav-child"));return e+t.getBoundingClientRect().height-(n?.getBoundingClientRect().height||0)},0)+5,o.style.setProperty("--height",`${i}px`)}o=o.parentElement} 61// Scrolling to center active TOC element if TOC content is taller then viewport. 62const r=document.querySelector("Pisces"===CONFIG.scheme||"Gemini"===CONFIG.scheme?".sidebar-panel-container":".sidebar");document.querySelector(".sidebar-toc-active")&&NexT.utils.scrollTo(r,r.scrollTop-r.offsetHeight/2+n.getBoundingClientRect().top-r.getBoundingClientRect().top)},updateSidebarPosition(){if(window.innerWidth<1200||"Pisces"===CONFIG.scheme||"Gemini"===CONFIG.scheme)return; 63// Expand sidebar on post detail page by default, when post has a toc. 64const e=document.querySelector(".post-toc:not(.placeholder-toc)");let t=CONFIG.page.sidebar;"boolean"!=typeof t&&( 65// There's no definition sidebar in the page front-matter. 66t="always"===CONFIG.sidebar.display||"post"===CONFIG.sidebar.display&&e),t&&window.dispatchEvent(new Event("sidebar:show"))},activateSidebarPanel(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; 67// For TOC activation, try to use the animated TOC height 68if(0===e){const e=i.querySelector(".nav");e&&(c=parseInt(e.style.getPropertyValue("--height"),10))}const s=[c,r.scrollHeight];o.style.setProperty("--inactive-panel-height",`${s[1-e]}px`),o.style.setProperty("--active-panel-height",`${s[e]}px`),t.classList.replace(n[1-e],n[e])},updateFooterPosition(){function e(){const e=document.querySelector(".footer"),t=document.querySelector(".main").offsetHeight+e.offsetHeight;e.classList.toggle("footer-fixed",t<=window.innerHeight)}"Pisces"!==CONFIG.scheme&&"Gemini"!==CONFIG.scheme&&(e(),window.addEventListener("resize",e),window.addEventListener("scroll",e,{passive:!0}))}, 69/** 70 * Sets the CSS variable '--dialog-scrollgutter' to the specified gap value. 71 * If no gap is provided, it calculates the gap as the difference between 72 * the window's inner width and the document body's client width. 73 * 74 * @param {string} [gap] - The gap value to be set. If not provided, the 75 * default gap is calculated automatically. 76 */ 77setGutter(e){document.body.style.setProperty("--dialog-scrollgutter",e||window.innerWidth-document.body.clientWidth+"px")},getScript(e,t={},n){if("function"==typeof t)return this.getScript(e,{condition:n}).then(t);const{condition:o=!1,attributes:{id:i="",defer:r=!1,crossOrigin:c="",dataset:s={},...a}={},parentNode:l=null}=t,d=t.async??!1;return new Promise((t,n)=>{if(o)t();else{const o=document.createElement("script");if(i&&(o.id=i),c&&(o.crossOrigin=c),o.async=d,o.defer=r,Object.assign(o.dataset,s),Object.entries(a).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;(l||document.head).appendChild(o)}})},loadComments(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)})},debounce(e,t){let n;return function(...o){const i=this;clearTimeout(n),n=setTimeout(()=>e.apply(i,o),t)}}};
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.