1(function () { 2 const accordionButtonSelector = 3 ".bde-accordion__content-wrapper > .bde-accordion__title-tag > button[aria-expanded]"; 4 5 const { mergeObjects, isBuilder } = BreakdanceFrontend.utils; 6 7 class BreakdanceAdvancedAccordion { 8 defaultOptions = { 9 accordion: false, 10 openFirst: false, 11 ignoreClickEvent: false 12 }; 13 14 constructor(selector, options) { 15 this.selector = selector; 16 this.element = document.querySelector(`${this.selector}`); 17 this.options = mergeObjects(this.defaultOptions, options); 18 this.isBuilder = isBuilder(); 19 this.init(); 20 } 21 22 init() { 23 this.elements = this.getAccordions(); 24 this.handleOnClick = this.onClick.bind(this); 25 26 // Builder: remove click listener in favor of `activateAccordionFromSelector` 27 if (!this.options.ignoreClickEvent) { 28 this.elements.forEach((accordion) => { 29 accordion.addEventListener("click", this.handleOnClick); 30 }); 31 } 32 33 if (this.options.openFirst) { 34 this.open(this.elements[0], true); 35 } 36 37 if (this.isBuilder && this.elements.length === 1) { 38 this.open(this.elements[0], true); 39 } 40 } 41 42 getAccordions() { 43 // Return all accordions that are direct children of the selector. 44 // This is to prevent nested accordions from being selected. 45 return Array.from(document.querySelectorAll(`${this.selector} > ${accordionButtonSelector}`)); 46 } 47 48 onClick(event) { 49 if (this.options.accordion) { 50 this.closeAllAccordions(); 51 } 52 this.toggle(event.currentTarget); 53 } 54 55 isTransitioning(item) { 56 return item.classList.contains("is-collapsing"); 57 } 58 59 isActive(item) { 60 return item.classList.contains("is-active"); 61 } 62 63 getOpenAccordion() { 64 return this.elements.find((accordion) => this.isActive(this.getAccordionContainer(accordion))); 65 } 66 67 getOpenAccordionIndex() { 68 return this.elements.indexOf(this.getOpenAccordion()); 69 } 70 71 getAccordionContent(button) { 72 const controlsId = button.getAttribute("aria-controls"); 73 return this.element.querySelector(`#${controlsId}`); 74 } 75 76 getAccordionContainer(button) { 77 return button.closest(".bde-accordion__content-wrapper"); 78 } 79 80 async open(button, instant = false) { 81 const containerEl = this.getAccordionContainer(button); 82 const contentEl = this.getAccordionContent(button); 83 84 if (this.isTransitioning(containerEl)) return; 85 if (this.isActive(containerEl)) return; 86 87 const event = new Event("breakdance_play_animations", {bubbles: true}); 88 contentEl.dispatchEvent(event); 89 90 const complete = () => { 91 containerEl.classList.add("is-active"); 92 containerEl.classList.remove("is-collapsing"); 93 contentEl.style.height = ""; 94 }; 95 96 if (instant) { 97 complete(); 98 button.setAttribute("aria-expanded", true); 99 return; 100 } 101 102 containerEl.classList.add("is-collapsing"); 103 contentEl.style.height = "0px"; 104 105 const endHeight = this.getEndHeight(contentEl); 106 contentEl.style.height = `${endHeight}px`; 107 button.setAttribute("aria-expanded", true); 108 109 await this.onTransitionEnd(contentEl); 110 111 complete(); 112 } 113 114 async close(button, instant = false) { 115 const containerEl = this.getAccordionContainer(button); 116 const contentEl = this.getAccordionContent(button); 117 118 if (this.isTransitioning(containerEl)) return; 119 if (!this.isActive(containerEl) || this.options.ignoreClickEvent) return; 120 121 if (instant) { 122 containerEl.classList.remove("is-active"); 123 button.setAttribute("aria-expanded", false); 124 return; 125 } 126 127 const endHeight = this.getEndHeight(contentEl); 128 contentEl.style.height = `${endHeight}px`; 129 130 this.reflow(contentEl); 131 132 containerEl.classList.add("is-collapsing"); 133 containerEl.classList.remove("is-active"); 134 contentEl.style.height = ""; 135 button.setAttribute("aria-expanded", false); 136 137 await this.onTransitionEnd(contentEl); 138 139 containerEl.classList.remove("is-collapsing"); 140 } 141 142 toggle(button) { 143 const containerEl = this.getAccordionContainer(button); 144 145 if (this.isActive(containerEl)) { 146 this.close(button); 147 } else { 148 this.open(button); 149 } 150 } 151 152 closeAllAccordions() {
153 this.elements.forEach((accordion) => { 154 this.close(accordion); 155 }); 156 } 157 158 closeAllItemsExcept(item, instant = false) { 159 this.elements 160 .filter((el) => el !== item) 161 .forEach((el) => this.close(el, instant)); 162 } 163 164 destroy() { 165 this.elements.forEach((accordion) => { 166 this.close(accordion, true); 167 accordion.removeEventListener("click", this.handleOnClick); 168 }); 169 this.elements = null; 170 } 171 172 getEndHeight(element) { 173 const height = element.getBoundingClientRect().height; 174 return Math.max(element.scrollHeight, height); 175 } 176 177 reflow(element) { 178 element.offsetHeight; 179 } 180 181 onTransitionEnd(item) { 182 const duration = window.getComputedStyle(item) 183 .getPropertyValue("transition-duration"); 184 185 // If duration is 0s, transitionend will not fire. 186 if (duration === "0s") { 187 return Promise.resolve(); 188 } 189 190 return new Promise((resolve) => { 191 item.addEventListener("transitionend", resolve, {once: true}); 192 }); 193 } 194 195 update() { 196 this.elements = this.getAccordions(); 197 198 this.destroy(); 199 this.init(); 200 } 201 202 activateAccordionFromSelector(selector) { 203 this.elements = this.getAccordions(); 204 205 const buttonEl = selector.querySelector("button[aria-expanded]"); 206 const index = this.elements.indexOf(buttonEl); 207 208 const item = index !== -1 ? this.elements[index] : this.elements[0]; 209 210 this.closeAllItemsExcept(item, true); 211 this.open(this.elements[index], true); 212 } 213 } 214 215 window.BreakdanceAdvancedAccordion = BreakdanceAdvancedAccordion; 216})();
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.