1(function(){'use strict';const ROOT_ID='sticky-toc';const ACTIVATION_OFFSET_PX=16;const PANEL_CLOSE_DELAY_MS=60;const ACTIVE_LOCK_MS=900;const DOCUMENT_BOTTOM_TOLERANCE_PX=4;const HEADER_ID='header-v2';const STATEBAR_SELECTOR='.statebar-block';const CONTENT_SELECTOR='section.entry-content';const TOPLIST_SELECTOR='.toplist-container';const FOOTER_ID='footer';class StickyToc{constructor(root){this.root=root;this.toggle=root.querySelector('.sticky-toc-toggle');this.panel=root.querySelector('.sticky-toc-panel');this.activeTitle=root.querySelector('.sticky-toc-active');this.pctLabel=root.querySelector('.sticky-toc-pct');this.progress=root.querySelector('.sticky-toc-progress');this.progressFill=root.querySelector('.sticky-toc-progress-fill');this.links=Array.from(root.querySelectorAll('.sticky-toc-link'));this.targets=this.links.map((link)=>StickyToc.resolveTarget(link));this.content=document.querySelector(CONTENT_SELECTOR)||this.targets.find(Boolean)||null;this.header=document.getElementById(HEADER_ID);this.statebar=document.querySelector(STATEBAR_SELECTOR);this.footer=document.getElementById(FOOTER_ID);this.pctTemplate=this.pctLabel?this.pctLabel.dataset.template||'[pct]%':'[pct]%';this.state={pinned:!1,open:!1,active:0,pct:-1,stackBottom:0,stackHeight:0,};this.ticking=!1;this.closeTimer=0;this.lockedActive=-1;this.lockTimer=0} 2static resolveTarget(link){const hash=link.hash||'';if(hash.length<2){return null} 3let id=hash.slice(1);try{id=decodeURIComponent(id)}catch(e){} 4return document.getElementById(id)} 5static prefersReducedMotion(){return(window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches)} 6init(){if(!this.toggle||!this.panel||!this.content){return} 7this.onScroll=()=>this.requestMeasure();document.addEventListener('scroll',this.onScroll,{passive:!0,capture:!0,});window.addEventListener('resize',this.onScroll);this.toggle.addEventListener('click',()=>this.setOpen(!this.state.open));this.links.forEach((link,index)=>link.addEventListener('click',(event)=>this.onLinkClick(event,index)));document.addEventListener('keydown',(event)=>this.onKeydown(event));document.addEventListener('click',(event)=>this.onDocumentClick(event));window.addEventListener('hashchange',()=>this.syncToHash());window.addEventListener('load',()=>this.syncToHash(),{once:!0,});document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='visible'){this.measure()}});this.render();this.measure();this.syncToHash()} 8indexForHash(hash){if(!hash||hash.length<2){return-1} 9let id=hash.slice(1);try{id=decodeURIComponent(id)}catch(e){} 10return this.targets.findIndex((target)=>target&&target.id===id)} 11syncToHash(){const index=this.indexForHash(window.location.hash);if(index<0){return} 12this.measure();this.scrollToTarget(index,'auto');this.lockActive(index)} 13lockActive(index){window.clearTimeout(this.lockTimer);this.lockedActive=index;this.lockTimer=window.setTimeout(()=>{this.lockedActive=-1;this.requestMeasure()},ACTIVE_LOCK_MS);if(this.state.active!==index){this.state.active=index;this.render();if(this.state.open){this.scrollActiveIntoView()}}} 14scrollToTarget(index,behavior){const target=this.targets[index];if(!target){return} 15const styles=window.getComputedStyle(document.documentElement);const barHeight=parseFloat(styles.getPropertyValue('--bf-sticky-toc-h'))||this.root.offsetHeight;const gap=parseFloat(styles.getPropertyValue('--bf-sticky-toc-jump-gap'))||12;const offset=this.measureStackHeight()+barHeight+gap;const top=Math.max(0,target.getBoundingClientRect().top+window.scrollY-offset);window.scrollTo({top,behavior})} 16requestMeasure(){if(this.ticking){return} 17this.ticking=!0;window.requestAnimationFrame(()=>{this.ticking=!1;this.measure()})} 18measureStackBottom(){let bottom=0;if(this.header){bottom=this.header.getBoundingClientRect().bottom} 19if(this.statebar){const rect=this.statebar.getBoundingClientRect();const isStuckUnderHeader=rect.height>0&&rect.top<=bottom+1&&rect.bottom>bottom;if(isStuckUnderHeader){bottom=rect.bottom}} 20return Math.max(0,bottom)} 21measureStackHeight(){let height=0;if(this.header){const stickyTop=parseFloat(window.getComputedStyle(this.header).top)||0;height=Math.max(0,stickyTop)+this.header.offsetHeight} 22if(this.statebar){height+=this.statebar.offsetHeight} 23return height} 24measureContentTop(){let top=this.content.getBoundingClientRect().top;const first=this.targets.find(Boolean);if(first){top=Math.min(top,first.getBoundingClientRect().top)}
25document.querySelectorAll(TOPLIST_SELECTOR).forEach((toplist)=>{top=Math.max(top,toplist.getBoundingClientRect().bottom)});return top} 26measurePercent(viewportHeight){const rect=this.content.getBoundingClientRect();let bottom=rect.bottom;const top=this.measureContentTop();for(let i=this.targets.length-1;i>=0;i--){if(this.targets[i]){bottom=Math.max(bottom,this.targets[i].getBoundingClientRect().bottom);break}} 27const span=Math.max(1,bottom-top-viewportHeight);let pct=Math.round((-top/span)*100);const atDocumentBottom=window.scrollY+viewportHeight>=document.documentElement.scrollHeight-DOCUMENT_BOTTOM_TOLERANCE_PX;if(atDocumentBottom){pct=100} 28return Math.max(0,Math.min(100,pct))} 29measureActive(line){let active=0;for(let i=0;i<this.targets.length;i++){const target=this.targets[i];if(target&&target.getBoundingClientRect().top<=line){active=i}} 30return active} 31measure(){const viewportHeight=window.innerHeight;const stackBottom=this.measureStackBottom();const stackHeight=this.measureStackHeight();const contentTop=this.measureContentTop();const footerReached=!!this.footer&&this.footer.getBoundingClientRect().top<viewportHeight;const pinned=contentTop<=stackBottom&&!footerReached;const barHeight=pinned?this.root.offsetHeight:0;const line=stackBottom+barHeight+ACTIVATION_OFFSET_PX;const active=this.lockedActive>=0?this.lockedActive:this.measureActive(line);const pct=this.measurePercent(viewportHeight);const changed=pinned!==this.state.pinned||active!==this.state.active||pct!==this.state.pct||stackBottom!==this.state.stackBottom||stackHeight!==this.state.stackHeight;if(!changed){return} 32const activeChanged=active!==this.state.active;this.state={...this.state,pinned,active,pct,stackBottom,stackHeight,};if(!pinned&&this.state.open){this.setOpen(!1)} 33this.render();if(activeChanged&&this.state.open){this.scrollActiveIntoView()}} 34render(){const{pinned,active,pct,stackBottom,stackHeight}=this.state;document.documentElement.style.setProperty('--bf-sticky-toc-top',Math.floor(stackBottom)+'px');document.documentElement.style.setProperty('--bf-sticky-toc-stack-h',stackHeight+'px');this.root.dataset.pinned=pinned?'true':'false';this.links.forEach((link,index)=>{if(index===active){link.setAttribute('aria-current','location')}else{link.removeAttribute('aria-current')} 35link.dataset.visited=index<active?'true':'false'});const activeLink=this.links[active];if(this.activeTitle&&activeLink){const title=activeLink.querySelector('.sticky-toc-title');this.activeTitle.textContent=title?title.textContent:activeLink.textContent} 36const shownPct=Math.max(0,pct);if(this.pctLabel){this.pctLabel.textContent=this.pctTemplate.replace('[pct]',String(shownPct))} 37if(this.progressFill){this.progressFill.style.width=shownPct+'%'} 38if(this.progress){this.progress.setAttribute('aria-valuenow',String(shownPct))}} 39setOpen(open){window.clearTimeout(this.closeTimer);this.state.open=open;this.root.dataset.open=open?'true':'false';this.toggle.setAttribute('aria-expanded',open?'true':'false');if(open){this.scrollActiveIntoView()}} 40scrollActiveIntoView(){const activeLink=this.links[this.state.active];if(activeLink&&typeof activeLink.scrollIntoView==='function'){activeLink.scrollIntoView({block:'nearest'})}} 41onLinkClick(event,index){const target=this.targets[index];if(!target){this.closeTimer=window.setTimeout(()=>this.setOpen(!1),PANEL_CLOSE_DELAY_MS);return} 42event.preventDefault();this.scrollToTarget(index,StickyToc.prefersReducedMotion()?'auto':'smooth');this.lockActive(index);if(window.history&&window.history.replaceState){window.history.replaceState(null,'','#'+target.id)} 43if(!target.hasAttribute('tabindex')){target.setAttribute('tabindex','-1')} 44target.focus({preventScroll:!0});this.closeTimer=window.setTimeout(()=>this.setOpen(!1),PANEL_CLOSE_DELAY_MS)} 45onKeydown(event){if(event.key==='Escape'&&this.state.open){this.setOpen(!1);this.toggle.focus()}} 46onDocumentClick(event){if(this.state.open&&!this.root.contains(event.target)){this.setOpen(!1)}}} 47function startHeaderBottomVar(){const header=document.getElementById(HEADER_ID);if(!header){return} 48let ticking=!1;const apply=()=>{ticking=!1;const bottom=Math.max(0,Math.floor(header.getBoundingClientRect().bottom));document.documentElement.style.setProperty('--bf-header-bottom',bottom+'px')};const request=()=>{if(!ticking){ticking=!0;
48window.requestAnimationFrame(apply)}};document.addEventListener('scroll',request,{passive:!0,capture:!0,});window.addEventListener('resize',request);window.addEventListener('load',apply,{once:!0});window.addEventListener('hashchange',apply);document.addEventListener('visibilitychange',()=>{if(document.visibilityState==='visible'){apply()}});apply()} 49function boot(){startHeaderBottomVar();const root=document.getElementById(ROOT_ID);if(root){const instance=new StickyToc(root);instance.init();window.bfStickyToc=instance}} 50if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',boot)}else{boot()}})()
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.