1/* 2* 2025 Sedestral 3* 4* NOTICE OF LICENSE 5* 6* This source file is subject to the Academic Free License (AFL 3.0) 7* that is bundled with this package in the file LICENSE.txt. 8* It is also available through the world-wide-web at this URL: 9* http://opensource.org/licenses/afl-3.0.php 10* If you did not receive a copy of the license and are unable to 11* obtain it through the world-wide-web, please send an email 12* to [email protected] so we can send you a copy immediately. 13* 14* DISCLAIMER 15* 16* Do not edit or add to this file if you wish to upgrade PrestaShop to newer 17* versions in the future. If you wish to customize PrestaShop for your 18* needs please refer to http://www.prestashop.com for more information. 19* 20* @author Sedestral <[email protected]> 21* @copyright 2025 Sedestral 22* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) 23* International Registered Trademark & Property of Sedestral 24* 25*/ 26document.addEventListener('DOMContentLoaded', () => { 27 try { 28 const activeLink = document.querySelector('.sedestral.blog-menu .active'); 29 if (activeLink) { 30 activeLink.scrollIntoView({ 31 behavior: 'smooth', 32 block: 'center' 33 }); 34 } 35 document.querySelectorAll('.sedestral.blog-menu a').forEach(link => { 36 link.addEventListener('click', () => { 37 link.scrollIntoView({ 38 behavior: 'smooth', 39 block: 'center' 40 }); 41 }); 42 }); 43 } catch (e) { 44 console.error('Error in Sedestral script:', e); 45 } 46 47 try { 48 const HEADER_OFFSET = 80; 49 const TOP_ZONE = 0.25; 50 51 const links = Array.from(document.querySelectorAll('.sedestral.toc-list a')); 52 const sections = links 53 .map(l => document.querySelector(l.getAttribute('href'))) 54 .filter(Boolean); 55 56 if (links.length > 0 && sections.length > 0) { 57 let pending = null; 58 59 const bottomMargin = Math.round((1 - TOP_ZONE) * 100); 60 const rootMarginValue = '-' + HEADER_OFFSET + 'px 0px -' + bottomMargin + '% 0px'; 61 62 const observer = new IntersectionObserver(entries => { 63 entries.forEach(entry => { 64 if (pending && entry.target !== pending) return; 65 if (!entry.isIntersecting) return; 66 67 const top = entry.boundingClientRect.top; 68 if (top < HEADER_OFFSET) return; 69 if (top > window.innerHeight * TOP_ZONE) return; 70 71 links.forEach(l => l.parentElement.classList.remove('active')); 72 const idx = sections.indexOf(entry.target); 73 if (idx > -1) links[idx].parentElement.classList.add('active'); 74 75 if (pending && entry.target === pending) { 76 pending = null; 77 } 78 }); 79 }, { 80 rootMargin: rootMarginValue, 81 threshold: 0 82 }); 83 84 sections.forEach(s => observer.observe(s)); 85 86 links.forEach(link => { 87 link.addEventListener('click', e => { 88 e.preventDefault(); 89 90 const target = document.querySelector(link.getAttribute('href')); 91 if (!target) return; 92 93 pending = target; 94 links.forEach(l => l.parentElement.classList.remove('active')); 95 link.parentElement.classList.add('active'); 96 97 const y = target.getBoundingClientRect().top + window.pageYOffset - HEADER_OFFSET; 98 window.scrollTo({ top: y, behavior: 'smooth' }); 99 }); 100 }); 101 } 102 } catch (e) { 103 console.error('Error in Sedestral script:', e); 104 } 105 106 try { 107 document.querySelectorAll('.sedestral.faq-question').forEach(btn => { 108 btn.addEventListener('click', () => { 109 const item = btn.parentElement; 110 const open = item.classList.toggle('open'); 111 112 document.querySelectorAll('.sedestral.faq-item.open').forEach(i => { 113 if (i !== item) i.classList.remove('open'); 114 }); 115 116 btn.setAttribute('aria-expanded', open ? 'true' : 'false'); 117 }); 118 119 const wrapper = btn.nextElementSibling; 120 const id = 'faq-' + Math.random().toString(36).substr(2,5); 121 wrapper.id = id; 122 btn.setAttribute('aria-controls', id); 123 btn.setAttribute('aria-expanded', 'false'); 124 }); 125 } catch (e) { 126 console.error('Error in Sedestral script:', e); 127 } 128 129 // Category description collapse (See more / See less) 130 try { 131 if (typeof sedestralCategoryCollapse !== 'undefined' && sedestralCategoryCollapse.enabled) { 132 var maxHeight = sedestralCategoryCollapse.maxHeight || 150; 133 var seeMoreText = sedestralCategoryCollapse.seeMoreText || 'See more'; 134 var seeLessText = sedestralCategoryCollapse.seeLessText || 'See less'; 135 136 function applyCollapse(categoryDesc) { 137 var originalHeight = categoryDesc.scrollHeight; 138 if (originalHeight <= maxHeight + 30) return; 139 140 var wrapper = document.createElement('div'); 141 wrapper.className = 'sedestral see-more-wrapper'; 142 143 var contentWrapper = document.createElement('div'); 144 contentWrapper.className = 'sedestral see-more-content collapsed'; 145 contentWrapper.style.maxHeight = maxHeight + 'px'; 146 147 categoryDesc.parentNode.insertBefore(wrapper, categoryDesc); 148 contentWrapper.appendChild(categoryDesc); 149 wrapper.appendChild(contentWrapper); 150 151 var toggleBtn = document.createElement('button'); 152 toggleBtn.type = 'button'; 153 toggleBtn.className = 'sedestral see-more-toggle'; 154 toggleBtn.textContent = seeMoreText; 155 wrapper.appendChild(toggleBtn); 156 157 var isExpanded = false;
158 toggleBtn.addEventListener('click', function() { 159 isExpanded = !isExpanded; 160 161 if (isExpanded) { 162 contentWrapper.classList.remove('collapsed'); 163 contentWrapper.style.maxHeight = originalHeight + 'px'; 164 contentWrapper.style.overflow = 'visible'; 165 toggleBtn.textContent = seeLessText; 166 setTimeout(function() { 167 if (isExpanded) { 168 contentWrapper.style.maxHeight = 'none'; 169 } 170 }, 450); 171 } else { 172 contentWrapper.style.maxHeight = contentWrapper.scrollHeight + 'px'; 173 contentWrapper.style.overflow = 'hidden'; 174 contentWrapper.offsetHeight; 175 contentWrapper.classList.add('collapsed'); 176 contentWrapper.style.maxHeight = maxHeight + 'px'; 177 toggleBtn.textContent = seeMoreText; 178 179 var wrapperTop = wrapper.getBoundingClientRect().top + window.pageYOffset - 100; 180 if (window.pageYOffset > wrapperTop) { 181 window.scrollTo({ top: wrapperTop, behavior: 'smooth' }); 182 } 183 } 184 }); 185 } 186 187 // Main category description (compatible with PS 1.6 to 9.x and most themes) 188 var selectors = [ 189 '#category-description', 190 '#category_description', 191 '.category-description', 192 '.cat_desc', 193 '.block-category .category-description', 194 '[data-category-description]', 195 '.category-top-column .category-description', 196 '#center_column .category-description', 197 '#main .category-description' 198 ]; 199 200 var categoryDesc = null; 201 for (var i = 0; i < selectors.length; i++) { 202 categoryDesc = document.querySelector(selectors[i]); 203 if (categoryDesc && categoryDesc.textContent.trim().length > 0) { 204 break; 205 } 206 categoryDesc = null; 207 } 208 209 if (categoryDesc) { 210 applyCollapse(categoryDesc); 211 } 212 213 // Additional category description (opt-in, bottom SEO block) 214 if (sedestralCategoryCollapse.additionalEnabled) { 215 document.querySelectorAll('.category-additional-description').forEach(function(el) { 216 if (el === categoryDesc) return; 217 if (el.textContent.trim().length === 0) return; 218 applyCollapse(el); 219 }); 220 } 221 } 222 } catch (e) { 223 console.error('Error in Sedestral category collapse:', e); 224 } 225 226 // Products carousel - responsive 227 try { 228 document.querySelectorAll('.sedestral.products-carousel').forEach(carousel => { 229 const wrapper = carousel.querySelector('.sedestral.products-wrapper'); 230 const track = carousel.querySelector('.sedestral.products-track'); 231 const items = carousel.querySelectorAll('.sedestral.product-item'); 232 const prevBtn = carousel.querySelector('.sedestral.carousel-prev'); 233 const nextBtn = carousel.querySelector('.sedestral.carousel-next'); 234 235 if (items.length === 0 || !wrapper) return; 236 237 let currentIndex = 0; 238 const gap = 16; 239 240 function getPerView() { 241 var wrapperWidth = wrapper.offsetWidth; 242 var itemWidth = items[0].offsetWidth; 243 if (itemWidth === 0) return 4; 244 return Math.max(1, Math.floor((wrapperWidth + gap) / (itemWidth + gap))); 245 } 246 247 function updateCarousel() { 248 var perView = getPerView(); 249 var maxIndex = Math.max(0, items.length - perView); 250 251 if (currentIndex > maxIndex) { 252 currentIndex = maxIndex; 253 } 254 255 var itemWidth = items[0].offsetWidth; 256 var offset = currentIndex * (itemWidth + gap); 257 track.style.transform = 'translateX(-' + offset + 'px)'; 258 259 prevBtn.disabled = currentIndex === 0; 260 nextBtn.disabled = currentIndex >= maxIndex; 261 prevBtn.style.opacity = currentIndex === 0 ? '0.5' : '1'; 262 nextBtn.style.opacity = currentIndex >= maxIndex ? '0.5' : '1'; 263 264 // Hide buttons if all items visible 265 if (items.length <= perView) { 266 prevBtn.style.display = 'none'; 267 nextBtn.style.display = 'none'; 268 } else { 269 prevBtn.style.display = ''; 270 nextBtn.style.display = ''; 271 } 272 } 273 274 prevBtn.addEventListener('click', function() { 275 if (currentIndex > 0) { 276 currentIndex--; 277 updateCarousel(); 278 } 279 }); 280 281 nextBtn.addEventListener('click', function() { 282 var perView = getPerView(); 283 var maxIndex = Math.max(0, items.length - perView); 284 if (currentIndex < maxIndex) { 285 currentIndex++; 286 updateCarousel(); 287 } 288 }); 289 290 updateCarousel(); 291 292 // Update on resize 293 var resizeTimeout; 294 window.addEventListener('resize', function() { 295 clearTimeout(resizeTimeout); 296 resizeTimeout = setTimeout(updateCarousel, 100); 297 }); 298 }); 299 } catch (e) { 300 console.error('Error in Sedestral products carousel:', e); 301 } 302});
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.