PageSourceSearch

https://watsonnivenskahendds.com/wp-content/plugins/accessibility-…/js/accessibility-fixer.js?ver=1779705903

js watsonnivenskahendds.com collected 2026-10-04 02:33:54 UTC 73,572 bytes, 2,252 lines download raw bytes

1/**
2 * Accessibility Fixer — Around the Mountain Dental
3 * Fixes all DOM-level WCAG issues detected by accessScan.
4 */
5(function () {
6  'use strict';
7
8  var hashJumpGuardUntil = 0;
9  var hashJumpGuardScroll = null;
10  var hadKeyboardFocusEvent = false;
11
12  onPageReady(function () {
13    runWhenIdle(runAccessibilityFixes);
14  });
15
16  function onPageReady(callback) {
17    if (document.readyState === 'complete') {
18      callback();
19      return;
20    }
21
22    window.addEventListener('load', function onLoad() {
23      window.removeEventListener('load', onLoad);
24      callback();
25    });
26  }
27
28  function runWhenIdle(callback) {
29    if ('requestIdleCallback' in window) {
30      window.requestIdleCallback(callback, { timeout: 1500 });
31      return;
32    }
33
34    window.setTimeout(callback, 250);
35  }
36
37  function runAccessibilityFixes() {
38    if (isFixEnabled('focus_visibility')) enableFocusVisibility();
39    if (isFixEnabled('image_alt_text')) fixImageAltText();
40    if (isFixEnabled('active_nav_aria_current')) fixCurrentPageLinks();
41    if (isFixEnabled('skip_link')) fixSkipLinks();
42    if (isFixEnabled('icon_only_links')) fixIconOnlyLinks();
43    if (isFixEnabled('lightbox_links')) fixEmptyLightboxLinks();
44    if (isFixEnabled('read_more_links')) {
45      fixReadMoreLinks();
46      watchReadMoreLinks();
47    }
48    if (isFixEnabled('book_online_links')) fixBookOnlineLinks();
49    if (isFixEnabled('positive_tabindex_forms')) {
50      fixPositiveTabindexInForms();
51      watchPositiveTabindexInForms();
52    }
53    if (isFixEnabled('button_like_links')) {
54      fixButtonLikeLinks();
55      watchButtonLikeLinks();
56    }
57    if (isFixEnabled('x_anchor_button_accessibility')) {
58      fixXAnchorButtons();
59      watchXAnchorButtons();
60      enableRoleButtonKeyboardSupport();
61      watchRoleButtonKeyboardSupport();
62    }
63    if (isFixEnabled('x_anchor_toggle_remove_aria_label')) {
64      removeXAnchorToggleAriaLabels();
65      watchXAnchorToggleAriaLabels();
66      scheduleXAnchorToggleAriaLabelCleanup();
67    }
68    if (isFixEnabled('empty_hash_links')) {
69      fixEmptyHashLinks();
70      watchEmptyHashLinks();
71    }
72    if (isFixEnabled('x_menu_navigation_landmark')) {
73      wrapXMenuNavigation();
74      watchXMenuNavigation();
75    }
76    if (isFixEnabled('x_acc_header_remove_role')) {
77      removeXAccHeaderRoles();
78      watchXAccHeaderRoles();
79    }
80    if (isFixEnabled('x_acc_item_child_div_remove_role')) {
81      removeXAccItemChildDivRoles();
82      watchXAccItemChildDivRoles();
83    }
84    if (isFixEnabled('cornerstone_accordion_headers')) {
85      fixCornerstoneAccordionHeaders();
86      watchCornerstoneAccordionHeaders();
87    }
88    if (isFixEnabled('nav_menu_button_roles')) {
89      removeNavMenuButtonRoles();
90      watchNavMenuButtonRoles();
91    }
92    if (isFixEnabled('modal_links')) fixModalLinks();
93    if (isFixEnabled('slick_carousel')) fixSlickCarousel();
94    if (isFixEnabled('hidden_modals')) fixHiddenModals();
95    if (isFixEnabled('decorative_icons')) fixIconElements();
96    if (isFixEnabled('frame_names')) {
97      fixFrameAccessibleNames();
98      watchFrameAccessibleNames();
99    }
100    if (isFixEnabled('title_only_info')) fixTitleOnlyInformation();
101    if (isFixEnabled('aria_label_visible_text')) fixAriaLabelsOverridingVisibleText();
102    if (isFixEnabled('article_page_tags')) {
103      fixArticlePageTags();
104      watchArticlePageTags();
105    }
106    if (isFixEnabled('article_regions')) fixArticleRegions();
107    if (isFixEnabled('main_content_div_to_main')) {
108      changeMainContentDivToMain();
109      watchMainContentDivToMain();
110    }
111    if (isFixEnabled('main_landmark') || isFixEnabled('landmark_nesting') || isFixEnabled('contentinfo_landmark')) fixPageLandmarks();
112    if (isFixEnabled('secondary_nav_role')) fixSecondaryNavRole();
113    if (isFixEnabled('minor_navigation_landmarks')) {
114      fixMinorNavigationLandmarks();
115      watchMinorNavigationLandmarks();
116    }
117    if (isFixEnabled('links_discernible_text')) {
118      fixLinksWithoutDiscernibleText();
119      watchLinksWithoutDiscernibleText();
120    }
121    if (isFixEnabled('pdf_link_warning')) {
122      fixPdfLinkWarnings();
123      watchPdfLinkWarnings();
124    }
125
126    // Re-run carousel fix after Slick initialises (it may run after DOMContentLoaded)
127    if (isFixEnabled('slick_carousel')) waitForSlick();
128    enableEmptyHashJumpGuard();
129  }
130
131  function isFixEnabled(key) {
132    if (!window.atmA11ySettings || !window.atmA11ySettings.enabledFixes) return true;
133    return window.atmA11ySettings.enabledFixes[key] !== 0 && window.atmA11ySettings.enabledFixes[key] !== '0';
134  }
135
136  function enableFocusVisibility() {
137    document.documentElement.classList.add('atm-a11y-focus-enabled');
138    updateStickyHeaderOffset();
139    protectFocusedElementsFromStickyHeader();
140
141    window.addEventListener('resize', debounce(updateStickyHeaderOffset, 150));
142    window.addEventListener('orientationchange', debounce(updateStickyHeaderOffset, 150));
143  }
144
145  function protectFocusedElementsFromStickyHeader() {
146    if (document.documentElement.getAttribute('data-atm-a11y-focus-protection') === '1') return;
147    document.documentElement.setAttribute('data-atm-a11y-focus-protection', '1');
148
149    document.addEventListener('keydown', function (event) {
150      if (event.key === 'Tab' || event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
151        hadKeyboardFocusEvent = true;
152      }
153    }, true);
154
155    document.addEventListener('mousedown', function () {
156      hadKeyboardFocusEvent = false;
157    }, true);
158
159    document.addEventListener('touchstart', function () {
160      hadKeyboardFocusEvent = false;
161    }, true);
162
163    document.addEventListener('pointerdown', function () {
164      hadKeyboardFocusEvent = false;
165    }, true);
166
167    document.addEventListener('focusin', function (event) {
168      var target = event.target;
169      if (!target || target === document.body || target === document.documentElement) return;
170      if (!hadKeyboardFocusEvent) return;
171
172      window.setTimeout(function () {
173        scrollFocusedElementBelowStickyHeader(target);
174      }, 0);
175    });
176
177    document.addEventListener('click', function (event) {
178      var link = event.target && event.target.closest ? event.target.closest('a[href^="#"]') : null;
179      if (!link) return;
180
181      window.setTimeout(function () {
182        var id = decodeURIComponent((link.getAttribute('href') || '').replace(/^#/, ''));
183        var target = id ? document.getElementById(id) : null;
184        if (target) scrollFocusedElementBelowStickyHeader(target);
185      }, 50);
186    });
187  }
188
189  function scrollFocusedElementBelowStickyHeader(el) {
190    var offset = getStickyHeaderOffset();
191    if (!offset) return;
192
193    var rect = el.getBoundingClientRect();
194    var buffer = 12;
195
196    if (rect.top < offset + buffer) {
197      window.scrollBy({
198        top: rect.top - offset - buffer,
199        left: 0,
200        behavior: 'auto'
201      });
202    }
203  }
204
205  function updateStickyHeaderOffset() {
206    document.documentElement.style.setProperty('--atm-header-height', getStickyHeaderOffset() + 'px');
207  }
208
209  function getStickyHeaderOffset() {
210    var maxBottom = 0;
211    var selectors = [
212      'header',
213      '.site-header',
214      '#site-header',
215      '#masthead',
216      '.sticky-header',
217      '.fixed-header',
218      '.et-l--header',
219      '.et-fixed-header',
220      '.elementor-location-header',
221      '.elementor-sticky--active',
222      '.x-navbar',
223      '.x-bar-fixed',
224      '.x-masthead',
225      '.cs-header'
226    ];
227
228    document.querySelectorAll(selectors.join(',')).forEach(function (el) {
229      var style = window.getComputedStyle(el);
230      var rect = el.getBoundingClientRect();
231      var isStickyOrFixed = style.position === 'fixed' || style.position === 'sticky';
232
233      if (!isStickyOrFixed || style.display === 'none' || style.visibility === 'hidden') return;
234      if (rect.height <= 0 || rect.bottom <= 0 || rect.top > 80) return;
235
236      maxBottom = Math.max(maxBottom, rect.bottom);
237    });
238
239    return Math.ceil(maxBottom);
240  }
241
242  function debounce(fn, delay) {
243    var timer = null;
244    return function () {
245      window.clearTimeout(timer);
246      timer = window.setTimeout(fn, delay);
247    };
248  }
249
250  function fixTitleOnlyInformation() {
251    var elements = document.querySelectorAll('[title]');
252    elements.forEach(function (el) {
253      var title = (el.getAttribute('title') || '').trim();
254      if (title === '') return;
255      if ((el.getAttribute('aria-label') || '').trim() !== '') return;
256      if ((el.getAttribute('aria-labelledby') || '').trim() !== '') return;
257      if (!isFormOrFrameElement(el) && getVisibleText(el) !== '') return;
258
259      el.setAttribute('aria-label', title);
260    });
261  }
262
263  function fixAriaLabelsOverridingVisibleText() {
264    var elements = document.querySelectorAll('a[aria-label], button[aria-label], [role="button"][aria-label], [role="link"][aria-label]');
265    elements.forEach(function (el) {
266      fixAriaLabelForVisibleText(el);
267    });
268  }
269
270  function fixAriaLabelForVisibleText(el) {
271    if (isLandmark(el)) return;
272
273    var visibleText = getVisibleText(el);
274    if (visibleText === '') return;
275
276    var label = (el.getAttribute('aria-label') || '').trim();
277    if (label === '' || labelContainsVisibleText(label, visibleText)) return;
278
279    el.setAttribute('aria-label', visibleText);
280  }
281
282  function labelContainsVisibleText(label, visibleText) {
283    return normalizeAccessibleText(label).indexOf(normalizeAccessibleText(visibleText)) !== -1;
284  }
285
286  function normalizeAccessibleText(text) {
287    return (text || '').replace(/\s+/g, ' ').trim().toLowerCase();
288  }
289
290  function isFormOrFrameElement(el) {
291    return /^(input|textarea|select|iframe|frame)$/i.test(el.tagName);
292  }
293
294  function fixCurrentPageLinks() {
295    var links = document.querySelectorAll('a[href], area[href], [role="link"]');
296    links.forEach(function (link) {
297      if ((link.getAttribute('aria-current') || '').trim() !== '') return;
298      if (!pointsToCurrentPage(link)) return;
299
300      link.setAttribute('aria-current', 'page');
301    });
302  }
303
304  function pointsToCurrentPage(link) {
305    var href = link.getAttribute('href');
306    if (!href || href.charAt(0) === '#') return false;
307
308    try {
309      var target = new URL(href, window.location.href);
310      var current = new URL(window.location.href);
311
312      return normalizeUrlPath(target) === normalizeUrlPath(current) &&
313        target.hostname === current.hostname &&
314        target.protocol === current.protocol;
315    } catch (e) {
316      return false;
317    }
318  }
319
320  function normalizeUrlPath(url) {
321    var path = url.pathname.replace(/\/+$/, '');
322    return path === '' ? '/' : path;
323  }
324
325  function fixSkipLinks() {
326    var skipLink = document.querySelector('.atm-skip-link[href^="#"]');
327    if (!skipLink) return;
328
329    var targetId = (skipLink.getAttribute('href') || '').replace(/^#/, '');
330    var target = targetId ? document.getElementById(targetId) : null;
331
332    if (!target) {
333      target = findSkipLinkTarget();
334      if (!target) return;
335
336      if (!target.id) {
337        target.id = 'main-content';
338      }
339
340      skipLink.setAttribute('href', '#' + target.id);
341    }
342
343    if (!isFocusable(target)) {
344      target.setAttribute('tabindex', '-1');
345    }
346  }
347
348  function findSkipLinkTarget() {
349    var selectors = [
350      'main',
351      '[role="main"]',
352      '#main-content',
353      '#content',
354      '#primary',
355      '.site-content',
356      '.content-area',
357      '#main',
358      '.main-content',
359      '.entry-content',
360      '.x-main'
361    ];
362
363    for (var i = 0; i < selectors.length; i++) {
364      var el = document.querySelector(selectors[i]);
365      if (el && hasMeaningfulContent(el)) {
366        return el;
367      }
368    }
369
370    return null;
371  }
372
373  function isFocusable(el) {
374    if (!el || !el.matches) return false;
375    if (el.matches('a[href], area[href], button, input, select, textarea, iframe, object, embed')) return true;
376    var tabindex = el.getAttribute('tabindex');
377    return tabindex !== null && !isNaN(parseInt(tabindex, 10));
378  }
379
380  function fixArticleRegions() {
381    var articles = document.querySelectorAll('article');
382    articles.forEach(function (article) {
383      if (!shouldDemoteArticle(article)) return;
384
385      demoteArticle(article);
386    });
387  }
388
389  function shouldDemoteArticle(article) {
390    if (article.hasAttribute('data-atm-keep-article')) return false;
391
392    var className = getClassString(article).toLowerCase();
393    if (/\b(page|type-page|et_pb_post|et-l|x-section|cs-content|elementor|fl-builder|vc_row)\b/.test(className)) {
394      return true;
395    }
396
397    if (/\b(post|type-post|single-post|blog|news|article|hentry)\b/.test(className) && !/\bpage\b/.test(className)) {
398      return false;
399    }
400
401    return false;
402  }
403
404  function fixArticlePageTags(root) {
405    var scope = root || document;
406    var articles = Array.prototype.slice.call(scope.querySelectorAll('article[class]')).filter(isArticlePageTag);
407
408    if (isArticlePageTag(scope)) {
409      articles.unshift(scope);
410    }
411
412    articles.forEach(function (article) {
413      demoteArticle(article);
414    });
415  }
416
417  function isArticlePageTag(el) {
418    return !!el && el.matches && el.matches('article') && el.classList && el.classList.contains('page');
419  }
420
421  function watchArticlePageTags() {
422    if (!window.MutationObserver) return;
423
424    var observer = new MutationObserver(function (mutations) {
425      mutations.forEach(function (mutation) {
426        if (mutation.type === 'attributes') {
427          fixArticlePageTags(mutation.target);
428          return;
429        }
430
431        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
432          if (!node || node.nodeType !== 1) return;
433          fixArticlePageTags(node);
434        });
435      });
436    });
437
438    observer.observe(document.body, {
439      attributes: true,
440      attributeFilter: ['class'],
441      childList: true,
442      subtree: true
443    });
444  }
445
446  function demoteArticle(article) {
447    var replacement = document.createElement('div');
448    copyAttributes(article, replacement, []);
449    replacement.setAttribute('data-atm-a11y-demoted-article', 'true');
450
451    while (article.firstChild) {
452      replacement.appendChild(article.firstChild);
453    }
454
455    article.parentNode.replaceChild(replacement, article);
456  }
457
458  // -------------------------------------------------------------------------
459  // 1. Images without alt text → default "Image"
460  // -------------------------------------------------------------------------
461  function fixImageAltText() {
462    var imgs = document.querySelectorAll('img');
463    imgs.forEach(function (img) {
464      // Skip images already marked decorative (role="presentation" or aria-hidden)
465      if (
466        img.getAttribute('role') === 'presentation' ||
467        img.getAttribute('aria-hidden') === 'true' ||
468        img.hasAttribute('data-uw-rm-ignore')
469      ) {
470        return;
471      }
472      if (!img.hasAttribute('alt') || img.getAttribute('alt') === '') {
473        img.setAttribute('alt', 'Image');
474      }
475    });
476  }
477
478  // -------------------------------------------------------------------------
479  // 2. Social media / icon-only links → aria-label
480  //    Handles <a><i class="fa fa-google"></i></a> pattern
481  // -------------------------------------------------------------------------
482  var iconLabelMap = {
483    'fa-google':      'Visit us on Google',
484    'fa-facebook-f':  'Visit us on Facebook',
485    'fa-facebook':    'Visit us on Facebook',
486    'fa-instagram':   'Visit us on Instagram',
487    'fa-twitter':     'Visit us on Twitter',
488    'fa-youtube':     'Visit us on YouTube',
489    'fa-linkedin':    'Visit us on LinkedIn',
490    'fa-yelp':        'Visit us on Yelp',
491  };
492
493  function fixIconOnlyLinks() {
494    var links = document.querySelectorAll('a');
495    links.forEach(function (a) {
496      // Only target links whose visible text is empty
497      if (a.textContent.trim() !== '') return;
498      if (a.getAttribute('aria-label')) return;
499
500      var icon = a.querySelector('i[class]');
501      if (!icon) return;
502
503      var classes = icon.className.split(' ');
504      for (var c = 0; c < classes.length; c++) {
505        if (iconLabelMap[classes[c]]) {
506          a.setAttribute('aria-label', iconLabelMap[classes[c]]);
507          break;
508        }
509      }
510
511      // Fallback for any unmatched icon link — make at least non-empty
512      if (!a.getAttribute('aria-label')) {
513        a.setAttribute('aria-label', 'External link');
514      }
515    });
516  }
517
518  // -------------------------------------------------------------------------
519  // 3. Empty gallery lightbox links → label + role
520  //    <a class="dm-lightbox" style="background-image: url(...)"></a>
521  // -------------------------------------------------------------------------
522  function fixEmptyLightboxLinks() {
523    var lightboxLinks = document.querySelectorAll('a.dm-lightbox');
524    lightboxLinks.forEach(function (a, index) {
525      if (!a.getAttribute('aria-label')) {
526        // Try to extract filename for a better label
527        var href = a.getAttribute('href') || '';
528        var filename = href.split('/').pop().replace(/\.[^.]+$/, '').replace(/[-_]/g, ' ') || '';
529        var label = filename
530          ? 'View gallery image: ' + filename + ' (opens image)'
531          : 'View gallery image ' + (index + 1) + ' (opens image)';
532        a.setAttribute('aria-label', label);
533      }
534      if (!a.getAttribute('role')) {
535        a.setAttribute('role', 'button');
536      }
537    });
538  }
539
540  // -------------------------------------------------------------------------
541  // 4. Ambiguous "Read More" links → context-aware aria-label
542  // -------------------------------------------------------------------------
543  function fixReadMoreLinks() {
544    var links = document.querySelectorAll('a');
545    links.forEach(function (link, index) {
546      fixGenericLinkContext(link, index);
547    });
548  }
549
550  function watchReadMoreLinks() {
551    if (!window.MutationObserver) return;
552
553    var observer = new MutationObserver(function (mutations) {
554      mutations.forEach(function (mutation) {
555        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
556          if (!node || node.nodeType !== 1) return;
557
558          if (isLinkElement(node)) {
559            fixGenericLinkContext(node, 0);
560          }
561
562          Array.prototype.slice.call(node.querySelectorAll('a')).forEach(function (link, index) {
563            fixGenericLinkContext(link, index);
564          });
565        });
566      });
567    });
568
569    observer.observe(document.body, { childList: true, subtree: true });
570  }
571
572  function fixGenericLinkContext(link, index) {
573    var visibleText = getVisibleText(link);
574    var currentLabel = (link.getAttribute('aria-label') || '').trim();
575
576    if (!isGenericLinkText(visibleText) && !isGenericLinkText(currentLabel)) return;
577
578    var context = getLinkContextText(link);
579    if (context === '') return;
580
581    link.setAttribute('aria-label', getContextualLinkAction(link, index) + ' ' + context);
582  }
583
584  function isGenericLinkText(text) {
585    var normalized = normalizeAccessibleText(text).replace(/^[\s.:\-|»›>…]+|[\s.:\-|»›>…]+$/g, '');
586    return /^(read more|learn more|more|view more|find out more|discover more|see more|show more|click here|here|details|view details|more details|continue reading|keep reading)$/i.test(normalized);
587  }
588
589  function getLinkContextText(link) {
590    var title = (link.getAttribute('title') || '').trim();
591    if (title !== '' && !isGenericLinkText(title)) return normalizeContextText(title);
592
593    var imageLabel = getLabelFromLinkedImage(link);
594    if (imageLabel !== '' && !isGenericLinkText(imageLabel)) return normalizeContextText(imageLabel);
595
596    var svgLabel = getLabelFromSvg(link);
597    if (svgLabel !== '' && !isGenericLinkText(svgLabel)) return normalizeContextText(svgLabel);
598
599    var heading = findPrecedingHeading(link);
600    if (heading) return normalizeContextText(heading);
601
602    var hrefLabel = getContextFromHref(link.getAttribute('href') || '');
603    if (hrefLabel !== '') return normalizeContextText(hrefLabel);
604
605    var classLabel = getContextFromClassName(link);
606    if (classLabel !== '') return normalizeContextText(classLabel);
607
608    return '';
609  }
610
611  function getContextualLinkAction(link, index) {
612    var visibleText = normalizeAccessibleText(getVisibleText(link));
613    var actions = [
614      'Read more about',
615      'Learn more about',
616      'View details about',
617      'Explore',
618      'Discover',
619      'Find out more about',
620      'See more about',
621      'Open details for',
622      'Continue reading about',
623      'Get more information about',
624      'Review details for'
625    ];
626
627    if (visibleText === 'learn more') return 'Learn more about';
628    if (visibleText === 'read more') return 'Read more about';
629    return actions[Math.abs(hashString((link.getAttribute('href') || '') + ' ' + index)) % actions.length];
630  }
631
632  function hashString(text) {
633    var hash = 0;
634    for (var i = 0; i < text.length; i++) {
635      hash = ((hash << 5) - hash) + text.charCodeAt(i);
636      hash |= 0;
637    }
638    return hash;
639  }
640
641  function getContextFromHref(href) {
642    if (!href || href === '#') return '';
643    if (href.indexOf('tel:') === 0) return 'phone number ' + href.replace('tel:', '').replace(/[^\d+]/g, '');
644    if (href.indexOf('mailto:') === 0) return href.replace('mailto:', '').split('?')[0];
645
646    try {
647      var url = new URL(href, window.location.href);
648      var segments = url.pathname.split('/').filter(Boolean);
649      var segment = segments.pop() || '';
650      return segment.replace(/\.[^.]+$/, '').replace(/[-_]+/g, ' ');
651    } catch (e) {
652      return href.split('#')[0].split('?')[0].replace(/\/$/, '').split('/').pop().replace(/\.[^.]+$/, '').replace(/[-_]+/g, ' ');
653    }
654  }
655
656  function getContextFromClassName(link) {
657    var classText = (link.className && typeof link.className === 'string') ? link.className : '';
658    classText = classText.replace(/\b(button|btn|link|read|more|learn|cta|primary|secondary)\b/gi, '').replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim();
659    return isGenericLinkText(classText) ? '' : classText;
660  }
661
662  function normalizeContextText(text) {
663    return (text || '').replace(/\s+/g, ' ').trim();
664  }
665
666  function findPrecedingHeading(el) {
667    // Walk up the DOM tree looking for a sibling or ancestor heading
668    var node = el;
669    while (node && node !== document.body) {
670      node = node.parentElement;
671      if (!node) break;
672      var headings = node.querySelectorAll('h1, h2, h3, h4');
673      if (headings.length) {
674        return headings[headings.length - 1].textContent.trim();
675      }
676    }
677    return null;
678  }
679
680  // -------------------------------------------------------------------------
681  // 5. "Book Online" anchor acting as button
682  // -------------------------------------------------------------------------
683  function fixBookOnlineLinks() {
684    var links = document.querySelectorAll('a');
685    links.forEach(function (a) {
686      var text = a.textContent.trim().toLowerCase();
687      if (text !== 'book online') return;
688
689      var href = a.getAttribute('href');
690      if (href === null || href === '' || href === '#') {
691        a.setAttribute('role', 'button');
692        if (!a.getAttribute('tabindex')) {
693          a.setAttribute('tabindex', '0');
694        }
695        // Keyboard activation support
696        a.addEventListener('keydown', function (e) {
697          if (e.key === 'Enter' || e.key === ' ') {
698            e.preventDefault();
699            a.click();
700          }
701        });
702        preventHashJumpForButtonLikeLink(a);
703      }
704    });
705  }
706
707  function fixPositiveTabindexInForms(root) {
708    var scope = root || document;
709    var elements = Array.prototype.slice.call(scope.querySelectorAll('form[tabindex], form [tabindex], input[tabindex], select[tabindex], textarea[tabindex], button[tabindex]'));
710
711    if (scope.matches && scope.matches('form[tabindex], form [tabindex], input[tabindex], select[tabindex], textarea[tabindex], button[tabindex]')) {
712      elements.unshift(scope);
713    }
714
715    elements.forEach(function (el) {
716      normalizePositiveTabindex(el);
717    });
718  }
719
720  function watchPositiveTabindexInForms() {
721    if (!window.MutationObserver) return;
722
723    var observer = new MutationObserver(function (mutations) {
724      mutations.forEach(function (mutation) {
725        if (mutation.type === 'attributes') {
726          normalizePositiveTabindex(mutation.target);
727          return;
728        }
729
730        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
731          if (!node || node.nodeType !== 1) return;
732          fixPositiveTabindexInForms(node);
733        });
734      });
735    });
736
737    observer.observe(document.body, {
738      attributes: true,
739      attributeFilter: ['tabindex'],
740      childList: true,
741      subtree: true
742    });
743  }
744
745  function normalizePositiveTabindex(el) {
746    if (!isFormTabindexElement(el)) return;
747
748    var tabindex = parseInt(el.getAttribute('tabindex'), 10);
749    if (!isNaN(tabindex) && tabindex > 0) {
750      el.setAttribute('tabindex', '0');
751    }
752  }
753
754  function isFormTabindexElement(el) {
755    if (!el || !el.matches || !el.hasAttribute('tabindex')) return false;
756    return el.matches('form, input, select, textarea, button') || !!el.closest('form');
757  }
758
759  // -------------------------------------------------------------------------
760  // 6. Button-like anchors used by builders, sliders, popups, and Cornerstone
761  // -------------------------------------------------------------------------
762  function fixButtonLikeLinks() {
763    var links = document.querySelectorAll('a');
764    links.forEach(function (link) {
765      fixButtonLikeLink(link);
766    });
767  }
768
769  function watchButtonLikeLinks() {
770    if (!window.MutationObserver) return;
771
772    var observer = new MutationObserver(function (mutations) {
773      mutations.forEach(function (mutation) {
774        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
775          if (!node || node.nodeType !== 1) return;
776
777          if (node.matches && node.matches('a')) {
778            fixButtonLikeLink(node);
779          }
780
781          Array.prototype.slice.call(node.querySelectorAll('a')).forEach(function (link) {
782            fixButtonLikeLink(link);
783          });
784        });
785      });
786    });
787
788    observer.observe(document.body, { childList: true, subtree: true });
789  }
790
791  function fixButtonLikeLink(link) {
792    if (isFixEnabled('nav_menu_button_roles') && isNavMenuLink(link)) {
793      removeRoleButtonFromNavMenuLink(link);
794      return;
795    }
796
797    if (!isButtonLikeLink(link)) return;
798
799    link.setAttribute('role', 'button');
800    if (!link.getAttribute('tabindex')) {
801      link.setAttribute('tabindex', '0');
802    }
803
804    var currentLabel = (link.getAttribute('aria-label') || '').trim();
805    if (currentLabel === '' || currentLabel.toLowerCase() === 'read more') {
806      link.setAttribute('aria-label', getButtonLikeLinkLabel(link));
807    }
808
809    if (isFixEnabled('aria_label_visible_text')) {
810      fixAriaLabelForVisibleText(link);
811    }
812
813    if (link.getAttribute('data-atm-a11y-button-keydown') === '1') return;
814    link.setAttribute('data-atm-a11y-button-keydown', '1');
815
816    preventHashJumpForButtonLikeLink(link);
817
818    link.addEventListener('keydown', function (e) {
819      if (e.key !== 'Enter' && e.key !== ' ') return;
820
821      e.preventDefault();
822      link.click();
823    });
824  }
825
826  function fixXAnchorButtons(root) {
827    var scope = root || document;
828    var selector = [
829      '.x-anchor-button',
830      '[class*="x-anchor-button"]'
831    ].join(',');
832    var buttons = Array.prototype.slice.call(scope.querySelectorAll(selector));
833
834    if (scope.matches && scope.matches(selector)) {
835      buttons.unshift(scope);
836    }
837
838    buttons.forEach(function (button) {
839      button.setAttribute('role', 'button');
840
841      if (!button.getAttribute('tabindex')) {
842        button.setAttribute('tabindex', '0');
843      }
844
845      if ((button.getAttribute('aria-label') || '').trim() === '' && getVisibleText(button) === '') {
846        button.setAttribute('aria-label', getButtonLikeLinkLabel(button));
847      }
848
849      preventHashJumpForButtonLikeLink(button);
850      addRoleButtonKeyboardSupport(button);
851    });
852  }
853
854  function watchXAnchorButtons() {
855    if (!window.MutationObserver) return;
856
857    var observer = new MutationObserver(function (mutations) {
858      mutations.forEach(function (mutation) {
859        if (mutation.type === 'attributes') {
860          fixXAnchorButtons(mutation.target);
861          return;
862        }
863
864        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
865          if (!node || node.nodeType !== 1) return;
866          fixXAnchorButtons(node);
867        });
868      });
869    });
870
871    observer.observe(document.body, {
872      attributes: true,
873      attributeFilter: ['class'],
874      childList: true,
875      subtree: true
876    });
877  }
878
879  function enableRoleButtonKeyboardSupport(root) {
880    var scope = root || document;
881    var buttons = Array.prototype.slice.call(scope.querySelectorAll('[role="button"]'));
882
883    if (scope.matches && scope.matches('[role="button"]')) {
884      buttons.unshift(scope);
885    }
886
887    buttons.forEach(addRoleButtonKeyboardSupport);
888  }
889
890  function watchRoleButtonKeyboardSupport() {
891    if (!window.MutationObserver) return;
892
893    var observer = new MutationObserver(function (mutations) {
894      mutations.forEach(function (mutation) {
895        if (mutation.type === 'attributes') {
896          enableRoleButtonKeyboardSupport(mutation.target);
897          return;
898        }
899
900        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
901          if (!node || node.nodeType !== 1) return;
902          enableRoleButtonKeyboardSupport(node);
903        });
904      });
905    });
906
907    observer.observe(document.body, {
908      attributes: true,
909      attributeFilter: ['role'],
910      childList: true,
911      subtree: true
912    });
913  }
914
915  function addRoleButtonKeyboardSupport(button) {
916    if (button.getAttribute('data-atm-a11y-role-button-keydown') === '1') return;
917    if (button.getAttribute('data-atm-a11y-button-keydown') === '1') {
918      button.setAttribute('data-atm-a11y-role-button-keydown', '1');
919      return;
920    }
921
922    button.setAttribute('data-atm-a11y-role-button-keydown', '1');
923
924    button.addEventListener('keydown', function (e) {
925      if (e.key !== 'Enter' && e.key !== ' ') return;
926
927      e.preventDefault();
928      button.click();
929    });
930  }
931
932  function removeXAnchorToggleAriaLabels(root) {
933    var scope = root || document;
934    var selector = [
935      '.x-anchor-toggle[aria-label]',
936      '.x-anchor-toggle [aria-label]',
937      '[class*="x-anchor-toggle"][aria-label]',
938      '[class*="x-anchor-toggle"] [aria-label]'
939    ].join(',');
940    var toggles = Array.prototype.slice.call(scope.querySelectorAll(selector));
941
942    if (scope.matches && scope.matches(selector)) {
943      toggles.unshift(scope);
944    }
945
946    toggles.forEach(function (toggle) {
947      toggle.removeAttribute('aria-label');
948    });
949  }
950
951  function scheduleXAnchorToggleAriaLabelCleanup() {
952    [100, 500, 1500, 3000].forEach(function (delay) {
953      window.setTimeout(removeXAnchorToggleAriaLabels, delay);
954    });
955  }
956
957  function watchXAnchorToggleAriaLabels() {
958    if (!window.MutationObserver) return;
959
960    var observer = new MutationObserver(function (mutations) {
961      mutations.forEach(function (mutation) {
962        if (mutation.type === 'attributes') {
963          removeXAnchorToggleAriaLabels(mutation.target);
964          return;
965        }
966
967        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
968          if (!node || node.nodeType !== 1) return;
969          removeXAnchorToggleAriaLabels(node);
970        });
971      });
972    });
973
974    observer.observe(document.body, {
975      attributes: true,
976      attributeFilter: ['aria-label', 'class'],
977      childList: true,
978      subtree: true
979    });
980  }
981
982  function fixEmptyHashLinks(root) {
983    var scope = root || document;
984    var links = Array.prototype.slice.call(scope.querySelectorAll('a[href="#"]'));
985
986    if (scope.matches && scope.matches('a[href="#"]')) {
987      links.unshift(scope);
988    }
989
990    links.forEach(function (link) {
991      link.setAttribute('href', 'javascript:void(0)');
992    });
993  }
994
995  function watchEmptyHashLinks() {
996    if (!window.MutationObserver) return;
997
998    var observer = new MutationObserver(function (mutations) {
999      mutations.forEach(function (mutation) {
1000        if (mutation.type === 'attributes') {
1001          fixEmptyHashLinks(mutation.target);
1002          return;
1003        }
1004
1005        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1006          if (!node || node.nodeType !== 1) return;
1007          fixEmptyHashLinks(node);
1008        });
1009      });
1010    });
1011
1012    observer.observe(document.body, {
1013      childList: true,
1014      subtree: true,
1015      attributes: true,
1016      attributeFilter: ['href']
1017    });
1018  }
1019
1020  function wrapXMenuNavigation(root) {
1021    var scope = root || document;
1022    var menus = Array.prototype.slice.call(scope.querySelectorAll('ul.x-menu'));
1023
1024    if (scope.matches && scope.matches('ul.x-menu')) {
1025      menus.unshift(scope);
1026    }
1027
1028    menus.forEach(function (menu) {
1029      if (menu.closest('nav')) return;
1030      if (!menu.parentNode) return;
1031
1032      var nav = document.createElement('nav');
1033      nav.setAttribute('aria-label', 'Main Navigation');
1034      menu.parentNode.insertBefore(nav, menu);
1035      nav.appendChild(menu);
1036    });
1037  }
1038
1039  function watchXMenuNavigation() {
1040    if (!window.MutationObserver) return;
1041
1042    var observer = new MutationObserver(function (mutations) {
1043      mutations.forEach(function (mutation) {
1044        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1045          if (!node || node.nodeType !== 1) return;
1046          wrapXMenuNavigation(node);
1047        });
1048      });
1049    });
1050
1051    observer.observe(document.body, { childList: true, subtree: true });
1052  }
1053
1054  function fixCornerstoneAccordionHeaders(root) {
1055    var scope = root || document;
1056    var headers = Array.prototype.slice.call(scope.querySelectorAll('.x-acc-header'));
1057
1058    if (scope.matches && scope.matches('.x-acc-header')) {
1059      headers.unshift(scope);
1060    }
1061
1062    headers.forEach(function (header) {
1063      if ((header.getAttribute('role') || '').trim().toLowerCase() === 'button') {
1064        header.removeAttribute('role');
1065      }
1066
1067      if ((header.getAttribute('type') || '').trim().toLowerCase() === 'button') {
1068        header.removeAttribute('type');
1069      }
1070
1071      header.removeAttribute('aria-expanded');
1072      header.removeAttribute('aria-controls');
1073    });
1074  }
1075
1076  function removeXAccHeaderRoles(root) {
1077    var scope = root || document;
1078    var headers = Array.prototype.slice.call(scope.querySelectorAll('.x-acc-header[role]'));
1079
1080    if (scope.matches && scope.matches('.x-acc-header[role]')) {
1081      headers.unshift(scope);
1082    }
1083
1084    headers.forEach(function (header) {
1085      header.removeAttribute('role');
1086    });
1087  }
1088
1089  function watchXAccHeaderRoles() {
1090    if (!window.MutationObserver) return;
1091
1092    var observer = new MutationObserver(function (mutations) {
1093      mutations.forEach(function (mutation) {
1094        if (mutation.type === 'attributes') {
1095          removeXAccHeaderRoles(mutation.target);
1096          return;
1097        }
1098
1099        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1100          if (!node || node.nodeType !== 1) return;
1101          removeXAccHeaderRoles(node);
1102        });
1103      });
1104    });
1105
1106    observer.observe(document.body, {
1107      attributes: true,
1108      attributeFilter: ['role', 'class'],
1109      childList: true,
1110      subtree: true
1111    });
1112  }
1113
1114  function removeXAccItemChildDivRoles(root) {
1115    var scope = root || document;
1116    var childDivs = Array.prototype.slice.call(scope.querySelectorAll('.x-acc-item > div[role]'));
1117
1118    if (scope.matches && scope.matches('.x-acc-item > div[role]')) {
1119      childDivs.unshift(scope);
1120    }
1121
1122    childDivs.forEach(function (childDiv) {
1123      childDiv.removeAttribute('role');
1124    });
1125  }
1126
1127  function watchXAccItemChildDivRoles() {
1128    if (!window.MutationObserver) return;
1129
1130    var observer = new MutationObserver(function (mutations) {
1131      mutations.forEach(function (mutation) {
1132        if (mutation.type === 'attributes') {
1133          removeXAccItemChildDivRoles(mutation.target);
1134          if (mutation.target.parentElement) {
1135            removeXAccItemChildDivRoles(mutation.target.parentElement);
1136          }
1137          return;
1138        }
1139
1140        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1141          if (!node || node.nodeType !== 1) return;
1142          removeXAccItemChildDivRoles(node);
1143          if (node.parentElement) {
1144            removeXAccItemChildDivRoles(node.parentElement);
1145          }
1146        });
1147      });
1148    });
1149
1150    observer.observe(document.body, {
1151      attributes: true,
1152      attributeFilter: ['role', 'class'],
1153      childList: true,
1154      subtree: true
1155    });
1156  }
1157
1158  function watchCornerstoneAccordionHeaders() {
1159    if (!window.MutationObserver) return;
1160
1161    var observer = new MutationObserver(function (mutations) {
1162      mutations.forEach(function (mutation) {
1163        if (mutation.type === 'attributes') {
1164          fixCornerstoneAccordionHeaders(mutation.target);
1165          return;
1166        }
1167
1168        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1169          if (!node || node.nodeType !== 1) return;
1170          fixCornerstoneAccordionHeaders(node);
1171        });
1172      });
1173    });
1174
1175    observer.observe(document.body, {
1176      childList: true,
1177      subtree: true,
1178      attributes: true,
1179      attributeFilter: ['role', 'type', 'aria-expanded', 'aria-controls', 'class']
1180    });
1181  }
1182
1183  function isButtonLikeLink(link) {
1184    var href = ((link.getAttribute('href') || '') + '').trim().toLowerCase();
1185    var className = getClassString(link).toLowerCase();
1186
1187    if (href === '' || href === '#' || href.indexOf('javascript:') === 0) return true;
1188
1189    return /(?:popup|modal|lightbox|trigger|toggle|accordion|tab|carousel|slider|slide|swiper|slick|splide|owl|et-pb-arrow|elementor-swiper-button|elementor-menu-toggle|x-anchor|cs-|cornerstone)/i.test(className);
1190  }
1191
1192  function removeNavMenuButtonRoles() {
1193    var links = document.querySelectorAll('.site-menu a[role="button"], .nav-menu a[role="button"], .x-menu a[role="button"]');
1194    links.forEach(function (link) {
1195      removeRoleButtonFromNavMenuLink(link);
1196    });
1197  }
1198
1199  function watchNavMenuButtonRoles() {
1200    if (!window.MutationObserver) return;
1201
1202    var observer = new MutationObserver(function (mutations) {
1203      mutations.forEach(function (mutation) {
1204        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1205          if (!node || node.nodeType !== 1) return;
1206
1207          if (node.matches && node.matches('.site-menu a[role="button"], .nav-menu a[role="button"], .x-menu a[role="button"]')) {
1208            removeRoleButtonFromNavMenuLink(node);
1209          }
1210
1211          Array.prototype.slice.call(node.querySelectorAll('.site-menu a[role="button"], .nav-menu a[role="button"], .x-menu a[role="button"]')).forEach(function (link) {
1212            removeRoleButtonFromNavMenuLink(link);
1213          });
1214        });
1215      });
1216    });
1217
1218    observer.observe(document.body, { childList: true, subtree: true });
1219  }
1220
1221  function isNavMenuLink(link) {
1222    return !!(link.closest && link.closest('.site-menu, .nav-menu, .x-menu'));
1223  }
1224
1225  function removeRoleButtonFromNavMenuLink(link) {
1226    if (!isNavMenuLink(link)) return;
1227    if ((link.getAttribute('role') || '').trim().toLowerCase() === 'button') {
1228      link.removeAttribute('role');
1229    }
1230  }
1231
1232  function preventHashJumpForButtonLikeLink(link) {
1233    if (link.getAttribute('data-atm-a11y-prevent-hash-jump') === '1') return;
1234
1235    var href = ((link.getAttribute('href') || '') + '').trim().toLowerCase();
1236    if (href !== '#') return;
1237
1238    link.setAttribute('data-atm-a11y-prevent-hash-jump', '1');
1239    link.addEventListener('click', function () {
1240      var scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0;
1241      var scrollY = window.pageYOffset || document.documentElement.scrollTop || 0;
1242
1243      armEmptyHashJumpGuard(scrollX, scrollY);
1244      restoreHashLinkScroll(scrollX, scrollY);
1245    });
1246  }
1247
1248  function enableEmptyHashJumpGuard() {
1249    if (document.documentElement.getAttribute('data-atm-a11y-empty-hash-guard') === '1') return;
1250    document.documentElement.setAttribute('data-atm-a11y-empty-hash-guard', '1');
1251
1252    document.addEventListener('click', function () {
1253      if (!hashJumpGuardScroll || Date.now() > hashJumpGuardUntil) return;
1254
1255      var scrollX = hashJumpGuardScroll.x;
1256      var scrollY = hashJumpGuardScroll.y;
1257      window.setTimeout(function () {
1258        restoreUnexpectedTopJump(scrollX, scrollY);
1259      }, 0);
1260      window.setTimeout(function () {
1261        restoreUnexpectedTopJump(scrollX, scrollY);
1262      }, 80);
1263      window.setTimeout(function () {
1264        restoreUnexpectedTopJump(scrollX, scrollY);
1265      }, 300);
1266    }, true);
1267  }
1268
1269  function armEmptyHashJumpGuard(scrollX, scrollY) {
1270    hashJumpGuardScroll = { x: scrollX, y: scrollY };
1271    hashJumpGuardUntil = Date.now() + 5000;
1272  }
1273
1274  function restoreUnexpectedTopJump(scrollX, scrollY) {
1275    var currentY = window.pageYOffset || document.documentElement.scrollTop || 0;
1276    if (scrollY < 80 || currentY > 5) return;
1277    if (window.location.hash !== '' && window.location.hash !== '#') return;
1278
1279    window.scrollTo(scrollX, scrollY);
1280    removeEmptyHashFromUrl();
1281  }
1282
1283  function restoreHashLinkScroll(scrollX, scrollY) {
1284    var restore = function () {
1285      if (window.location.hash === '' || window.location.hash === '#') {
1286        window.scrollTo(scrollX, scrollY);
1287        removeEmptyHashFromUrl();
1288      }
1289    };
1290
1291    window.setTimeout(restore, 0);
1292    window.setTimeout(restore, 50);
1293    window.setTimeout(restore, 250);
1294
1295    if (window.requestAnimationFrame) {
1296      window.requestAnimationFrame(restore);
1297    }
1298  }
1299
1300  function removeEmptyHashFromUrl() {
1301    if (!window.history || !window.history.replaceState) return;
1302    if (window.location.hash !== '' && window.location.hash !== '#') return;
1303
1304    var cleanUrl = window.location.href.replace(/#$/, '');
1305    if (cleanUrl !== window.location.href) {
1306      window.history.replaceState(null, document.title, cleanUrl);
1307    }
1308  }
1309
1310  function getButtonLikeLinkLabel(link) {
1311    var className = getClassString(link).toLowerCase();
1312    var text = link.textContent.replace(/\s+/g, ' ').trim();
1313
1314    if (/(?:next|arrow-next|slick-next|swiper-button-next|splide__arrow--next|owl-next)/i.test(className)) {
1315      return 'Next Slide';
1316    }
1317    if (/(?:prev|previous|arrow-prev|slick-prev|swiper-button-prev|splide__arrow--prev|owl-prev)/i.test(className)) {
1318      return 'Previous Slide';
1319    }
1320    if (/(?:close|dismiss)/i.test(className + ' ' + text)) {
1321      return 'Close';
1322    }
1323    if (/(?:menu|nav-toggle|hamburger|elementor-menu-toggle)/i.test(className)) {
1324      return 'Toggle menu';
1325    }
1326    if (text) {
1327      return text;
1328    }
1329    if (/(?:popup|modal|book|appointment|newpopup)/i.test(className + ' ' + text)) {
1330      return 'Book Appointment';
1331    }
1332
1333    return 'Button';
1334  }
1335
1336  function getClassString(el) {
1337    return (el.getAttribute('class') || '').toString();
1338  }
1339
1340  // -------------------------------------------------------------------------
1341  // 7. Modal trigger links with tabindex="-1" and href="#"
1342  //    <a href="#" class="dm_classic_modal" tabindex="-1">read more</a>
1343  // -------------------------------------------------------------------------
1344  function fixModalLinks() {
1345    var modals = document.querySelectorAll('a.dm_classic_modal');
1346    modals.forEach(function (a) {
1347      a.setAttribute('role', 'button');
1348      if (a.getAttribute('tabindex') === '-1') {
1349        a.setAttribute('tabindex', '0');
1350      }
1351      a.addEventListener('keydown', function (e) {
1352        if (e.key === 'Enter' || e.key === ' ') {
1353          e.preventDefault();
1354          a.click();
1355        }
1356      });
1357      preventHashJumpForButtonLikeLink(a);
1358    });
1359  }
1360
1361  // -------------------------------------------------------------------------
1362  // 7. Slick carousel — fix broken ARIA tab roles
1363  //    The Slick slider incorrectly uses role="tablist/tab/tabpanel".
1364  //    Replace with the ARIA carousel pattern (group + roledescription).
1365  // -------------------------------------------------------------------------
1366  function fixSlickCarousel() {
1367    // Fix dot navigation (was role="tablist")
1368    var dotLists = document.querySelectorAll('ul.slick-dots[role="tablist"]');
1369    dotLists.forEach(function (ul) {
1370      ul.removeAttribute('role');
1371      ul.setAttribute('aria-label', 'Slide navigation');
1372    });
1373
1374    // Fix dot buttons (was role="tab")
1375    var dotButtons = document.querySelectorAll('ul.slick-dots button[role="tab"]');
1376    dotButtons.forEach(function (btn) {
1377      btn.removeAttribute('role');
1378      btn.removeAttribute('aria-selected');
1379    });
1380
1381    // Fix slides (was role="tabpanel")
1382    var slides = document.querySelectorAll('.slick-slide[role="tabpanel"]');
1383    slides.forEach(function (slide, index) {
1384      slide.removeAttribute('role');
1385      slide.setAttribute('aria-roledescription', 'slide');
1386      if (!slide.getAttribute('aria-label')) {
1387        slide.setAttribute('aria-label', 'Slide ' + (index + 1));
1388      }
1389    });
1390
1391    // Wrap the slider track in a group if not already
1392    var sliders = document.querySelectorAll('.slick-slider');
1393    sliders.forEach(function (slider) {
1394      if (!slider.getAttribute('role')) {
1395        slider.setAttribute('role', 'region');
1396        slider.setAttribute('aria-roledescription', 'carousel');
1397        slider.setAttribute('aria-label', 'Image slideshow');
1398      }
1399    });
1400  }
1401
1402  function waitForSlick() {
1403    if (typeof jQuery === 'undefined') return;
1404    jQuery(document).on('init.slick', function () {
1405      fixSlickCarousel();
1406    });
1407  }
1408
1409  // -------------------------------------------------------------------------
1410  // 8. Hidden modals — add aria-hidden when visually hidden
1411  // -------------------------------------------------------------------------
1412  function fixHiddenModals() {
1413    var hiddenModals = document.querySelectorAll('.hidde-modals');
1414    hiddenModals.forEach(function (modal) {
1415      var style = window.getComputedStyle(modal);
1416      if (style.display === 'none' || style.visibility === 'hidden') {
1417        modal.setAttribute('aria-hidden', 'true');
1418      }
1419
1420      // Watch for visibility changes (e.g. when modal opens via JS)
1421      var observer = new MutationObserver(function () {
1422        var s = window.getComputedStyle(modal);
1423        if (s.display === 'none' || s.visibility === 'hidden') {
1424          modal.setAttribute('aria-hidden', 'true');
1425        } else {
1426          modal.removeAttribute('aria-hidden');
1427          // Move focus into the modal
1428          var focusable = modal.querySelector('button, [href], input, [tabindex]:not([tabindex="-1"])');
1429          if (focusable) focusable.focus();
1430        }
1431      });
1432
1433      observer.observe(modal, { attributes: true, attributeFilter: ['style', 'class'] });
1434    });
1435  }
1436
1437  // -------------------------------------------------------------------------
1438  // 9. Decorative icon <i> elements inside links → aria-hidden
1439  // -------------------------------------------------------------------------
1440  function fixIconElements() {
1441    var icons = document.querySelectorAll('a i[class*="fa "], a i[class*=" fa-"]');
1442    icons.forEach(function (icon) {
1443      if (!icon.getAttribute('aria-hidden')) {
1444        icon.setAttribute('aria-hidden', 'true');
1445      }
1446    });
1447  }
1448
1449  // -------------------------------------------------------------------------
1450  // 10. Secondary navigation div — remove incorrect role="navigation"
1451  //     (PHP handles this on first render;
1451 JS handles cache/CDN edge cases)
1452  // -------------------------------------------------------------------------
1453  function fixPageLandmarks() {
1454    if (isFixEnabled('main_landmark')) {
1455      ensureLandmarkRole('header, .site-header, #masthead', 'banner');
1456    }
1457    if (isFixEnabled('contentinfo_landmark')) {
1458      fixContentinfoLandmarks();
1459    }
1460
1461    var main = findMainLandmark();
1462    if (!main && isFixEnabled('main_landmark')) {
1463      main = findMainContentContainer();
1464      if (main) {
1465        main.setAttribute('role', 'main');
1466      }
1467    }
1468
1469    if (main) {
1470      if (isFixEnabled('main_landmark')) {
1471        main = repairMainLandmarkContent(main);
1472      }
1473      if (isFixEnabled('landmark_nesting')) {
1474        removeParentLandmarks(main);
1475      }
1476      if (isFixEnabled('main_landmark') && !main.id) {
1477        main.id = 'main-content';
1478      }
1479      updateSkipLinkTarget(main.id);
1480    }
1481
1482    if (isFixEnabled('main_landmark')) {
1483      wrapUnlandmarkedBodyContent();
1484    }
1485  }
1486
1487  function changeMainContentDivToMain(root) {
1488    var scope = root || document;
1489    var mainContent = null;
1490
1491    if (scope.matches && scope.matches('div#main-content')) {
1492      mainContent = scope;
1493    } else if (scope.querySelector) {
1494      mainContent = scope.querySelector('div#main-content');
1495    }
1496
1497    if (!mainContent) return;
1498
1499    var replacement = replaceElementTag(mainContent, 'main', []);
1500    updateSkipLinkTarget(replacement.id);
1501  }
1502
1503  function watchMainContentDivToMain() {
1504    if (!window.MutationObserver) return;
1505
1506    var observer = new MutationObserver(function (mutations) {
1507      mutations.forEach(function (mutation) {
1508        if (mutation.type === 'attributes') {
1509          changeMainContentDivToMain(mutation.target);
1510          return;
1511        }
1512
1513        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1514          if (!node || node.nodeType !== 1) return;
1515          changeMainContentDivToMain(node);
1516        });
1517      });
1518    });
1519
1520    observer.observe(document.body, {
1521      attributes: true,
1522      attributeFilter: ['id'],
1523      childList: true,
1524      subtree: true
1525    });
1526  }
1527
1528  function updateSkipLinkTarget(mainId) {
1529    var skipLink = document.querySelector('.atm-skip-link');
1530    if (skipLink && mainId) {
1531      skipLink.setAttribute('href', '#' + mainId);
1532    }
1533  }
1534
1535  function repairMainLandmarkContent(main) {
1536    if (!mainContainsRepeatedLandmarks(main)) {
1537      return main;
1538    }
1539
1540    var betterMain = findUniqueContentInsideMain(main);
1541    if (betterMain && betterMain !== main) {
1542      moveMainLandmark(main, betterMain);
1543      return betterMain;
1544    }
1545
1546    demoteRepeatedLandmarksInsideMain(main);
1547    return main;
1548  }
1549
1550  function mainContainsRepeatedLandmarks(main) {
1551    return !!main.querySelector('header, footer, nav, [role="banner"], [role="contentinfo"], [role="navigation"]');
1552  }
1553
1554  function findUniqueContentInsideMain(main) {
1555    var selectors = [
1556      '.entry-content',
1557      '.page-content',
1558      '.post-content',
1559      '.site-main',
1560      '#primary',
1561      '.content-area',
1562      '.et_builder_inner_content',
1563      '.elementor',
1564      '.elementor-section-wrap',
1565      '.wp-block-post-content'
1566    ];
1567
1568    for (var i = 0; i < selectors.length; i++) {
1569      var candidate = main.querySelector(selectors[i]);
1570      if (!candidate || candidate === main) continue;
1571      if (isInsideRepeatedLandmark(candidate)) continue;
1572      if (mainContainsRepeatedLandmarks(candidate)) continue;
1573      if (!hasMeaningfulContent(candidate)) continue;
1574
1575      return candidate;
1576    }
1577
1578    return null;
1579  }
1580
1581  function moveMainLandmark(oldMain, newMain) {
1582    var oldId = oldMain.id;
1583    oldMain.removeAttribute('role');
1584
1585    if (oldMain.tagName.toLowerCase() === 'main') {
1586      replaceElementTag(oldMain, 'div', ['role', 'id']);
1587    }
1588
1589    if (newMain.tagName.toLowerCase() !== 'main') {
1590      newMain.setAttribute('role', 'main');
1591    }
1592
1593    if (!newMain.id) {
1594      newMain.id = oldId || 'main-content';
1595    }
1596  }
1597
1598  function demoteRepeatedLandmarksInsideMain(main) {
1599    var repeated = main.querySelectorAll('header, footer, nav, [role="banner"], [role="contentinfo"], [role="navigation"]');
1600    repeated.forEach(function (el) {
1601      if (el.closest('[data-atm-a11y-preserve-landmark]')) return;
1602      if (!isBuilderOrThemeRepeatedElement(el)) return;
1603
1604      el.removeAttribute('role');
1605      if (/^(header|footer|nav)$/i.test(el.tagName)) {
1606        replaceElementTag(el, 'div', ['role']);
1607      }
1608    });
1609  }
1610
1611  function isBuilderOrThemeRepeatedElement(el) {
1612    var className = getClassString(el).toLowerCase();
1613    var id = (el.id || '').toLowerCase();
1614    var text = (id + ' ' + className);
1615
1616    return /(?:header|footer|nav|menu|masthead|site-header|site-footer|et-l--header|et-l--footer|elementor-location-header|elementor-location-footer|x-masthead|x-navbar|cs-header)/i.test(text);
1617  }
1618
1619  function isInsideRepeatedLandmark(el) {
1620    return !!el.closest('header, footer, nav, [role="banner"], [role="contentinfo"], [role="navigation"]');
1621  }
1622
1623  function replaceElementTag(el, tagName, excludedAttrs) {
1624    var replacement = document.createElement(tagName);
1625    copyAttributes(el, replacement, excludedAttrs || []);
1626
1627    while (el.firstChild) {
1628      replacement.appendChild(el.firstChild);
1629    }
1630
1631    el.parentNode.replaceChild(replacement, el);
1632    return replacement;
1633  }
1634
1635  function removeParentLandmarks(main) {
1636    var parentLandmark = getParentLandmark(main);
1637
1638    while (parentLandmark) {
1639      if (!canDemoteLandmark(parentLandmark)) {
1640        break;
1641      }
1642
1643      demoteLandmark(parentLandmark);
1644      parentLandmark = getParentLandmark(main);
1645    }
1646  }
1647
1648  function getParentLandmark(el) {
1649    if (!el.parentElement) return null;
1650    return el.parentElement.closest('main, aside, section[aria-label], section[aria-labelledby], [role="main"], [role="complementary"], [role="region"]');
1651  }
1652
1653  function canDemoteLandmark(el) {
1654    if (!el || el === document.body) return false;
1655    if (el.matches('header, footer, nav, [role="banner"], [role="contentinfo"], [role="navigation"], [role="search"]')) return false;
1656    return true;
1657  }
1658
1659  function demoteLandmark(el) {
1660    if (el.tagName.toLowerCase() === 'main') {
1661      var replacement = document.createElement('div');
1662      copyAttributes(el, replacement, ['role']);
1663
1664      while (el.firstChild) {
1665        replacement.appendChild(el.firstChild);
1666      }
1667
1668      el.parentNode.replaceChild(replacement, el);
1669      return;
1670    }
1671
1672    el.removeAttribute('role');
1673    if (el.tagName.toLowerCase() === 'section') {
1674      el.removeAttribute('aria-label');
1675      el.removeAttribute('aria-labelledby');
1676    }
1677  }
1678
1679  function copyAttributes(from, to, excludedAttrs) {
1680    Array.prototype.slice.call(from.attributes).forEach(function (attr) {
1681      if (excludedAttrs.indexOf(attr.name) === -1) {
1682        to.setAttribute(attr.name, attr.value);
1683      }
1684    });
1685  }
1686
1687  function ensureLandmarkRole(selector, role) {
1688    var el = document.querySelector(selector);
1689    if (!el || isInsideLandmark(el)) return;
1690
1691    if (!isNativeLandmark(el, role) && !el.getAttribute('role')) {
1692      el.setAttribute('role', role);
1693    }
1694  }
1695
1696  function fixContentinfoLandmarks() {
1697    var globalFooter = findGlobalFooterElement();
1698    var contentinfos = document.querySelectorAll('footer, [role="contentinfo"]');
1699
1700    contentinfos.forEach(function (el) {
1701      if (el === globalFooter) return;
1702
1703      if (el.getAttribute('role') === 'contentinfo') {
1704        el.removeAttribute('role');
1705      }
1706
1707      if (el.tagName.toLowerCase() === 'footer') {
1708        var replacement = replaceElementTag(el, 'div', ['role']);
1709        replacement.setAttribute('data-atm-a11y-demoted-footer', 'true');
1710      }
1711    });
1712
1713    if (!globalFooter) return;
1714    if (globalFooter.tagName.toLowerCase() !== 'footer' && !globalFooter.getAttribute('role')) {
1715      globalFooter.setAttribute('role', 'contentinfo');
1716    }
1717  }
1718
1719  function findGlobalFooterElement() {
1720    var selectors = [
1721      '#site-footer',
1722      '#colophon',
1723      '.site-footer',
1724      '.footer-main',
1725      '.footer-wrapper',
1726      '.elementor-location-footer',
1727      '.et-l--footer',
1728      '.x-colophon',
1729      '.x-site-footer',
1730      '.cs-footer',
1731      'body > footer'
1732    ];
1733
1734    for (var i = 0; i < selectors.length; i++) {
1735      var footer = document.querySelector(selectors[i]);
1736      if (footer && isGlobalFooterCandidate(footer)) {
1737        return footer;
1738      }
1739    }
1740
1741    return null;
1742  }
1743
1744  function isGlobalFooterCandidate(el) {
1745    if (el.closest('main, article, aside, section, [role="main"], [role="article"], [role="complementary"], [role="region"]')) {
1746      return false;
1747    }
1748
1749    var classAndId = ((el.id || '') + ' ' + getClassString(el)).toLowerCase();
1750    if (/(?:site-footer|colophon|global-footer|footer-main|footer-wrapper|elementor-location-footer|et-l--footer|x-colophon|x-site-footer|cs-footer)/i.test(classAndId)) {
1751      return true;
1752    }
1753
1754    return el.parentElement === document.body && el.tagName.toLowerCase() === 'footer';
1755  }
1756
1757  function findMainLandmark() {
1758    return document.querySelector('main, [role="main"]');
1759  }
1760
1761  function findMainContentContainer() {
1762    var selectors = [
1763      '#main-content',
1764      '#content',
1765      '.site-content',
1766      '#primary',
1767      '.content-area',
1768      '#main',
1769      '.main-content'
1770    ];
1771
1772    for (var i = 0; i < selectors.length; i++) {
1773      var el = document.querySelector(selectors[i]);
1774      if (el && !isInsideLandmark(el)) {
1775        return el;
1776      }
1777    }
1778
1779    return null;
1780  }
1781
1782  function wrapUnlandmarkedBodyContent() {
1783    var wrapper = null;
1784    var children = Array.prototype.slice.call(document.body.children);
1785
1786    children.forEach(function (child) {
1787      if (!needsLandmarkWrapper(child)) {
1788        wrapper = null;
1789        return;
1790      }
1791
1792      if (!wrapper) {
1793        wrapper = document.createElement('div');
1794        wrapper.setAttribute('role', 'region');
1795        wrapper.setAttribute('aria-label', 'Page content');
1796        wrapper.className = 'atm-a11y-landmark-region';
1797        child.parentNode.insertBefore(wrapper, child);
1798      }
1799
1800      wrapper.appendChild(child);
1801    });
1802  }
1803
1804  function needsLandmarkWrapper(el) {
1805    if (isIgnoredBodyChild(el)) return false;
1806    if (isSkipLinkElement(el)) return false;
1807    if (isLandmark(el) || isInsideLandmark(el)) return false;
1808    if (hasLandmarkDescendant(el)) return false;
1809    if (el.id === 'wpadminbar') return false;
1810    if (el.classList.contains('atm-a11y-landmark-region')) return false;
1811    return hasMeaningfulContent(el);
1812  }
1813
1814  function isIgnoredBodyChild(el) {
1815    return ['SCRIPT', 'STYLE', 'NOSCRIPT', 'LINK', 'META', 'TEMPLATE'].indexOf(el.tagName) !== -1;
1816  }
1817
1818  function isSkipLinkElement(el) {
1819    return el.classList.contains('atm-skip-link') || el.classList.contains('atm-skip-links');
1820  }
1821
1822  function hasMeaningfulContent(el) {
1823    if (el.hasAttribute('hidden') || el.getAttribute('aria-hidden') === 'true') return false;
1824    if (el.textContent.trim() !== '') return true;
1825    return !!el.querySelector('img, svg, canvas, iframe, video, audio, input, button, select, textarea, a[href]');
1826  }
1827
1828  function isInsideLandmark(el) {
1829    var selector = 'main, header, footer, nav, aside, section[aria-label], section[aria-labelledby], [role="main"], [role="banner"], [role="contentinfo"], [role="navigation"], [role="complementary"], [role="search"], [role="region"]';
1830    return !!el.parentElement && !!el.parentElement.closest(selector);
1831  }
1832
1833  function hasLandmarkDescendant(el) {
1834    var selector = 'main, header, footer, nav, aside, section[aria-label], section[aria-labelledby], [role="main"], [role="banner"], [role="contentinfo"], [role="navigation"], [role="complementary"], [role="search"], [role="region"]';
1835    return !!el.querySelector(selector);
1836  }
1837
1838  function isLandmark(el) {
1839    return isNativeLandmark(el) || !!el.matches('[role="main"], [role="banner"], [role="contentinfo"], [role="navigation"], [role="complementary"], [role="search"], [role="region"]');
1840  }
1841
1842  function isNativeLandmark(el, expectedRole) {
1843    var tag = el.tagName.toLowerCase();
1844    var roleMap = {
1845      main: 'main',
1846      header: 'banner',
1847      footer: 'contentinfo',
1848      nav: 'navigation',
1849      aside: 'complementary'
1850    };
1851
1852    if (tag === 'section') {
1853      return !expectedRole && (!!el.getAttribute('aria-label') || !!el.getAttribute('aria-labelledby'));
1854    }
1855
1856    return expectedRole ? roleMap[tag] === expectedRole : !!roleMap[tag];
1857  }
1858
1859  function fixFrameAccessibleNames() {
1860    var frames = document.querySelectorAll('iframe, frame');
1861    frames.forEach(function (frame, index) {
1862      fixSingleFrameAccessibleName(frame, index);
1863    });
1864  }
1865
1866  function frameHasPresentationRole(frame) {
1867    var role = (frame.getAttribute('role') || '').trim().toLowerCase();
1868    return role === 'none' || role === 'presentation';
1869  }
1870
1871  function hasFrameAccessibleNameOrPresentationRole(frame) {
1872    if (frameHasPresentationRole(frame)) return true;
1873    if ((frame.getAttribute('title') || '').trim() !== '') return true;
1874    if ((frame.getAttribute('aria-label') || '').trim() !== '') return true;
1875    return getFrameLabelledByText(frame) !== '';
1876  }
1877
1878  function watchFrameAccessibleNames() {
1879    if (!window.MutationObserver) return;
1880
1881    var observer = new MutationObserver(function (mutations) {
1882      mutations.forEach(function (mutation) {
1883        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1884          if (!node || node.nodeType !== 1) return;
1885
1886          if (node.matches && node.matches('iframe, frame')) {
1887            fixSingleFrameAccessibleName(node);
1888          }
1889
1890          Array.prototype.slice.call(node.querySelectorAll('iframe, frame')).forEach(function (frame) {
1891            fixSingleFrameAccessibleName(frame);
1892          });
1893        });
1894      });
1895    });
1896
1897    observer.observe(document.body, { childList: true, subtree: true });
1898  }
1899
1900  function fixSingleFrameAccessibleName(frame, index) {
1901    if (frameHasPresentationRole(frame)) return;
1902
1903    var title = (frame.getAttribute('title') || '').trim();
1904    if (title !== '') {
1905      if ((frame.getAttribute('aria-label') || '').trim() !== title) {
1906        frame.setAttribute('aria-label', title);
1907      }
1908      return;
1909    }
1910
1911    if ((frame.getAttribute('aria-label') || '').trim() !== '') return;
1912    if (getFrameLabelledByText(frame) !== '') return;
1913
1914    title = getFrameTitle(frame, index || 0);
1915    frame.setAttribute('title', title);
1916    frame.setAttribute('aria-label', title);
1917  }
1918
1919  function getFrameLabelledByText(frame) {
1920    var labelledBy = (frame.getAttribute('aria-labelledby') || '').trim();
1921    if (labelledBy === '') return '';
1922
1923    return labelledBy.split(/\s+/).map(function (id) {
1924      var el = document.getElementById(id);
1925      return el ? el.textContent.trim() : '';
1926    }
1926).filter(Boolean).join(' ').trim();
1927  }
1928
1929  function getFrameTitle(frame, index) {
1930    var frameText = [
1931      frame.getAttribute('src') || '',
1932      frame.getAttribute('data-src') || '',
1933      frame.getAttribute('data-lazy-src') || '',
1934      frame.getAttribute('data-original-src') || '',
1935      frame.getAttribute('allow') || '',
1936      frame.getAttribute('class') || '',
1937      frame.getAttribute('id') || '',
1938      frame.getAttribute('name') || ''
1939    ].join(' ').toLowerCase();
1940
1941    if (frameText.indexOf('google.com/maps') !== -1 || frameText.indexOf('maps.google') !== -1) {
1942      return 'Map';
1943    }
1944    if (frameText.indexOf('youtube.com') !== -1 || frameText.indexOf('youtu.be') !== -1) {
1945      return 'Video';
1946    }
1947    if (frameText.indexOf('vimeo.com') !== -1) {
1948      return 'Video';
1949    }
1950    if (frameText.indexOf('picture-in-picture') !== -1 || frameText.indexOf('fullscreen') !== -1 || frameText.indexOf('fitvid') !== -1 || frameText.indexOf('video') !== -1) {
1951      return 'Video';
1952    }
1953    if (frameText.indexOf('recaptcha') !== -1 || frameText.indexOf('captcha') !== -1) {
1954      return 'Verification challenge';
1955    }
1956
1957    return 'Embedded content ' + (index + 1);
1958  }
1959
1960  function fixSecondaryNavRole() {
1961    var secondaryNav = document.getElementById('secondary-navigation');
1962    if (secondaryNav && secondaryNav.tagName.toLowerCase() !== 'nav') {
1963      secondaryNav.removeAttribute('role');
1964    }
1965  }
1966
1967  function fixMinorNavigationLandmarks() {
1968    var navs = document.querySelectorAll('nav, [role="navigation"]');
1969    navs.forEach(function (el) {
1970      demoteMinorNavigationLandmark(el);
1971    });
1972  }
1973
1974  function watchMinorNavigationLandmarks() {
1975    if (!window.MutationObserver) return;
1976
1977    var observer = new MutationObserver(function (mutations) {
1978      mutations.forEach(function (mutation) {
1979        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
1980          if (!node || node.nodeType !== 1) return;
1981
1982          if (node.matches && node.matches('nav, [role="navigation"]')) {
1983            demoteMinorNavigationLandmark(node);
1984          }
1985
1986          Array.prototype.slice.call(node.querySelectorAll('nav, [role="navigation"]')).forEach(function (el) {
1987            demoteMinorNavigationLandmark(el);
1988          });
1989        });
1990      });
1991    });
1992
1993    observer.observe(document.body, { childList: true, subtree: true });
1994  }
1995
1996  function demoteMinorNavigationLandmark(el) {
1997    if (!isMinorNavigationLandmark(el)) return;
1998
1999    el.removeAttribute('role');
2000    el.setAttribute('data-atm-a11y-demoted-navigation', 'true');
2001
2002    if (el.tagName.toLowerCase() === 'nav') {
2003      replaceElementTag(el, 'div', ['role']);
2004    }
2005  }
2006
2007  function isMinorNavigationLandmark(el) {
2008    if (el.hasAttribute('data-atm-a11y-preserve-navigation')) return false;
2009
2010    var text = (
2011      el.tagName + ' ' +
2012      (el.id || '') + ' ' +
2013      getClassString(el) + ' ' +
2014      (el.getAttribute('aria-label') || '') + ' ' +
2015      getLabelledByText(el) + ' ' +
2016      getVisibleText(el)
2017    ).toLowerCase();
2018
2019    if (/(?:secondary|utility|top-bar|topbar|footer|social|share|sharing|breadcrumb|breadcrumbs|pagination|pager|post-navigation|posts-navigation|image-navigation|comment-navigation|legal|privacy|terms|copyright|related|tag|tags|categor|archive|sidebar|widget|quick-links|quicklinks)/i.test(text)) {
2020      return true;
2021    }
2022
2023    if (isKeyNavigationLandmark(text)) {
2024      return false;
2025    }
2026
2027    return el.tagName.toLowerCase() === 'nav' && el.querySelectorAll('a[href]').length > 0;
2028  }
2029
2030  function isKeyNavigationLandmark(text) {
2031    return /(?:primary|main|site-navigation|main-navigation|header-navigation|primary-menu|main-menu|mobile-menu|mega-menu|site-menu|menu-primary|menu-main|nav-menu|menu-item|current-menu-item|\bhome\b|\babout\b|\bservices?\b|\bcontact\b|\blocations?\b|\bpatients?\b|\bdentistry\b|\bdental\b)/i.test(text);
2032  }
2033
2034  // -------------------------------------------------------------------------
2035  // 11. Links without discernible text
2036  // -------------------------------------------------------------------------
2037  function fixLinksWithoutDiscernibleText() {
2038    var links = document.querySelectorAll('a');
2039    links.forEach(function (link, index) {
2040      fixLinkWithoutDiscernibleText(link, index);
2041    });
2042  }
2043
2044  function watchLinksWithoutDiscernibleText() {
2045    if (!window.MutationObserver) return;
2046
2047    var observer = new MutationObserver(function (mutations) {
2048      mutations.forEach(function (mutation) {
2049        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
2050          if (!node || node.nodeType !== 1) return;
2051
2052          if (isLinkElement(node)) {
2053            fixLinkWithoutDiscernibleText(node, 0);
2054          }
2055
2056          Array.prototype.slice.call(node.querySelectorAll('a')).forEach(function (link, index) {
2057            fixLinkWithoutDiscernibleText(link, index);
2058          });
2059        });
2060      });
2061    });
2062
2063    observer.observe(document.body, { childList: true, subtree: true });
2064  }
2065
2066  function fixLinkWithoutDiscernibleText(link, index) {
2067    if ((link.getAttribute('aria-label') || '').trim() !== '') return;
2068    if (getVisibleText(link) !== '') return;
2069
2070    var context = getLinkContextText(link);
2071    link.setAttribute('aria-label', context ? getContextualLinkAction(link, index) + ' ' + context : getFallbackLinkLabel(link, index));
2072
2073    if (isFixEnabled('aria_label_visible_text')) {
2074      fixAriaLabelForVisibleText(link);
2075    }
2076  }
2077
2078  function getFallbackLinkLabel(link, index) {
2079    var labels = [
2080      'Related link',
2081      'Additional information',
2082      'More information',
2083      'Details link',
2084      'Learn more link',
2085      'Read more link',
2086      'Continue link',
2087      'Open related content',
2088      'View additional details',
2089      'Explore related content',
2090      'More details'
2091    ];
2092
2093    return labels[Math.abs(hashString((link.getAttribute('href') || '') + ' ' + index)) % labels.length];
2094  }
2095
2096  function isLinkElement(el) {
2097    if (!el.matches) return false;
2098    return el.matches('a');
2099  }
2100
2101  function hasDiscernibleText(link) {
2102    if ((link.getAttribute('aria-label') || '').trim() !== '') return true;
2103    if ((link.getAttribute('title') || '').trim() !== '') return true;
2104    if (getLabelledByText(link) !== '') return true;
2105    if (getLabelFromLinkedImage(link) !== '') return true;
2106    if (getLabelFromSvg(link) !== '') return true;
2107    return getVisibleText(link) !== '' && !isIconOnlyLink(link);
2108  }
2109
2110  function getLabelledByText(link) {
2111    var labelledBy = (link.getAttribute('aria-labelledby') || '').trim();
2112    if (labelledBy === '') return '';
2113
2114    var text = labelledBy.split(/\s+/).map(function (id) {
2115      var el = document.getElementById(id);
2116      return el ? el.textContent.trim() : '';
2117    }).filter(Boolean).join(' ');
2118
2119    return text.trim();
2120  }
2121
2122  function getVisibleText(link) {
2123    return link.textContent.replace(/\s+/g, ' ').trim();
2124  }
2125
2126  function isIconOnlyLink(link) {
2127    if (link.querySelector('img[alt], svg[aria-label], svg title')) return false;
2128    if (link.querySelector('i[class], span[class*="icon"], span[class*="fa-"], span[class*="dashicons"]')) return true;
2129
2130    var text = getVisibleText(link);
2131    return text !== '' && /^[\uE000-\uF8FF\s]+$/.test(text);
2132  }
2133
2134  function getLabelFromLinkedImage(link) {
2135    var img = link.querySelector('img[alt]');
2136    return img ? img.getAttribute('alt').trim() : '';
2137  }
2138
2139  function getLabelFromSvg(link) {
2140    var labelledSvg = link.querySelector('svg[aria-label], svg title');
2141    if (!labelledSvg) return '';
2142
2143    if (labelledSvg.getAttribute && labelledSvg.getAttribute('aria-label')) {
2144      return labelledSvg.getAttribute('aria-label').trim();
2145    }
2146
2147    return labelledSvg.textContent.trim();
2148  }
2149
2150  function getLabelFromNearbyHeading(link) {
2151    var heading = findPrecedingHeading(link);
2152    return heading ? 'Learn more about ' + heading : '';
2153  }
2154
2155  function getLabelFromButtonLikeClasses(link) {
2156    var classText = (link.className && typeof link.className === 'string') ? link.className : '';
2157    classText = classText.replace(/\b(button|btn|link|icon|fa|fas|far|fab)\b/g, '').replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim();
2158    return classText ? 'Open ' + classText : '';
2159  }
2160
2161  function getLabelFromHref(href) {
2162    if (!href || href === '#') return '';
2163    if (href.indexOf('tel:') === 0) return 'Call ' + href.replace('tel:', '').replace(/[^\d+]/g, '');
2164    if (href.indexOf('mailto:') === 0) return 'Email ' + href.replace('mailto:', '').split('?')[0];
2165
2166    var cleaned = href.split('#')[0].split('?')[0].replace(/\/$/, '');
2167    var segment = cleaned.split('/').pop();
2168    if (!segment) return '';
2169
2170    return 'Open ' + segment.replace(/\.[^.]+$/, '').replace(/[-_]+/g, ' ');
2171  }
2172
2173  // -------------------------------------------------------------------------
2174  // 12. PDF links should warn users before opening a PDF reader
2175  // -------------------------------------------------------------------------
2176  function fixPdfLinkWarnings() {
2177    var links = document.querySelectorAll('a[href]');
2178    links.forEach(function (link) {
2179      fixPdfLinkWarning(link);
2180    });
2181  }
2182
2183  function watchPdfLinkWarnings() {
2184    if (!window.MutationObserver) return;
2185
2186    var observer = new MutationObserver(function (mutations) {
2187      mutations.forEach(function (mutation) {
2188        Array.prototype.slice.call(mutation.addedNodes).forEach(function (node) {
2189          if (!node || node.nodeType !== 1) return;
2190
2191          if (isLinkElement(node)) {
2192            fixPdfLinkWarning(node);
2193          }
2194
2195          Array.prototype.slice.call(node.querySelectorAll('a[href]')).forEach(function (link) {
2196            fixPdfLinkWarning(link);
2197          });
2198        });
2199      });
2200    });
2201
2202    observer.observe(document.body, { childList: true, subtree: true });
2203  }
2204
2205  function fixPdfLinkWarning(link) {
2206    var href = link.getAttribute('href') || '';
2207    if (!isPdfHref(href)) return;
2208
2209    var label = (link.getAttribute('aria-label') || '').trim() ||
2210      getVisibleText(link) ||
2211      (link.getAttribute('title') || '').trim() ||
2212      getLabelFromHref(href) ||
2213      'Open document';
2214
2215    if (!textMentionsPdf(label)) {
2216      label += ' (opens a PDF file)';
2217    }
2218
2219    link.setAttribute('aria-label', label);
2220    appendPdfWarningText(link);
2221
2222    var title = (link.getAttribute('title') || '').trim();
2223    if (title === '') {
2224      link.setAttribute('title', 'Opens a PDF file');
2225    } else if (!textMentionsPdf(title)) {
2226      link.setAttribute('title', title + ' - opens a PDF file');
2227    }
2228  }
2229
2230  function isPdfHref(href) {
2231    try {
2232      return /\.pdf$/i.test(new URL(href, window.location.href).pathname);
2233    } catch (e) {
2234      return /\.pdf(?:$|[?#])/i.test(href);
2235    }
2236  }
2237
2238  function textMentionsPdf(text) {
2239    return /\bpdf\b/i.test(text || '');
2240  }
2241
2242  function appendPdfWarningText(link) {
2243    if (link.querySelector('.atm-a11y-pdf-warning')) return;
2244    if (textMentionsPdf(getVisibleText(link))) return;
2245
2246    var warning = document.createElement('span');
2247    warning.className = 'atm-a11y-sr-only atm-a11y-pdf-warning';
2248    warning.textContent = ' (opens a PDF file)';
2249    link.appendChild(warning);
2250  }
2251
2252})();

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.