1 2function accordion_script(wpgbAccordion = null) { 3 4 const accordionInstances = new WeakMap(); 5 6 class Accordion { 7 constructor(element, options) { 8 this.element = element; 9 this.options = options; 10 11 // new order 12 if (!this.validateStructure()) { 13 return; 14 } 15 16 this.removePlaceholder() 17 // end new order 18 19 this.removePlaceholderChild(); 20 this.accordionItems = this.element.querySelectorAll(':scope > .fr-accordion__item') 21 this.lastOpenedItem = null; 22 this.init(); 23 accordionInstances.set(this.element, this); 24 } 25 26 init() { 27 this.setEventListeners(); 28 this.handleFirstItemOpened(); 29 this.handleAllItemsExpanded(); 30 this.handleExpandedClass(); 31 this.handleScrollToHash(); 32 this.handleShowDuration(); 33 this.addTabindex(); 34 this.addIdToBody(); 35 this.addAriaControls(); 36 this.addIdToHeader(); 37 this.addAriaLabelledBy(); 38 this.addFaqSchema(); 39 this.addRoleButton(); 40 41 this.ITEMS(item => { 42 if (this.HEADER(item).getAttribute('aria-expanded') !== 'true') { 43 this.updateFocusableTabIndex(item, false); 44 } 45 }); 46 47 this.ITEMS(item => { 48 if (this.hasCurrentPageLink(item) ) { 49 this.expandItem(item); 50 } 51 }); 52 53 } 54 55 BODY(item) { 56 //return item.querySelector('.fr-accordion__body') || item.children[1]; 57 const body = item.querySelector('.fr-accordion__body'); 58 if (!body && item.children.length > 1 && item.children[1].tagName !== 'STYLE') { 59 return item.children[1]; 60 } 61 return body; 62 } 63 64 HEADER(item) { 65 //return item.querySelector('.fr-accordion__header') || item.children[0]; 66 const header = item.querySelector('.fr-accordion__header'); 67 if (!header && item.children.length > 0 && item.children[0].tagName !== 'STYLE') { 68 return item.children[0]; 69 } 70 return header; 71 } 72 73 ITEMS(init) { 74 this.accordionItems.forEach(item => { 75 if (item.tagName !== 'STYLE' && 76 !item.classList.contains('brx-nestable-children-placeholder') && 77 !(item.tagName === 'DIV' && item.classList.contains('brx-query-trail'))) { 78 init(item); 79 } 80 }) 81 } 82 83 84 removePlaceholder() { 85 const placeholder = this.element.querySelector('.brx-nestable-children-placeholder'); 86 const accordionChildren = this.element.querySelectorAll('.fr-accordion__item'); 87 88 if (placeholder) { 89 placeholder.remove(); 90 } 91 92 // If there are no children of accordion, add the placeholder back 93 if (accordionChildren.length === 0) { 94 const newPlaceholder = document.createElement('li'); 95 newPlaceholder.className = 'brx-nestable-children-placeholder fr-accordion__item'; 96 this.element.appendChild(newPlaceholder); 97 } 98 } 99 100 101 setEventListeners() { 102 this.ITEMS((item) => this.attachHeaderListeners(item)); 103 } 104 105 attachHeaderListeners(item) { 106 const header = this.HEADER(item); 107 header.addEventListener('click', () => { 108 this.headerInteraction(item); 109 }); 110 header.addEventListener('keydown', (e) => { 111 if (e.key === 'Enter') { 112 this.headerInteraction(item); 113 } 114 }); 115 header.addEventListener('keyup', (e) => { 116 if (e.key === ' ') { 117 this.headerInteraction(item); 118 } 119 }); 120 } 121 122 headerInteraction(item) { 123 if (window.innerWidth <= this.options.scrollToHeadingOn) { 124 this.scrollToItemHeader(item); 125 } 126 127 if (this.options.closePreviousItem && this.options.allItemsExpanded) { 128 this.ITEMS((otherItem) => { 129 if (otherItem !== item) { 130 this.collapseItem(otherItem); 131 } 132 }); 133 } else if (this.options.closePreviousItem && this.lastOpenedItem && this.lastOpenedItem !== item) { 134 this.collapseItem(this.lastOpenedItem); 135 } 136 this.toggleItem(item); 137 this.lastOpenedItem = this.HEADER(item).getAttribute('aria-expanded') === 'true' ? item : null; 138 139 140 } 141 142 scrollToItemHeader(item) { 143 144 if (!this.options.scrollToHeading) return 145 146 setTimeout(() => { 147 this.HEADER(item).scrollIntoView({ 148 behavior: 'smooth', 149 block: 'start', 150 inline: 'nearest' 151 }); 152 }, this.options.showDuration); 153 154 } 155 156 157 158 toggleItem(item) { 159 if (this.HEADER(item).getAttribute('aria-expanded') === 'true') { 160 this.collapseItem(item); 161 } else { 162 this.expandItem(item); 163 } 164 } 165 166 collapseItem(item) { 167 const body = this.BODY(item); 168 body.style.height = body.offsetHeight + 'px'; 169 setTimeout(() => { 170 body.style.height = 0; 171 }, 0); 172 this.HEADER(item).setAttribute('aria-expanded', 'false'); 173 // Dispatch the 'accordionItemCollapsed' event
174 const collapseEvent = new CustomEvent('accordionItemCollapsed', { detail: { item: item } }); 175 this.element.dispatchEvent(collapseEvent); 176 this.flipIcon(item); 177 this.updateFocusableTabIndex(item, false); 178 } 179 180 expandItem(item) { 181 const body = this.BODY(item); 182 body.style.height = body.scrollHeight + 'px'; 183 this.HEADER(item).setAttribute('aria-expanded', 'true'); 184 // Dispatch the 'accordionItemExpanded' event 185 const expandEvent = new CustomEvent('accordionItemExpanded', { detail: { item: item } }); 186 this.element.dispatchEvent(expandEvent); 187 this.flipIcon(item); 188 this.updateFocusableTabIndex(item, true); 189 } 190 191 // fr-accordion__icon flip icon on expand and collapse 192 flipIcon(item) { 193 const icon = this.HEADER(item).querySelector('.fr-accordion__icon'); 194 if (icon) { 195 // add transition property with time of showDuration 196 icon.style.transition = `transform ${this.options.showDuration}ms ease-in-out`; 197 icon.classList.toggle('fr-accordion__icon--flipped'); 198 } 199 } 200 201 202 handleFirstItemOpened() { 203 if (!this.options.firstItemOpened) return 204 this.expandItem(this.accordionItems[0]); 205 this.lastOpenedItem = this.accordionItems[0]; 206 } 207 208 handleAllItemsExpanded() { 209 if (!this.options.allItemsExpanded) return 210 this.ITEMS((item) => this.expandItem(item)); 211 } 212 213 handleExpandedClass() { 214 if (!this.options.expandedClass) return 215 this.ITEMS((item) => { 216 if (item.classList.contains('fr-accordion__item--expanded')) { 217 this.expandItem(item); 218 } 219 }); 220 } 221 222 handleScrollToHash() { 223 if (!this.options.scrollToHash) return 224 const hash = window.location.hash; 225 if (hash) { 226 const item = this.element.querySelector(hash); 227 if (item) { 228 this.scrollToItemAndExpand(item); 229 } 230 } else { 231 this.ITEMS((item) => { 232 const id = item.id; 233 if (id) { 234 const links = document.querySelectorAll(`a[href="#${id}"]`); 235 links.forEach((link) => { 236 link.addEventListener('click', (e) => { 237 this.scrollToItemAndExpand(item); 238 }); 239 }); 240 } 241 }); 242 } 243 } 244 245 scrollToItemAndExpand(item) { 246 this.expandItem(item); 247 const header = this.HEADER(item); 248 const headerOffset = header.offsetTop; 249 const headerHeight = header.offsetHeight; 250 const offsetPosition = headerOffset - headerHeight - this.options.scrollToHashOffset; 251 window.scrollTo({ 252 top: offsetPosition, 253 behavior: 'smooth' 254 }); 255 } 256 257 258 259 260 261 handleShowDuration() { 262 if (!this.options.showDuration) return 263 this.ITEMS((item) => { 264 this.BODY(item).style.transitionDuration = this.options.showDuration + 'ms'; 265 // for header transition 266 this.HEADER(item).style.transitionDuration = this.options.showDuration + 'ms'; 267 // for __title in header 268 this.HEADER(item).querySelector('.fr-accordion__title').style.transitionDuration = this.options.showDuration + 'ms'; 269 }); 270 } 271 272 addTabindex() { 273 this.ITEMS((item) => { 274 this.HEADER(item).setAttribute('tabindex', 0); 275 }); 276 } 277 278 addIdToBody() { 279 this.ITEMS((item) => { 280 if (!this.BODY(item).id) { 281 this.BODY(item).id = 'fr-accordion__body-' + Math.random().toString(36).substr(2, 9); 282 } 283 }); 284 } 285 286 addIdToHeader() { 287 this.ITEMS((item) => { 288 if (!this.HEADER(item).id) { 289 this.HEADER(item).id = 'fr-accordion__header-' + Math.random().toString(36).substr(2, 9); 290 } 291 }); 292 } 293 294 addAriaControls() { 295 this.ITEMS((item) => { 296 this.HEADER(item).setAttribute('aria-controls', this.BODY(item).id); 297 }); 298 } 299 300 301 addAriaLabelledBy() { 302 this.ITEMS((item) => { 303 this.BODY(item).setAttribute('aria-labelledby', this.HEADER(item).id); 304 }); 305 } 306 307 addRoleRegion() { 308 this.ITEMS((item) => { 309 this.BODY(item).setAttribute('role', 'region'); 310 }); 311 } 312 313 addRoleButton() { 314 this.ITEMS((item) => { 315 this.HEADER(item).setAttribute('role', 'button'); 316 }); 317 } 318 319 addFaqSchema() { 320 if (this.options.faqSchema) { 321 322 const faqData = []; 323 324 this.ITEMS((item) => { 325 const question = this.HEADER(item).textContent.trim(); 326 const answer = this.BODY(item).textContent.trim(); 327 328 faqData.push({ 329 "@type": "Question", 330 "name": question,
331 "acceptedAnswer": { 332 "@type": "Answer", 333 "text": answer, 334 }, 335 }); 336 }); 337 338 const faqSchema = { 339 "@context": "https://schema.org", 340 "@type": "FAQPage", 341 "mainEntity": faqData, 342 }; 343 344 const script = document.createElement('script'); 345 script.type = 'application/ld+json'; 346 script.innerHTML = JSON.stringify(faqSchema); 347 document.head.appendChild(script); 348 } 349 } 350 351 352 validateStructure() { 353 if (this.element.children.length === 0) { 354 console.warn('The fr-accordion has no children.', this.element); 355 return false; 356 } 357 358 // Validation 359 const validChildren = { 360 'UL': (child) => child.tagName === 'LI' || child.tagName === 'STYLE' || (child.tagName === 'DIV' && child.classList.contains('brx-query-trail')), 361 'OL': (child) => child.tagName === 'LI' || child.tagName === 'STYLE' || (child.tagName === 'DIV' && child.classList.contains('brx-query-trail')) 362 } 363 364 for (const child of this.element.children) { 365 366 if (child.tagName === 'STYLE') { 367 console.log('found a STYLE, continuing', child) 368 continue 369 } 370 371 if (!child.classList.contains('fr-accordion__item')) { 372 child.classList.add('fr-accordion__item'); 373 } 374 375 const body = child.querySelector('.fr-accordion__body') 376 const header = child.querySelector('.fr-accordion__header') 377 if (!header || !body) { 378 console.warn('Accordion items do not have a Header or a Body element', child) 379 return false 380 } 381 382 if (child.children.length > 2) { 383 console.warn('Remove extra children elements from accordion item:', child); 384 return false; 385 } 386 387 if (child.children.length < 2 && child.children.length > 0) { 388 console.warn('Add a header and a body to accordion item:', child); 389 return false; 390 } 391 392 // if element tag is ul or or and direct children are not li elements 393 // add warning "add li elements to ul or ol" 394 if (this.element.tagName === 'UL' || this.element.tagName === 'OL') { 395 if (!validChildren[this.element.tagName](child)) { 396 console.warn(`Direct child of the "ul" or "ol" element should have a tag of "li" instead of "${child.tagName}"`, child); 397 } 398 } 399 400 } 401 return true; 402 } 403 404 removePlaceholderChild() { 405 if (this.element.querySelectorAll('.brx-nestable-children-placeholder')) { 406 this.element.querySelectorAll('.brx-nestable-children-placeholder').forEach(el => { 407 el.remove(); 408 }); 409 } 410 } 411 412 updateFocusableTabIndex(item, isOpened) { 413 const focusableSelectors = 'a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select'; 414 const focusables = item.querySelectorAll(focusableSelectors);
415 focusables.forEach(el => { 416 el.setAttribute('tabindex', isOpened ? '0' : '-1'); 417 }); 418 } 419 420 hasCurrentPageLink(item) { 421 return this.BODY(item).querySelector('a[aria-current="page"]'); 422 } 423 424 425 // end of class Accordion 426 } 427 428 function framesGetAccordionInstance(element) { 429 return accordionInstances.get(element); 430 } 431 432 window.getAccordionInstance = framesGetAccordionInstance; 433 434 // * Differentiate between a normal accordion and a wpgb accordion so we can call the appropriate one to re-init 435 // const accordions = document.querySelectorAll('.fr-accordion'); 436 // accordions.forEach(accordion => { 437 438 // if (!accordion.dataset.frAccordionOptions) { 439 // console.warn('Options not provided for the following fr-accordion element:', accordion); 440 // return; 441 // } 442 443 // const options = JSON.parse(accordion.dataset.frAccordionOptions); 444 // new Accordion(accordion, options); 445 // }); 446 if (wpgbAccordion) { 447 if (!wpgbAccordion.dataset.frAccordionOptions) { 448 console.warn('Options not provided for the following fr-accordion element:', wpgbAccordion); 449 return; 450 } 451 452 const options = JSON.parse(wpgbAccordion.dataset.frAccordionOptions); 453 new Accordion(wpgbAccordion, options); 454 } else { 455 const accordions = document.querySelectorAll('.fr-accordion'); 456 accordions.forEach(accordion => { 457 if (!accordion.dataset.frAccordionOptions) { 458 console.warn('Options not provided for the following fr-accordion element:', accordion); 459 return; 460 } 461 462 const options = JSON.parse(accordion.dataset.frAccordionOptions); 463 new Accordion(accordion, options); 464 }); 465 } 466 467 468 469 function accordion_script_builder() { 470 471 if (!document.querySelector('.brx-body.iframe.mounted')) return; 472 473 const accordions = document.querySelectorAll('.fr-accordion'); 474 accordions.forEach(accordion => { 475 const items = Array.from(accordion.children); 476 items.forEach(item => { 477 const header = item.children[0]; 478 const body = item.children[1]; 479 // toggle height auto on body on header click 480 header.addEventListener('click', () => { 481 body.style.height = body.style.height === 'auto' ? '0px' : 'auto'; 482 // toggle aria-expanded on header 483 header.setAttribute('aria-expanded', header.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'); 484 }); 485 }); 486 }); 487 } 488 489 accordion_script_builder() 490 491} 492 493 494function wpgb_accordion_script() { 495 window.WP_Grid_Builder && WP_Grid_Builder.on('init', function (wpgb) { 496 497 console.dir(wpgb); // Holds all instances. 498 console.log('console logging WPGB:', wpgb) 499 500 wpgb.facets.on('appended', function (nodes) { 501 502 // We create a set to store Unique wpgb Accordions 503 // if wpgbAccordion then we add to the Set the wpgbAccordions found 504 const wpgbAccordionsSet = new Set(); 505 nodes.forEach(node => { 506 const wpgbAccordion = node.closest('.fr-accordion.wpgb-enabled'); 507 if (wpgbAccordion) { 508 wpgbAccordionsSet.add(wpgbAccordion); 509 } 510 }); 511 512 // For each accordion on the set we init them on facets appended ( appended works when filtering / stopping filtering) 513 wpgbAccordionsSet.forEach(wpgbAccordion => { 514 const instance = window.getAccordionInstance(wpgbAccordion); 515 if (instance) { 516 accordion_script(wpgbAccordion); 517 } 518 }); 519 520 }); 521 522 }); 523} 524 525 526document.addEventListener("DOMContentLoaded", function (e) { 527 bricksIsFrontend && accordion_script() 528 bricksIsFrontend && wpgb_accordion_script() 529 530}); 531 532 533// We listen every time a grid/template/content is initialized.
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.