29 lines
19 KiB
JavaScript
Executable File
29 lines
19 KiB
JavaScript
Executable File
const MIN_SEARCH_QUERY_LENGTH=2,SEARCH_RESULTS_LIMIT=10,SEARCH_DEBOUNCE_DELAY=200,SEARCH_INDEX_URL="/index.json",MODAL_FOCUS_DELAY=100,SCROLL_THRESHOLD=300,WIDE_SCREEN_BREAKPOINT=1024,MOBILE_BREAKPOINT=768,TOC_SCROLL_OFFSET=100,TOC_RESIZE_DEBOUNCE=150,DEBOUNCE_DELAY=200;function debounce(e,t){let n;return function(...s){const o=()=>{clearTimeout(n),e(...s)};clearTimeout(n),n=setTimeout(o,t)}}function throttle(e){let t=!1;return function(...n){t||(window.requestAnimationFrame(()=>{e(...n),t=!1}),t=!0)}}function isWideScreen(){return window.innerWidth>WIDE_SCREEN_BREAKPOINT}function isMobileScreen(){return window.innerWidth<=MOBILE_BREAKPOINT}function escapeRegex(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function formatDate(e){if(!e)return"";try{const t=new Date(e),n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];return`${n[t.getMonth()]} ${t.getDate()}, ${t.getFullYear()}`}catch(t){return console.error("Error formatting date:",t),e}}function initOnReady(e){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}const THEME_STORAGE_KEY="theme",THEME_LIGHT="light",THEME_DARK="dark";function getInitialTheme(){const e=localStorage.getItem(THEME_STORAGE_KEY);if(e)return e;const t=window.matchMedia("(prefers-color-scheme: light)").matches;return t?THEME_LIGHT:THEME_DARK}function setTheme(e){const t=document.documentElement;t.setAttribute("data-theme",e),localStorage.setItem(THEME_STORAGE_KEY,e),updateThemeToggleIcon(e)}function updateThemeToggleIcon(e){const t=document.getElementById("theme-toggle");if(!t)return;const n=t.querySelector(".icon-sun"),s=t.querySelector(".icon-moon");e===THEME_LIGHT?(n?.setAttribute("style","display: none;"),s?.setAttribute("style","display: block;")):(n?.setAttribute("style","display: block;"),s?.setAttribute("style","display: none;"))}function toggleTheme(){const e=document.documentElement,t=e.getAttribute("data-theme"),n=t===THEME_LIGHT?THEME_DARK:THEME_LIGHT;setTheme(n)}function initThemeToggle(){const e=document.getElementById("theme-toggle");if(!e)return;const t=getInitialTheme();setTheme(t),window.matchMedia("(prefers-color-scheme: light)").addEventListener("change",e=>{localStorage.getItem(THEME_STORAGE_KEY)||setTheme(e.matches?THEME_LIGHT:THEME_DARK)}),e.addEventListener("click",toggleTheme)}initOnReady(initThemeToggle);let searchIndex=[],searchTimeout=null;async function loadSearchIndex(){try{const e=await fetch(SEARCH_INDEX_URL);e.ok?searchIndex=await e.json():console.error("Failed to load search index:",e.status)}catch(e){console.error("Failed to load search index:",e)}}function toggleSearchResultsVisibility(){const e=document.getElementById("search-results");if(!e)return;const t=e.innerHTML.trim().length>0;e.style.display=t?"block":"none"}function highlightMatch(e,t){if(!e||!t)return e;const s=t.endsWith(" "),n=t.trim();if(!n)return e;const o=escapeRegex(n),i=new RegExp(`(${o})`,"gi");return e.replace(i,e=>s?`<mark>${e} </mark>`:`<mark>${e}</mark>`)}function performSearch(e){const t=document.getElementById("search-results");if(!t)return;const s=e?e.trim():"";if(!s||s.length<MIN_SEARCH_QUERY_LENGTH){t.innerHTML="",toggleSearchResultsVisibility();return}const n=s.toLowerCase(),o=new Set,i=searchIndex.filter(e=>{if(!e||!e.permalink)return!1;if(o.has(e.permalink))return!1;o.add(e.permalink);const t=e.title?.toLowerCase().includes(n),s=e.summary?.toLowerCase().includes(n),i=e.content?.toLowerCase().includes(n),a=e.tags?.some(e=>e.toLowerCase().includes(n));return t||s||i||a}).slice(0,SEARCH_RESULTS_LIMIT);if(i.length===0){t.innerHTML='<div class="search-result-empty">No results found</div>',toggleSearchResultsVisibility();return}t.innerHTML=i.map(t=>`
|
||
<a href="${t.permalink}" class="search-result-item">
|
||
<div class="search-result-title">${highlightMatch(t.title||"",e)}</div>
|
||
${t.summary?`<div class="search-result-summary">${highlightMatch(t.summary.substring(0,150),e)}...</div>`:""}
|
||
${t.date?`<div class="search-result-date">
|
||
<svg class="search-result-date-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
||
</svg>
|
||
${formatDate(t.date)}
|
||
</div>`:""}
|
||
</a>
|
||
`).join(""),toggleSearchResultsVisibility()}function toggleClearButton(){const e=document.getElementById("search-input-clear"),t=document.getElementById("search-input");if(!e||!t)return;t.value.length>0?e.classList.add("visible"):e.classList.remove("visible")}function openSearchModal(){const e=document.getElementById("search-modal");if(!e)return;e.setAttribute("aria-hidden","false"),document.body.style.overflow="hidden",setTimeout(()=>{const e=document.getElementById("search-input");e?.focus(),toggleClearButton()},MODAL_FOCUS_DELAY)}function closeSearchModal(){const t=document.getElementById("search-modal");if(!t)return;t.setAttribute("aria-hidden","true"),document.body.style.overflow="";const n=document.getElementById("search-input"),e=document.getElementById("search-results");n&&(n.value="",toggleClearButton()),e&&(e.innerHTML="",e.style.display="none")}function setupSearchInput(){const e=document.getElementById("search-input");if(!e)return;const t=debounce(e=>{performSearch(e)},SEARCH_DEBOUNCE_DELAY);e.addEventListener("input",e=>{const n=e.target.value;toggleClearButton(),t(n)}),toggleClearButton()}function setupClearButton(){const e=document.getElementById("search-input-clear");if(!e)return;e.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation();const t=document.getElementById("search-input"),n=document.getElementById("search-results");t&&(t.value="",t.focus(),toggleClearButton(),n&&(n.innerHTML="",n.style.display="none"),t.dispatchEvent(new Event("input",{bubbles:!0})))})}function setupKeyboardShortcuts(){document.addEventListener("keydown",e=>{const t=document.getElementById("search-modal");e.key==="Escape"&&t?.getAttribute("aria-hidden")==="false"&&closeSearchModal()})}function setupModalClickHandling(){const e=document.getElementById("search-modal"),t=e?.querySelector(".search-modal-container");t?.addEventListener("click",e=>{e.stopPropagation()})}function initSearch(){const e=document.getElementById("search-toggle"),t=document.getElementById("search-modal-backdrop"),n=document.getElementById("search-modal-close");e&&e.addEventListener("click",openSearchModal),t&&t.addEventListener("click",closeSearchModal),n&&n.addEventListener("click",closeSearchModal),setupSearchInput(),setupClearButton(),setupKeyboardShortcuts(),setupModalClickHandling(),loadSearchIndex()}(function(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",initSearch):initSearch()})();function toggleScrollButton(){const e=document.getElementById("scroll-to-top");if(!e)return;window.scrollY>SCROLL_THRESHOLD?e.classList.add("visible"):e.classList.remove("visible")}function scrollToTop(){window.scrollTo({top:0,behavior:"smooth"})}function initScrollToTop(){const e=document.getElementById("scroll-to-top");if(!e)return;window.addEventListener("scroll",toggleScrollButton),e.addEventListener("click",scrollToTop)}(function(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",initScrollToTop):initScrollToTop()})();function initGlassDockNavigation(){const t=document.querySelector(".glass-dock-toggle"),e=document.querySelector(".glass-dock-list");if(!t||!e)return;t.addEventListener("click",function(){const t=this.getAttribute("aria-expanded")==="true";this.setAttribute("aria-expanded",!t),e.classList.toggle("mobile-open",!t)}),e.querySelectorAll(".glass-dock-link").forEach(n=>{n.addEventListener("click",function(){window.innerWidth<=MOBILE_BREAKPOINT&&(t.setAttribute("aria-expanded","false"),e.classList.remove("mobile-open"))})}),document.addEventListener("click",function(n){window.innerWidth<=MOBILE_BREAKPOINT&&!t.contains(n.target)&&!e.contains(n.target)&&(t.setAttribute("aria-expanded","false"),e.classList.remove("mobile-open"))})}function initSmoothScroll(){document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){const t=this.getAttribute("href");if(t==="#"||t.length<=1)return;const n=document.querySelector(t);n&&(e.preventDefault(),n.scrollIntoView({behavior:"smooth",block:"start"}))})})}function initPostCardClick(){document.querySelectorAll(".post-card[data-post-url]").forEach(e=>{e.addEventListener("click",function(e){if(e.target.closest("a"))return;const t=this.getAttribute("data-post-url");t&&(window.location.href=t)})})}function initNavigation(){initGlassDockNavigation(),initSmoothScroll(),initPostCardClick()}(function(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",initNavigation):initNavigation()})();function isWideScreen(){return window.innerWidth>WIDE_SCREEN_BREAKPOINT}function expandTOC(e,t){e.setAttribute("aria-expanded","true"),t.classList.add("expanded")}function collapseTOC(e,t){e.setAttribute("aria-expanded","false"),t.classList.remove("expanded")}function toggleTOC(e,t){const n=e.getAttribute("aria-expanded")==="true";n?collapseTOC(e,t):expandTOC(e,t)}function initializeTOCState(e,t){isWideScreen()?expandTOC(e,t):collapseTOC(e,t)}function updateActiveTOCItem(e,t,n){const o=window.scrollY+TOC_SCROLL_OFFSET;let s=null;for(let t=e.length-1;t>=0;t--){const{element:n}=e[t];if(n&&n.offsetTop<=o){s=e[t];break}}if(t.forEach(e=>e.classList.remove("active")),s&&(s.link.classList.add("active"),n.classList.contains("expanded")&&s.link)){const e=s.link.offsetTop,t=s.link.offsetHeight,o=n.offsetHeight,i=n.scrollTop;e<i?n.scrollTo({top:e-20,behavior:"smooth"}):e+t>i+o&&n.scrollTo({top:e-o+t+20,behavior:"smooth"})}}function initTOC(){const i=document.getElementById("post-toc"),t=document.getElementById("toc-toggle"),e=document.getElementById("toc-content");if(!i||!t||!e)return;const n=i.querySelectorAll('a[href^="#"]'),s=Array.from(n).map(e=>{const n=e.getAttribute("href"),t=n.substring(1),s=document.getElementById(t);return{link:e,element:s,id:t}}).filter(e=>e.element);t.addEventListener("click",()=>{toggleTOC(t,e)});let a;if(window.addEventListener("resize",()=>{clearTimeout(a),a=setTimeout(()=>{initializeTOCState(t,e)},TOC_RESIZE_DEBOUNCE)}),initializeTOCState(t,e),s.length===0)return;let o=!1;window.addEventListener("scroll",()=>{o||(window.requestAnimationFrame(()=>{updateActiveTOCItem(s,n,e),o=!1}),o=!0)}),updateActiveTOCItem(s,n,e)}(function(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",initTOC):initTOC()})();function initFilters(){const r=document.getElementById("filter-year-month"),j=document.getElementById("clear-filters"),h=document.getElementById("posts-container"),f=document.getElementById("filtered-count"),n=document.getElementById("tag-search-input"),t=document.getElementById("tag-search-results"),l=document.getElementById("selected-tags-container"),b=document.getElementById("all-tags-data");if(!h)return;const m=Array.from(b?.querySelectorAll("[data-tag]")||[]).map(e=>e.getAttribute("data-tag")),o=new Set;let e=-1;function p(e){const t=document.createElement("label");return t.className="filter-tag-checkbox",t.innerHTML=`
|
||
<input type="checkbox" value="${e}" class="filter-tag-input" checked>
|
||
<span class="filter-tag-label">${e}</span>
|
||
`,t}function c(e){if(o.has(e))return;o.add(e);const t=p(e);t.querySelector(".filter-tag-input").addEventListener("change",function(){this.checked||g(e),a()}),l.appendChild(t),a()}function g(e){o.delete(e);const t=l.querySelector(`input[value="${e}"]`)?.closest(".filter-tag-checkbox");t&&t.remove(),a()}function u(s){if(!t)return;const i=s.toLowerCase().trim();if(!i){t.innerHTML="",t.style.display="none",e=-1;return}const a=m.filter(e=>e.toLowerCase().includes(i)&&!o.has(e));if(a.length===0){t.innerHTML='<div class="tag-search-no-results">No tags found</div>',t.style.display="block",e=-1;return}t.innerHTML=a.map((t,n)=>`<div class="tag-search-result-item ${n===e?"selected":""}" data-tag="${t}" data-index="${n}">${t}</div>`).join(""),t.style.display="block",t.querySelectorAll(".tag-search-result-item").forEach(s=>{s.addEventListener("click",()=>{const o=s.getAttribute("data-tag");c(o),n.value="",t.style.display="none",e=-1,n.focus()})}),d()}function d(){const n=t.querySelectorAll(".tag-search-result-item");n.forEach((t,n)=>{n===e?t.classList.add("selected"):t.classList.remove("selected")})}function v(){const s=t.querySelectorAll(".tag-search-result-item");if(s.length===0){const i=n.value.trim(),s=m.find(e=>e.toLowerCase()===i.toLowerCase()&&!o.has(e));if(s){c(s),n.value="",t.style.display="none",e=-1;return}return}if(e>=0&&e<s.length){const o=s[e],i=o.getAttribute("data-tag");c(i),n.value="",t.style.display="none",e=-1}else if(s.length>0){const o=s[0],i=o.getAttribute("data-tag");c(i),n.value="",t.style.display="none",e=-1}}function a(){const e=r?.value||"",t=Array.from(o),s=h.querySelectorAll(".post-card");let n=0;s.forEach(s=>{const o=s.getAttribute("data-year-month")||"",i=s.getAttribute("data-tags")?.split(",")||[],a=!e||o===e,r=t.length===0||t.some(e=>i.includes(e));a&&r?(s.style.display="",n++):s.style.display="none"}),f&&(f.textContent=n)}r?.addEventListener("change",a),n&&(n.addEventListener("input",t=>{e=-1,u(t.target.value)}),n.addEventListener("focus",()=>{n.value.trim()&&u(n.value)}),n.addEventListener("keydown",s=>{const o=t.querySelectorAll(".tag-search-result-item");s.key==="ArrowDown"?(s.preventDefault(),o.length>0&&(e=e<o.length-1?e+1:0,d(),o[e]&&o[e].scrollIntoView({block:"nearest",behavior:"smooth"}))):s.key==="ArrowUp"?(s.preventDefault(),o.length>0&&(e=e>0?e-1:o.length-1,d(),o[e]&&o[e].scrollIntoView({block:"nearest",behavior:"smooth"}))):s.key==="Enter"?(s.preventDefault(),v()):s.key==="Escape"&&(t.style.display="none",e=-1,n.blur())}),document.addEventListener("click",s=>{!n.contains(s.target)&&!t.contains(s.target)&&(t.style.display="none",e=-1)})),j?.addEventListener("click",()=>{r&&(r.value=""),o.clear(),l.innerHTML="",n&&(n.value=""),t&&(t.style.display="none"),a()});const i=document.getElementById("filter-toggle-btn"),s=document.getElementById("posts-filter-content");i&&s&&(s.style.display="flex",i.addEventListener("click",()=>{const e=i.getAttribute("aria-expanded")==="true";if(e)s.style.maxHeight=s.scrollHeight+"px",s.offsetHeight,s.style.maxHeight="0px",s.classList.remove("expanded");else{s.style.maxHeight="none";const e=s.scrollHeight;s.style.maxHeight="0px",s.offsetHeight,s.style.maxHeight=Math.max(e,300)+"px",s.classList.add("expanded")}i.setAttribute("aria-expanded",!e),i.classList.toggle("expanded",!e)}))}(function(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",initFilters):initFilters()})();function initCollapsibleCodeBlocks(){const e=document.querySelector(".post-content-main");if(!e)return;const t=e.querySelectorAll(".highlight"),n=Array.from(e.querySelectorAll("pre")).filter(e=>!e.closest(".highlight")),s=[...t,...n];s.forEach(e=>{let n=e.closest(".code-block-wrapper"),s=!1;n||(n=document.createElement("div"),n.className="code-block-wrapper collapsed",s=!0);let t=n.querySelector(".code-block-copy");if(!t){t=document.createElement("button"),t.className="code-block-copy",t.setAttribute("aria-label","Copy code"),t.innerHTML=`
|
||
<svg class="copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||
</svg>
|
||
<svg class="checkmark-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display: none;">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||
</svg>
|
||
<span class="copy-text">Copy</span>
|
||
<span class="copied-text" style="display: none;">Copied!</span>
|
||
`;const o=()=>{const t=e.querySelector("code")||e;return t.textContent||t.innerText||""};t.addEventListener("click",async e=>{e.stopPropagation();const s=o(),n=window.innerWidth<=768;try{await navigator.clipboard.writeText(s);const e=t.querySelector(".copy-text"),o=t.querySelector(".copied-text"),i=t.querySelector(".copy-icon"),a=t.querySelector(".checkmark-icon");t.setAttribute("aria-label","Code copied"),i.style.display="none",a.style.display="block",n||(e.style.display="none",o.style.display="inline"),t.classList.add("copied"),t.setAttribute("aria-label","Code copied"),setTimeout(()=>{a.style.display="none",i.style.display="block",n||(o.style.display="none",e.style.display="inline"),t.classList.remove("copied"),t.setAttribute("aria-label","Copy code")},2e3)}catch{const e=document.createElement("textarea");e.value=s,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy");const e=t.querySelector(".copy-text"),s=t.querySelector(".copied-text"),o=t.querySelector(".copy-icon"),i=t.querySelector(".checkmark-icon");o.style.display="none",i.style.display="block",n||(e.style.display="none",s.style.display="inline"),t.classList.add("copied"),t.setAttribute("aria-label","Code copied"),setTimeout(()=>{o.style.display="block",i.style.display="none",n||(e.style.display="inline",s.style.display="none"),t.classList.remove("copied"),t.setAttribute("aria-label","Copy code")},2e3)}catch(e){console.error("Failed to copy code:",e)}document.body.removeChild(e)}});const s=n.querySelector(".code-block-toggle");s?n.insertBefore(t,s):n.insertBefore(t,n.firstChild)}if(s){const t=document.createElement("button");t.className="code-block-toggle",t.setAttribute("aria-label","Toggle code block"),t.setAttribute("aria-expanded","false"),t.innerHTML=`
|
||
<span>Expand</span>
|
||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7"></path>
|
||
</svg>
|
||
`;const s=document.createElement("div");s.className="code-block-content",e.parentNode.insertBefore(n,e),n.appendChild(t),n.appendChild(s),s.appendChild(e),t.addEventListener("click",()=>{const e=n.classList.contains("collapsed");n.classList.toggle("collapsed"),t.setAttribute("aria-expanded",!e),t.querySelector("span").textContent=e?"Collapse":"Expand"})}})}(function(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",initCollapsibleCodeBlocks):initCollapsibleCodeBlocks()})(),function(){"use strict";function e(){const e=document.querySelectorAll(".alert-collapsible .alert-header");e.forEach(e=>{if(e.dataset.initialized==="true")return;e.dataset.initialized="true",e.addEventListener("click",function(){const e=this.closest(".alert-collapsible"),t=e.querySelector(".alert-content"),n=e.querySelector(".alert-toggle");if(!t||!n)return;const s=t.style.display==="none"||e.dataset.collapsed==="true";s?(t.style.display="block",n.textContent="−",e.dataset.collapsed="false"):(t.style.display="none",n.textContent="+",e.dataset.collapsed="true")})})}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e(),typeof window!="undefined"&&(window.initAlertToggles=e)}() |