1/** 2 * 2008 - 2024 (c) Prestablog 3 * 4 * MODULE PrestaBlog 5 * 6 * @author Prestablog 7 * @copyright Copyright (c) permanent, Prestablog 8 * @license Commercial 9 */ 10 11// Global function for the onclick attribute in templates 12function toggleTOC(element) { 13 const toc = element.closest('.prestablog-toc'); 14 if (toc) { 15 // Toggle the collapsed class 16 if (toc.classList.contains('collapsed')) { 17 toc.classList.remove('collapsed'); 18 19 // Store state in localStorage 20 if (toc.id) { 21 localStorage.setItem('prestablog_toc_' + toc.id, 'expanded'); 22 } 23 24 // Réactiver le placeholder si nécessaire 25 const tocWrapper = toc.closest('.toc-wrapper'); 26 if (tocWrapper) { 27 const placeholder = tocWrapper.querySelector('.toc-placeholder'); 28 if (placeholder) { 29 placeholder.style.display = 'block'; 30 } 31 } 32 } else { 33 toc.classList.add('collapsed'); 34 35 // Store state in localStorage 36 if (toc.id) { 37 localStorage.setItem('prestablog_toc_' + toc.id, 'collapsed'); 38 } 39 40 // Masquer le placeholder 41 const tocWrapper = toc.closest('.toc-wrapper'); 42 if (tocWrapper) { 43 const placeholder = tocWrapper.querySelector('.toc-placeholder'); 44 if (placeholder) { 45 placeholder.style.display = 'none'; 46 } 47 } 48 } 49 } 50} 51 52// Initialize TOC on page load 53document.addEventListener('DOMContentLoaded', function() { 54 // Restore saved state from localStorage 55 const allTocs = document.querySelectorAll('.prestablog-toc'); 56 allTocs.forEach(function(toc) { 57 if (toc.id) { 58 const savedState = localStorage.getItem('prestablog_toc_' + toc.id); 59 if (savedState === 'expanded') { 60 toc.classList.remove('collapsed'); 61 } else { 62 // Default is collapsed 63 toc.classList.add('collapsed'); 64 } 65 } 66 }); 67 68 // Highlight active section while scrolling 69 const tocLinks = document.querySelectorAll('.prestablog-toc-link'); 70 const headings = []; 71 72 // Get all heading elements that have IDs 73 tocLinks.forEach(function(link) { 74 const targetId = link.getAttribute('href').substring(1); 75 const targetHeading = document.getElementById(targetId); 76 if (targetHeading) { 77 headings.push({ 78 element: targetHeading, 79 link: link 80 }); 81 } 82 }); 83 84 // Set up scroll event to highlight current section 85 if (headings.length > 0) { 86 window.addEventListener('scroll', function() { 87 const scrollPosition = window.scrollY + 100; // Offset for better UX 88 89 // Find the current visible heading 90 let currentHeading = null; 91 for (let i = 0; i < headings.length; i++) { 92 const heading = headings[i]; 93 if (heading.element.offsetTop <= scrollPosition) { 94 currentHeading = heading; 95 } else { 96 break; 97 } 98 } 99 100 // Remove highlight from all links 101 tocLinks.forEach(function(link) { 102 link.classList.remove('active'); 103 }); 104 105 // Add highlight to current link 106 if (currentHeading) { 107 currentHeading.link.classList.add('active'); 108 } 109 }); 110 111 // Trigger scroll event once on page load 112 window.dispatchEvent(new Event('scroll')); 113 } 114 115 // Add smooth scrolling to TOC links 116 tocLinks.forEach(function(link) { 117 link.addEventListener('click', function(e) { 118 e.preventDefault(); 119 const targetId = this.getAttribute('href').substring(1); 120 const targetElement = document.getElementById(targetId); 121 122 if (targetElement) { 123 // Calculer un offset dynamique basé sur la taille du TOC et une marge fixe 124 let dynamicOffset = 150; // Valeur de base 125 126 // Obtenir le TOC et sa hauteur si possible 127 const tocElement = document.querySelector('.prestablog-toc'); 128 if (tocElement) { 129 if (tocElement.classList.contains('toc-fixed')) { 130 // Si le TOC est fixe, on prend sa hauteur réelle 131 dynamicOffset = tocElement.offsetHeight + 50; // 50px de marge supplémentaire 132 } else { 133 // Sinon on utilise une valeur plus importante pour être sûr 134 dynamicOffset = Math.max(200, tocElement.offsetHeight + 50); 135 } 136 } 137 138 // Position absolue dans le document 139 const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY; 140 141 // Défilement avec offset dynamique 142 window.scrollTo({ 143 top: targetPosition - dynamicOffset, 144 behavior: 'smooth' 145 }); 146 } 147 }); 148 }); 149 150 // Setup sticky TOC with JavaScript (no layout shift approach) 151 function setupStickyTOC() { 152 // Get floating TOCs, but ignore those inside Swiper 153 const floatTocs = document.querySelectorAll('.prestablog-toc.float-toc'); 154 const validTocs = Array.from(floatTocs).filter(toc => !toc.closest('.prestablogswip')); 155 156 if (validTocs.length === 0) { 157 return; 158 } 159 160 // Get content area for boundary detection 161 const contentArea = document.getElementById('prestablogfont'); 162 if (!contentArea) { 163 return; 164 } 165 166 // Process each valid TOC 167 validTocs.forEach(function(toc) { 168 // Create a wrapper that will contain both TOC and placeholder 169 const wrapper = document.createElement('div'); 170 wrapper.className = 'toc-wrapper'; 171 172 // Move the TOC into the wrapper 173 toc.parentNode.insertBefore(wrapper, toc); 174 wrapper.appendChild(toc); 175 176 // Create a placeholder - TOUJOURS visible mais transparent 177 const placeholder = document.createElement('div'); 178 placeholder.className = 'toc-placeholder'; 179 wrapper.appendChild(placeholder); 180 181 // Set placeholder dimensions IMMÃDIATEMENT 182 placeholder.style.width = toc.offsetWidth + 'px'; 183 placeholder.style.height = toc.offsetHeight + 'px'; 184 185 // Get initial position of the TOC 186 const tocOffsetTop = wrapper.offsetTop; 187 188 // Handle scroll event with throttling 189 let lastScrollTime = 0; 190 let scrollTimeout = null; 191 let lastScrollY = window.scrollY; 192 let scrollDirection = 'down'; 193 194 function handleScroll() { 195 // Throttle scroll events 196 const now = Date.now(); 197 if (now - lastScrollTime < 50) { // 50ms throttle 198 clearTimeout(scrollTimeout); 199 scrollTimeout = setTimeout(handleScroll, 50); 200 return; 201 }
202 lastScrollTime = now; 203 204 const scrollY = window.scrollY; 205 206 scrollDirection = scrollY > lastScrollY ? 'down' : 'up'; 207 lastScrollY = scrollY; 208 209 const contentBottom = contentArea.offsetTop + contentArea.offsetHeight; 210 211 // begin: 200px after the start of the content 212 const startThreshold = tocOffsetTop + 200; 213 214 // end: maintain visible until 100px after the end of the content 215 const endThreshold = contentBottom + 100; 216 217 // Check if we are in the zone where the TOC should be fixed 218 const shouldBeFixed = scrollY > startThreshold && scrollY < endThreshold; 219 220 if (shouldBeFixed) { 221 // If the TOC is not already fixed, make it fixed 222 if (!toc.classList.contains('toc-fixed')) { 223 toc.classList.add('toc-fixed'); 224 toc.style.width = placeholder.style.width; 225 toc.style.left = wrapper.getBoundingClientRect().left + 'px'; 226 } else { 227 toc.style.left = wrapper.getBoundingClientRect().left + 'px'; 228 } 229 } else { 230 if (toc.classList.contains('toc-fixed')) { 231 toc.classList.remove('toc-fixed'); 232 toc.style.width = ''; 233 toc.style.left = ''; 234 } 235 } 236 } 237 238 window.addEventListener('scroll', handleScroll, { passive: true }); 239 240 // Handle resize event to update dimensions 241 window.addEventListener('resize', function() { 242 placeholder.style.width = toc.offsetWidth + 'px'; 243 placeholder.style.height = toc.offsetHeight + 'px'; 244 245 if (toc.classList.contains('toc-fixed')) { 246 toc.style.width = placeholder.style.width; 247 toc.style.left = wrapper.getBoundingClientRect().left + 'px'; 248 } 249 }); 250 251 // Trigger initial check 252 handleScroll(); 253 }); 254 } 255 256 // Initialize TOC 257 setupStickyTOC(); 258});
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.