PageSourceSearch

https://nomv.org/wp-content/uploads/intouch-accessibility/js/itacc-core-beaverbuilder.js?ver=1781079036

js nomv.org collected 2026-10-02 04:03:58 UTC 20,115 bytes, 490 lines download raw bytes

1(function() {
2  // Define a namespace to encapsulate all variables and functions
3  const itv_acc_bb = (() => {
4    /**
5     * Mutation Observer for 3rd Party Sliders
6     */
7    function observe3rdPartySliders( setupAttr, sliderControls ) {
8      let retries = 0;
9      const maxRetries = 50;     // Try up to 50 times (~10 seconds)
10      const retryInterval = 200; // Check every 200ms
11
12      const observer = new MutationObserver(() => {
13        if (sliderControls.length) {
14          setupAttr(sliderControls, 'role', 'button');
15          setupAttr(sliderControls, 'tabindex', '0');
16          const allUpdated = Array.from(sliderControls).every(el => el.getAttribute('role') === 'button');
17          if (allUpdated) {
18            observer.disconnect(); // Stop observing once the attributes are applied
19          }
20        } else if (++retries >= maxRetries) {
21          observer.disconnect(); // Stop observing if the element never appears
22        }
23      });
24
25      const targetNode = document.body;
26      observer.observe(targetNode, { childList: true, subtree: true });
27    }
28
29    function syncSwiperSlideA11y(carousel) {
30      if (!carousel) return;
31
32      const slides = carousel.querySelectorAll('.swiper-slide');
33      if (!slides.length) return;
34
35      slides.forEach(slide => {
36        const isDuplicate = slide.classList.contains('swiper-slide-duplicate');
37
38        // Keep this conservative: expose active/prev/next only.
39        // If your carousel shows multiple slides at once, this still typically covers what's visible.
40        /*const isVisible =
41          slide.classList.contains('swiper-slide-active') ||
42          slide.classList.contains('swiper-slide-prev') ||
43          slide.classList.contains('swiper-slide-next');*/
44
45        const shouldHide = isDuplicate;
46
47        if (shouldHide) {
48          slide.setAttribute('aria-hidden', 'true');
49        } else {
50          slide.removeAttribute('aria-hidden');
51        }
52      });
53    }
54
55    function initSwiperA11y() {
56      const carousels = document.querySelectorAll('.pp-image-carousel.swiper, .pp-image-carousel.swiper-container');
57      if (!carousels.length) return;
58
59      carousels.forEach(carousel => {
60        // Initial pass
61        syncSwiperSlideA11y(carousel);
62
63        // Avoid stacking observers if initializeAttributes runs more than once
64        if (carousel.dataset.itaccSwiperObserved === '1') return;
65        carousel.dataset.itaccSwiperObserved = '1';
66
67        const wrapper = carousel.querySelector('.swiper-wrapper');
68        if (!wrapper) return;
69
70        // Observe class changes because Swiper flips slide classes on every transition
71        const obs = new MutationObserver(() => {
72          syncSwiperSlideA11y(carousel);
73        });
74
75        obs.observe(wrapper, {
76          attributes: true,
77          subtree: true,
78          attributeFilter: ['class']
79        });
80      });
81    }
82
83    function removeUnwantedSchemaElements() {
84      // Remove author schema elements from containers with grid-remove-author class
85      const removeAuthorContainers = document.querySelectorAll('.grid-remove-author');
86      removeAuthorContainers.forEach(container => {
87        const authorElements = container.querySelectorAll('div[itemprop="author"][itemtype="https://schema.org/Person"]');
88        authorElements.forEach(el => el.remove());
89      });
90
91      // Remove interaction count schema elements from containers with grid-remove-interaction-count class
92      const removeInteractionContainers = document.querySelectorAll('.grid-remove-interaction-count');
93      removeInteractionContainers.forEach(container => {
94        const interactionElements = container.querySelectorAll('div[itemprop="interactionStatistic"][itemtype="https://schema.org/InteractionCounter"]');
95        interactionElements.forEach(el => el.remove());
96      });
97
98      // Add aria-hidden to all schema div containers that only contain meta tags
99      // This prevents accessibility scanners from flagging empty schema containers
100      const allSchemaContainers = document.querySelectorAll('div[itemscope], div[itemprop]');
101      allSchemaContainers.forEach(div => {
102        // Check if this div only contains meta tags (no visible content)
103        const hasOnlyMetaTags = Array.from(div.childNodes).every(node => {
104          // Allow meta tags, text nodes that are only whitespace, and comments
105          if (node.nodeType === Node.ELEMENT_NODE) {
106            return node.tagName.toLowerCase() === 'meta';
107          }
108          if (node.nodeType === Node.TEXT_NODE) {
109            return node.textContent.trim() === '';
110          }
111          if (node.nodeType === Node.COMMENT_NODE) {
112            return true;
113          }
114          return false;
115        });
116
117        if (hasOnlyMetaTags && div.childNodes.length > 0) {
118          div.setAttribute('aria-hidden', 'true');
119        }
120      });
121    }
122
123    function observeSchemaElements() {
124      let retries = 0;
125      const maxRetries = 50;
126
127      let scheduled = false;
128      const observer = new MutationObserver(() => {
129        if (scheduled) return;
130        scheduled = true;
131
132        requestAnimationFrame(() => {
133          scheduled = false;
134          removeUnwantedSchemaElements();
135
136          if (++retries >= maxRetries) {
137            observer.disconnect();
138          }
139        });
140      });
141
142      observer.observe(document.body, { childList: true, subtree: true });
143    }
144
145    /**
146     * Initialize attributes for elements in the DOM.
147     */
148    function initializeAttributes() {
149      // Make sure we have access to the Accessibility Core javascript
150      if ( !window.itv_acc ) {
151        console.error('ITACC: itv_acc is not loaded');
152        return;
153      }
154      const { setupAttr } = window.itv_acc;
155
156
157      const scrollAdjustElements = document.querySelectorAll(
158        'header[data-sticky="1"], ' +
159        '.pp-tabs, ' +
160        '.pp-tabs .pp-tabs-label'
161      );
162
163      // Landmarks
164      const ppOffCanvasWrap = document.querySelectorAll('div.pp-offcanvas-container');
165      setupAttr( ppOffCanvasWrap, 'role', 'none' );
166
167      // Replace fl-main-content div with a proper <main> element
168      /*const flMainDiv = document.getElementById('fl-main-content');
169      if (flMainDiv && flMainDiv.tagName !== 'MAIN') {
170        const mainEl = document.createElement('main');
171        // Copy all attributes, skipping role="main" since <main> is semantically equivalent
172        [...flMainDiv.attributes].forEach(attr => {
173          if (!(attr.name === 'role' && attr.value === 'main')) {
174            mainEl.setAttribute(attr.name, attr.value);
175          }
176        });
177        // Move all children
178        while (flMainDiv.firstChild) mainEl.appendChild(flMainDiv.firstChild);
179        mainEl.removeAttribute('itemprop')
180        flMainDiv.parentNode.replaceChild(mainEl, flMainDiv);
181      }*/
182
183      // Check if any tab elements exist on the page
184      /*if (scrollAdjustElements.length > 0) {
185        // Check if the style hasn't already been added
186        if (!document.querySelector('style[data-itacc-scroll-padding]')) {
187          // Create and append the style element
188          const styleElement = document.createElement('style');
189          styleElement.setAttribute('data-itacc-scroll-padding', 'true');
190          styleElement.textContent = 'html { scroll-padding-top: 25vh; }';
191          document.body.appendChild(styleElement);
192        }
193      }*/
194
195      const nestedColGroups = document.querySelectorAll('.fl-col-group-nested');
196      setupAttr(nestedColGroups, 'role', 'group');
197
198      // Menu toggles
199      const menuToggles = document.querySelectorAll('.fl-menu-toggle');
200      setupAttr(menuToggles, 'aria-label', 'Menu Toggle');
201      setupAttr(menuToggles, 'role', 'button');
202      setupAttr(menuToggles, 'tabindex', '0');
203
204      // Menu Fixes & Dropdowns
205      /* const flMenuWrappers = document.querySelectorAll('.fl-module-menu .fl-menu');
206      if ( flMenuWrappers.length ) {
207        flMenuWrappers.forEach( wrapper => {
208          const nav = wrapper.querySelector('nav');
209          if ( !nav ) return;
210
211          if ( !wrapper.hasAttribute('role') ) {
212            wrapper.setAttribute('role', 'navigation');
213          }
214        });
215      } */
216
217      const menuDropdowns = document.querySelectorAll('div.fl-has-submenu-container');
218      setupAttr(menuDropdowns, 'aria-haspopup', 'true');
219      setupAttr(menuDropdowns, 'role', 'none');
220      setupAttr(menuDropdowns, 'aria-expanded', 'false');
221      const ppMenuSubMenu = document.querySelectorAll('ul.menu li.menu-item-has-children div.pp-has-submenu-container');
222      setupAttr(ppMenuSubMenu, 'aria-haspopup', 'true');
223      setupAttr(ppMenuSubMenu, 'aria-expanded', 'false');
224      const uabbMenuWrap = document.querySelectorAll('ul.uabb-creative-menu-horizontal');
225      setupAttr(uabbMenuWrap, 'role', 'navigation');
226      const uabbMenuSubMenu = document.querySelectorAll('ul.uabb-creative-menu-horizontal div.uabb-has-submenu-container');
227      setupAttr(uabbMenuSubMenu, 'aria-haspopup', 'true');
228      setupAttr(uabbMenuSubMenu, 'aria-expanded', 'false');
229
230      // Separator elements
231      const bbSeparators = document.querySelectorAll(
232        '.fl-module.fl-module-separator .fl-separator, ' +
233        '.pp-heading-separator.line_only'
234      );
235      setupAttr(bbSeparators, 'aria-hidden', 'true');
236      const separatorElements = document.querySelectorAll('.fl-module.fl-module-separator, .pp-separator-line');
237      setupAttr(separatorElements, 'role', 'none');
238
239      // Layout elements with background image
240      const layoutElements = document.querySelectorAll(
241        '.fl-row .fl-row-content-wrap, ' +
242        '.fl-col .fl-col-content, ' +
243        '.fl-slide-bg-photo'
244      );
245      /*setupAttr(layoutElements, 'role', 'img', itv_acc.hasBackgroundImage); // -- changed to help scanners from flagging background CSS images */
246      setupAttr(layoutElements, 'role', 'none', itv_acc.hasBackgroundImage);
247
248      // Headers
249      const ppHeaderPs = document.querySelectorAll('.pp-heading p.heading-title .title-text');
250      setupAttr(ppHeaderPs, 'role', 'text');
251
252      // Video Backgrounds
253      const rowVideoBkgds = document.querySelectorAll('.fl-row .fl-row-content-wrap .fl-bg-video[data-fallback]');
254      setupAttr(rowVideoBkgds, 'role', 'none');
255
256      // Parallax rows
257      const parallaxRowElements = document.querySelectorAll('.fl-row.fl-row-bg-parallax .fl-row-content-wrap');
258      setupAttr(parallaxRowElements, 'role', 'none');
259
260      // Expandable rows
261      const expandRowElements = document.querySelectorAll('.pp-er-wrap');
262      setupAttr(expandRowElements, 'role', 'button');
263      setupAttr(expandRowElements, 'tabindex', '0');
264
265      // Swiper slides
266      const imgSwiperElements = document.querySelectorAll('figure.swiper-slide-inner.use-as-background');
267      setupAttr(imgSwiperElements, 'role', 'none');
268      const figureSwiperElements = document.querySelectorAll('figure.swiper-slide-inner');
269      setupAttr(figureSwiperElements, 'role', 'none', itv_acc.hasChildOfTag('img'));
270
271      // Slide drawer elements
272      const slidedrawerElements = document.querySelectorAll('.uabb-slide-box-wrap .uabb-slide-dropdown .uabb-icon-wrap');
273      setupAttr(slidedrawerElements, 'role', 'button');
274      setupAttr(slidedrawerElements, 'tabindex', '0');
275      setupAttr(slidedrawerElements, 'aria-label', 'Slides open');
276
277      const slidedrawerIcons = document.querySelectorAll('.uabb-slide-box-wrap .uabb-slide-dropdown .uabb-icon-wrap .uabb-icon');
278      setupAttr(slidedrawerIcons, 'role', 'none');
279
280      // Info box elements
281      const infoBoxImgElements = document.querySelectorAll('.uabb-ib2-outter .uabb-new-ib-img');
282      setupAttr(infoBoxImgElements, 'role', 'none');
283      //setupAttr(infoBoxImgElements, 'aria-hidden', 'true');
284
285      const infoBoxWrapElements = document.querySelectorAll('.uabb-ib2-outter .uabb-new-ib-desc');
286      //setupAttr(infoBoxWrapElements, 'aria-hidden', 'true');
287      setupAttr(infoBoxWrapElements, 'role', 'button');
288
289      const hoverCardElements = document.querySelectorAll('.pp-hover-card');
290      hoverCardElements.forEach(el => {
291        if (!el.getAttribute('onclick')?.trim()) { 
292          el.removeAttribute('onclick'); 
293        }
294      });
295      const hoverBoxElements = document.querySelectorAll('.uabb-ih-container .uabb-ih-item .uabb-ih-line');
296      setupAttr(hoverBoxElements, 'role', 'none');
297
298      const infoBoxDescriptions = document.querySelectorAll('.pp-infobox-description');
299      setupAttr(infoBoxDescriptions, 'aria-hidden', 'true', itv_acc.isDeepEmpty);
300
301      // Panels / Tabs
302      const tabElements = document.querySelectorAll(
303        'a.pp-panel-link, ' +
304        'a.uabb-tab-link'
305      );
306      setupAttr(tabElements, 'role', 'button');
307      // -- Update PP Tabs / Accordions
308      const ppTabsAccordionLabels = document.querySelectorAll(
309        '.pp-tabs .pp-tabs-panels .pp-tabs-panel .pp-tabs-panel-label[data-index]'
310      );
311      const isAccordionLabel = el => {
312        if (!el) return false;
313        if (el.closest('[role="tablist"]')) return false;
314        if (!el.hasAttribute('data-index')) return false;
315        return itv_acc.hasChildWithClass('pp-toggle-icon')(el);
316      };
317      setupAttr(ppTabsAccordionLabels, 'role', 'button', isAccordionLabel);
318      setupAttr(ppTabsAccordionLabels, 'tabindex', '0', isAccordionLabel);
319      ppTabsAccordionLabels.forEach(el => {
320        if (!isAccordionLabel(el)) return;
321
322        const isOpen = el.classList.contains('pp-tab-active');
323        el.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
324      });
325      if (!window.itv_acc_bb_pp_tabs_keydown) {
326        window.itv_acc_bb_pp_tabs_keydown = true;
327
328        document.addEventListener('keydown', e => {
329          const target = e.target && e.target.closest
330            ? e.target.closest('.pp-tabs-panel-label[role="button"][data-index]')
331            : null;
332
333          if (!target) return;
334
335          if (e.key === 'Enter' || e.key === ' ') {
336            e.preventDefault();
337            target.click();
338          }
339        });
340      }
341      // -- END: Update PP Tabs / Accordions
342      // -- UABB Tabs
343      const uabbTabNav = document.querySelectorAll('nav.uabb-tabs-nav > ul');
344      const uabbTabLink = document.querySelectorAll('nav.uabb-tabs-nav > ul > li[data-index]');
345      setupAttr(uabbTabNav, 'role', 'tablist');
346      setupAttr(uabbTabLink, 'role', 'tab');
347      // -- END UABB Tabs
348
349      // Lists / Icon / Info Lists
350      const uabbInfoListTitles = document.querySelectorAll('.uabb-info-list-title');
351      setupAttr(uabbInfoListTitles, 'role', 'heading');
352      const uabbInfoListLinks = document.querySelectorAll('.uabb-info-list-title a[aria-label]');
353      uabbInfoListLinks.forEach(a => {
354        const label = a.getAttribute('aria-label') || '';
355        if (/^go to https?:\/\//i.test(label.trim()) && itv_acc.hasVisibleText(a)) {
356          a.removeAttribute('aria-label');
357        }
358      });
359
360      // Testimonial elements
361      const testimonialElements = document.querySelectorAll(
362        '.pp-testimonials .pp-testimonial .pp-testimonials-content, ' +
363        '.uabb-testimonials .uabb-testimonial-info .uabb-testimonial-author-description, ' +
364        '.uabb-testimonials .uabb-testimonial-info .uabb-testimonial-author-description *'
365      );
366      setupAttr(testimonialElements, 'role', 'none');
367
368      // Modals
369      const modalElements = document.querySelectorAll(
370        '.pp-modal-wrap, ' +
371        '.pp-modal-wrap[role="dialog"], ' +
372        '.uabb-modal-wrap'
373      );
374      setupAttr(modalElements, 'aria-modal', 'true' );
375      setupAttr(modalElements, 'role', 'dialog' );
376
377      // Row Shape Dividers
378      const bbRowShapeDividers = document.querySelectorAll('.fl-builder-shape-layer svg');
379      bbRowShapeDividers.forEach(svg => {
380        svg.setAttribute('aria-hidden', 'true');
381        svg.setAttribute('role', 'none');
382
383        if (svg.hasAttribute('alt') && svg.getAttribute('alt').trim() === '') {
384          svg.removeAttribute('alt');
385        }
386
387        // BB sometimes injects these; remove to prevent naming/flags
388        // svg.querySelectorAll('title, desc').forEach(el => el.remove());
389      });
390
391      // Galleries and Overlays
392      const ppGalleryOverlays = document.querySelectorAll('.pp-gallery-overlay');
393      setupAttr(ppGalleryOverlays, 'aria-hidden', 'true', itv_acc.isDeepEmpty);
394
395      // Sliders
396      const uabbBXSliderPrev = document.querySelectorAll('.uabb-slider-prev .bx-prev');
397      const uabbBXSliderNext = document.querySelectorAll('.uabb-slider-next .bx-next');
398      const sliderControls = [...uabbBXSliderPrev, ...uabbBXSliderNext];
399
400      // Placeholder iFrames
401      const placeholderIFrames = document.querySelectorAll('iframe.pp-video-iframe:not([src]):not([src=""])');
402      setupAttr(placeholderIFrames, 'title', 'Placeholder Video Loader');
403
404      // Run functions once
405      removeUnwantedSchemaElements();
406
407      // Start Observers
408      observe3rdPartySliders(setupAttr, sliderControls);
409      initSwiperA11y();
410      observeSchemaElements();
411
412      // Fix incorrect PowerPack Smart Button Roles last
413      document.querySelectorAll('a.pp-button[href][role="button"]').forEach(a => {
414        if ( !itv_acc.isNavigationHref(a) ) return;
415
416        const before = getComputedStyle(a).cssText;
417        a.removeAttribute('role');
418        const after = getComputedStyle(a).cssText;
419
420        // If styles changed, something depended on role="button" — put it back
421        if (before !== after) {
422          a.setAttribute('role', 'button');
423        }
424      });
425    }
426
427    // Expose functions within the namespace
428    return {
429      initializeAttributes
430    };
431  })();
432
433  window.itv_acc_bb = itv_acc_bb;
434  window.addEventListener( 'load', function () {
435    setTimeout( function () {
436      itv_acc_bb.initializeAttributes();
437    }, 101 ); // Optional slight delay to let 3rd party render
438  });
439
440  // ─── PowerPack Accordion: Hash / Deep-link Scroll Fix ───────────────────────
441  (function () {
442    if (window.ppAccordionFixLoaded) return;
443    window.ppAccordionFixLoaded = true;
444    const KEY = 'ppAccordionTarget';
445    if (window.location.hash && window.location.hash.length > 1) {
446      sessionStorage.setItem(KEY, window.location.hash);
447    }
448    function getHeaderOffset() {
449      const header = document.querySelector('header');
450      if (header) {
451        return header.getBoundingClientRect().height + 40;
452      }
453      return 120;
454    }
455    function scrollToStoredTarget() {
456      const storedHash = sessionStorage.getItem(KEY);
457      if (!storedHash || storedHash.length < 2) return;
458      const id = decodeURIComponent(storedHash.substring(1));
459      let tries = 0;
460      const maxTries = 30;
461      const attempt = () => {
462        const el = document.getElementById(id);
463        if (!el) {
464          if (++tries < maxTries) setTimeout(attempt, 100);
465          return;
466        }
467        const header = el.querySelector(
468          '.pp-accordion-item-label, .pp-accordion-item-heading, .pp-advanced-accordion-header'
469        );
470        if (header && header.getAttribute('aria-expanded') !== 'true') {
471          header.click();
472        }
473        setTimeout(() => {
474          const headerOffset = getHeaderOffset();
475          const group = el.closest('.pp-accordion');
476          const items = group ? Array.from(group.querySelectorAll('.pp-accordion-item')) : [];
477          const itemIndex = items.indexOf(el);
478          const twoBack = itemIndex >= 2 ? items[itemIndex - 2] : null;
479          const scrollTarget = twoBack ? twoBack : (group ? group : el);
480          const y = scrollTarget.getBoundingClientRect().top + window.pageYOffset - headerOffset;
481          window.scrollTo({ top: y, behavior: 'smooth' });
482          sessionStorage.removeItem(KEY);
483        }, 350);
484      };
485      attempt();
486    }
487    window.addEventListener('load', scrollToStoredTarget);
488  })();
489  // ─── END: PowerPack Accordion Hash Fix ──────────────────────────────────────
490})();

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.