1/** 2 * Accordion / collapsible for pricing modal 2026 only. 3 * Uses event delegation so it works after AJAX replaces .collapsible-lines content (country change). 4 * Does not run on older pricing pages; accordions.js skips #featuresModal.vesr-2026 for this to handle. 5 * 6 * initPricingSearchClear (below): vanilla JS show/hide + clear for .pricing_search_input inside this modal only 7 * (blog-like UX). Scoped to #featuresModal.vesr-2026 â does not query document-wide selectors. 8 */ 9(function () { 10 /** 11 * Show .search__btn-close when input has text; hide when empty. On clear: reset filter UI inside modal 12 * and dispatch keyup so pricing.js pricingSearch re-runs with empty query. 13 */ 14 function initPricingSearchClear(modal) { 15 var searchWrap = modal.querySelector('.search--collapsible'); 16 if (!searchWrap) return; 17 18 var input = searchWrap.querySelector('input.form-control.pricing_search_input') || 19 searchWrap.querySelector('.pricing_search_input'); 20 var closeBtn = searchWrap.querySelector('.search__btn-close'); 21 if (!input || !closeBtn) return; 22 23 function syncCloseButton() { 24 closeBtn.style.display = input.value.length > 0 ? 'flex' : 'none'; 25 } 26 27 function resetFeatureSearchDom() { 28 var nothingToShow = modal.querySelector('.nothing-to-show'); 29 if (nothingToShow) { 30 nothingToShow.classList.add('hidden'); 31 } 32 modal.querySelectorAll('.highlight').forEach(function (el) { 33 el.classList.remove('highlight'); 34 }); 35 modal.querySelectorAll('.collapsible-line').forEach(function (line) { 36 line.classList.remove('hidden'); 37 Array.prototype.slice.call(line.querySelectorAll('.hidden')).forEach(function (node) { 38 node.classList.remove('hidden'); 39 }); 40 }); 41 } 42 43 input.addEventListener('input', syncCloseButton); 44 45 closeBtn.addEventListener('click', function (e) { 46 e.preventDefault(); 47 input.value = ''; 48 closeBtn.style.display = 'none'; 49 resetFeatureSearchDom(); 50 input.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, cancelable: true })); 51 }); 52 53 syncCloseButton(); 54 } 55 56 function init() { 57 var modal = document.querySelector('#featuresModal.vesr-2026'); 58 if (!modal) return; 59 60 initPricingSearchClear(modal); 61 62 var faqList = modal.querySelector('.collapsible-lines'); 63 if (!faqList) return; 64 65 var nowActiveElement = null; 66 67 function setAllClosed() { 68 var rows = modal.querySelectorAll('.collapsible-line'); 69 var contents = modal.querySelectorAll('.collapsible-content'); 70 rows.forEach(function (row) { 71 row.classList.remove('opened'); 72 }); 73 contents.forEach(function (el) { 74 el.style.height = '0'; 75 }); 76 var collapseBtn = modal.querySelector('.collapse-btn'); 77 if (collapseBtn) { 78 collapseBtn.classList.remove('expand'); 79 } 80 } 81 82 function faqClickToElement(header, scrollToElement) { 83 var faqItem = header.parentElement; 84 var isActive = faqItem.classList.contains('opened'); 85 var content = header.nextElementSibling; 86 87 if (!isActive) { 88 nowActiveElement = content; 89 if (nowActiveElement) { 90 nowActiveElement.style.height = nowActiveElement.scrollHeight + 'px'; 91 } 92 faqItem.classList.add('opened'); 93 if (scrollToElement && faqItem.scrollIntoView) { 94 setTimeout(function () { 95 //faqItem.scrollIntoView({ block: 'start', behavior: 'smooth' }); 96 }, 250); 97 } 98 } else { 99 faqItem.classList.remove('opened'); 100 } 101 } 102 103 // Initial state: all sections closed, button shows "Expand" 104 setAllClosed(); 105 106 // Delegated click: works for initial and AJAX-replaced headers 107 faqList.addEventListener('click', function (e) { 108 var header = e.target.closest('.collapsible-header'); 109 if (!header || !faqList.contains(header)) return; 110 faqClickToElement(header, true); 111 }); 112 113 // Expand / Collapse all button (capture + stopImmediatePropagation so no other script overrides) 114 var collapseBtn = modal.querySelector('.collapse-btn'); 115 if (collapseBtn) { 116 collapseBtn.addEventListener('click', function (e) { 117 e.stopImmediatePropagation(); 118 var isExpanded = collapseBtn.classList.contains('expand'); 119 var rows = modal.querySelectorAll('.collapsible-line'); 120 var contents = modal.querySelectorAll('.collapsible-content'); 121 if (isExpanded) { 122 // All open -> collapse all; button will show "Expand" 123 collapseBtn.classList.remove('expand'); 124 rows.forEach(function (row) { 125 row.classList.remove('opened'); 126 });
127 contents.forEach(function (el) { 128 el.style.height = '0'; 129 }); 130 } else { 131 // All closed -> expand all; button will show "Collapse" 132 collapseBtn.classList.add('expand'); 133 rows.forEach(function (row) { 134 row.classList.add('opened'); 135 }); 136 contents.forEach(function (el) { 137 el.style.height = el.scrollHeight + 'px'; 138 }); 139 } 140 }, true); 141 } 142 143 // Resize: keep open item height correct 144 window.addEventListener('resize', function () { 145 if (nowActiveElement && document.contains(nowActiveElement)) { 146 nowActiveElement.style.height = 'auto'; 147 nowActiveElement.style.height = nowActiveElement.scrollHeight + 'px'; 148 } 149 }, true); 150 } 151 152 if (document.readyState === 'loading') { 153 document.addEventListener('DOMContentLoaded', init); 154 } else { 155 init(); 156 } 157})();
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.