PageSourceSearch

https://www.dunnedwards.com/wp-content/themes/dunnedwards/assets/js/weglot-a11y.js?v=5.6.0

js dunnedwards.com collected 2026-10-02 03:24:44 UTC 3,932 bytes, 95 lines download raw bytes

1/**
2 * Weglot language-switcher accessibility shim — tracker rows 21, 55 and the vendor half of 20.
3 *
4 * The switcher DOM is built by the Weglot plugin's own front-end JavaScript, so no template in
5 * this theme can reach it. It ships as:
6 *
7 *   <div role="listbox" aria-activedescendant="weglot-language-en"
8 *        aria-label="Language Switcher: English" class="weglot_switcher …">
9 *     <div class="language-option" aria-label="Language selected: English">
10 *       <a role="none" id="weglot-language-en" href="…">English</a>
11 *     </div>
12 *   </div>
13 *
14 * Three separate scan findings come out of that:
15 *
16 *   1. role="listbox" may only contain role="option" children. It contains a generic <div>
17 *      and a link, so the listbox is invalid (ARIA 5.2.6, row 21).
18 *   2. role="none" on an <a> is ignored — a link cannot be made presentational while it is
19 *      still focusable and has an href (row 55).
20 *   3. aria-label on <div class="language-option"> — a bare div is role=generic, which
21 *      prohibits naming, so the label is discarded (ARIA 5.2.8.6, vendor half of row 20).
22 *
23 * The fix is to stop pretending this is a listbox. It is not a selection widget: each option is
24 * a link that navigates to the translated URL. Describing it as navigation is both honest and
25 * valid, and it resolves all three findings without inventing option semantics that the markup
26 * does not support.
27 *
28 * Deliberately conservative:
29 *   - only ARIA attributes are touched; no elements, classes, hrefs or handlers are altered,
30 *     so Weglot's own click behaviour is untouched;
31 *   - `tabindex` is left alone — Weglot may rely on it for focus management;
32 *   - it is idempotent, and re-runs if Weglot re-renders the switcher (it does, on language
33 *     change and on some navigation).
34 */
35(function () {
36  'use strict';
37
38  function fixSwitcher(root) {
39    var switchers = root.querySelectorAll('.weglot_switcher[role="listbox"]');
40
41    Array.prototype.forEach.call(switchers, function (sw) {
42      // A language switcher is navigation, not a listbox. role=navigation also permits an
43      // accessible name, so the existing aria-label on the container stays valid.
44      sw.setAttribute('role', 'navigation');
45      sw.removeAttribute('aria-activedescendant');
46    });
47
48    // The option wrappers are generic <div>s and cannot carry a name; the link inside already
49    // provides one. Scoped to the switcher so nothing else on the page is affected.
50    var wrappers = root.querySelectorAll('.weglot_switcher .language-option[aria-label]');
51    Array.prototype.forEach.call(wrappers, function (el) {
52      el.removeAttribute('aria-label');
53    });
54
55    // role="none" on a focusable link with an href is ignored by browsers anyway; removing it
56    // lets the anchor be announced as the link it actually is.
57    var links = root.querySelectorAll(
58      '.weglot_switcher a[role="none"], .weglot_switcher a[role="presentation"]'
59    );
60    Array.prototype.forEach.call(links, function (a) {
61      a.removeAttribute('role');
62    });
63  }
64
65  function init() {
66    fixSwitcher(document);
67
68    // Weglot rebuilds the switcher on language change and on some navigations, which would
69    // restore the original attributes. Watch for that and re-apply. childList/subtree only —
70    // observing attributes here would re-trigger on our own writes.
71    if (typeof MutationObserver !== 'function' || !document.body) {
72      return;
73    }
74
75    var pending = false;
76    var observer = new MutationObserver(function () {
77      if (pending) {
78        return;
79      }
80      pending = true;
81      window.setTimeout(function () {
82        pending = false;
83        fixSwitcher(document);
84      }, 100);
85    });
86
87    observer.observe(document.body, { childList: true, subtree: true });
88  }
89
90  if (document.readyState === 'loading') {
91    document.addEventListener('DOMContentLoaded', init);
92  } else {
93    init();
94  }
95})();

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.