PageSourceSearch

https://www.valleychildrens.org/modules/custom/hg_mercury_dxe_feat…/js/language-switcher-modal.js?tm8v49

js valleychildrens.org collected 2026-10-02 03:27:43 UTC 9,904 bytes, 252 lines download raw bytes

1(function (Drupal, drupalSettings) {
2  'use strict';
3
4  Drupal.behaviors.hgLanguageSwitcher = {
5    attach: function (context) {
6      var settings = drupalSettings.hg_language_switcher || {};
7      var languages = settings.languages || {};
8      var languageUrls = settings.language_urls || {};
9      var currentLanguage = settings.current_language || 'en';
10      var langKeys = Object.keys(languages);
11
12      if (langKeys.length < 2) {
13        return;
14      }
15
16      // --- Sub-header dropdown (existing logic) ---
17      var subHeaderTriggers = (context || document).querySelectorAll('.hgm-language-switcher--sub-header');
18
19      subHeaderTriggers.forEach(function (trigger) {
20        if (trigger.dataset.hgLangProcessed) {
21          return;
22        }
23        trigger.dataset.hgLangProcessed = 'true';
24
25        // Update trigger label.
26        var labelSpan = trigger.querySelector('.hgm-language-switcher-label');
27        if (labelSpan && languages[currentLanguage]) {
28          labelSpan.textContent = currentLanguage === 'es' ? 'Español' : languages[currentLanguage];
29        }
30
31        // Build dropdown HTML.
32        var heading = Drupal.t('Select your language');
33        var description = Drupal.t('Explore the library in your preferred language. Availability of some content may vary.');
34
35        var dropdownHtml = '<div class="hgm-language-switcher-dropdown" role="menu">' +
36          '<h4>' + heading + '</h4>' +
37          '<p>' + description + '</p>' +
38          '<ul>';
39
40        langKeys.forEach(function (langcode) {
41          var isActive = langcode === currentLanguage;
42          var url = languageUrls[langcode] || '#';
43          dropdownHtml += '<li>' +
44            '<a href="' + Drupal.checkPlain(url) + '"' +
45              ' role="menuitem"' +
46              ' hreflang="' + Drupal.checkPlain(langcode) + '"' +
47              ' lang="' + Drupal.checkPlain(langcode) + '"' +
48              (isActive ? ' class="active"' : '') + '>' +
49              '<span>' + Drupal.checkPlain(langcode === 'es' ? 'Español' : languages[langcode]) + '</span>' +
50              (isActive ? '<i class="fa-solid fa-check"></i>' : '') +
51              '</a>' +
52            '</li>';
53        });
54
55        dropdownHtml += '</ul></div>';
56
57        var wrapper = trigger.closest('.hgm-language-switcher-wrapper') || trigger.parentElement;
58        wrapper.insertAdjacentHTML('beforeend', dropdownHtml);
59
60        var dropdown = wrapper.querySelector('.hgm-language-switcher-dropdown');
61
62        trigger.addEventListener('click', function (e) {
63          e.preventDefault();
64          e.stopPropagation();
65          var isOpen = dropdown.classList.contains('open');
66
67          closeAllDropdowns();
68
69          if (!isOpen) {
70            dropdown.classList.add('open');
71            trigger.setAttribute('aria-expanded', 'true');
72          }
73        });
74      });
75
76      // Bind document-level close handlers once.
77      if (!document.documentElement.dataset.hgLangGlobalBound) {
78        document.documentElement.dataset.hgLangGlobalBound = 'true';
79
80        document.addEventListener('click', function (e) {
81          if (!e.target.closest('.hgm-language-switcher-wrapper')) {
82            closeAllDropdowns();
83          }
84        });
85
86        document.addEventListener('keydown', function (e) {
87          if (e.key === 'Escape' || e.key === 'Esc') {
88            closeAllDropdowns();
89          }
90        });
91      }
92
93      function closeAllDropdowns() {
94        document.querySelectorAll('.hgm-language-switcher-dropdown.open').forEach(function (d) {
95          d.classList.remove('open');
96        });
97        document.querySelectorAll('.hgm-language-switcher--sub-header[aria-expanded="true"]').forEach(function (t) {
98          t.setAttribute('aria-expanded', 'false');
99        });
100      }
101
102      // --- Drill-down language switcher ---
103      var drilldownItems = (context || document).querySelectorAll('.hgm-language-switcher-drilldown');
104
105      drilldownItems.forEach(function (drilldownLi) {
106        if (drilldownLi.dataset.hgLangDrilldownProcessed) {
107          return;
108        }
109        drilldownLi.dataset.hgLangDrilldownProcessed = 'true';
110
111        // Update trigger label dynamically.
112        var triggerLabel = drilldownLi.querySelector('.hgm-language-switcher-drilldown__label');
113        if (triggerLabel && languages[currentLanguage]) {
114          triggerLabel.textContent = currentLanguage === 'es' ? 'Español' : languages[currentLanguage];
115        }
116
117        // Build drill-down child content.
118        var backLabel = Drupal.t('Back');
119        var heading = Drupal.t('Select your language');
120        var description = Drupal.t('Explore the library in your preferred language. Availability of some content may vary.');
121
122        var childHtml = '';
123
124        // Back button.
125        childHtml += '<li class="hgm-language-switcher-drilldown__back">' +
126          '<a title="' + Drupal.checkPlain(backLabel) + '" href="#" class="back">' +
127            '<i class="fa-solid fa-chevron-left"></i> ' + Drupal.checkPlain(backLabel) +
128          '</a>' +
129        '</li>';
130
131        // Header with heading and description.
132        childHtml += '<li class="hgm-language-switcher-drilldown__header">' +
133          '<div>' +
134            '<h4>' + heading + '</h4>' +
135            '<p>' + description + '</p>' +
136          '</div>' +
137        '</li>';
138
139        // Language items.
140        langKeys.forEach(function (langcode) {
141          var isActive = langcode === currentLanguage;
142          var url = languageUrls[langcode] || '#';
143          childHtml += '<li class="hgm-language-switcher-drilldown__item">' +
144            '<a href="' + Drupal.checkPlain(url) + '"' +
145              ' hreflang="' + Drupal.checkPlain(langcode) + '"' +
146              ' lang="' + Drupal.checkPlain(langcode) + '"' +
147              (isActive ? ' class="active"' : '') + '>
147' +
148              '<span>' + Drupal.checkPlain(langcode === 'es' ? 'Español' : languages[langcode]) + '</span>' +
149              (isActive ? '<i class="fa-solid fa-check"></i>' : '') +
150            '</a>' +
151          '</li>';
152        });
153
154        // Populate the empty <ul>.
155        var childUl = drilldownLi.querySelector('ul');
156        childUl.innerHTML = childHtml;
157
158        // Wire up back button — directly reset drill-down state instead of
159        // delegating to the shared back button. The shared button binds to
160        // 'touchstart' on touch devices, so .click() (a 'click' event) is
161        // ignored on iOS/mobile.
162        var backBtn = childUl.querySelector('.hgm-language-switcher-drilldown__back a.back');
163        if (backBtn) {
164          backBtn.addEventListener('click', function (e) {
165            e.preventDefault();
166            e.stopPropagation();
167            resetDrillDown(drilldownLi);
168          });
169          backBtn.addEventListener('touchend', function (e) {
170            e.preventDefault();
171            e.stopPropagation();
172            resetDrillDown(drilldownLi);
173          });
174        }
175      });
176
177      // Reset drill-down to level 0 (primary nav) state.
178      function resetDrillDown(drilldownLi) {
179        var drillDown = drilldownLi.closest('.hgm-drill-down');
180        if (!drillDown) {
181          return;
182        }
183
184        // Remove active class from language switcher dropdown.
185        drilldownLi.classList.remove('active');
186
187        // Reset level to 0.
188        drillDown.setAttribute('data-level', '0');
189
190        var nav = drillDown.querySelector('nav');
191        if (nav) {
192          // Hide the back button in the shared heading.
193          var heading = nav.querySelector('.hgm-drill-down__heading');
194          if (heading) {
195            heading.classList.remove('back-visible');
196            // Reset heading title to original values.
197            var titleEl = heading.querySelector('.title');
198            if (titleEl) {
199              var titleLink = titleEl.querySelector('a');
200              if (titleLink) {
201                titleLink.setAttribute('href', titleEl.getAttribute('data-original-link') || '#');
202                titleLink.textContent = titleEl.getAttribute('data-original-title') || 'Main Menu';
203              }
204            }
205          }
206        }
207
208        // Reset the menu transform and recalculate height from root items.
209        var menu = drillDown.querySelector('.hgm-drill-down__menu');
210        if (menu) {
211          menu.style.transform = 'translateX(0)';
212          var listHeight = 0;
213          for (var i = 0; i < menu.children.length; i++) {
214            listHeight += menu.children[i].offsetHeight;
215          }
216          menu.style.height = listHeight + 'px';
217        }
218      }
219
220      // Reset drill-down state when off-canvas is closed, so reopening
221      // shows the primary nav instead of the language sub-panel.
222      if (!document.documentElement.dataset.hgLangOffcanvasBound) {
223        document.documentElement.dataset.hgLangOffcanvasBound = 'true';
224
225        var closeBtn = document.querySelector('.hgm-off-canvas__close');
226        if (closeBtn) {
227          closeBtn.addEventListener('click', function () {
228            var drilldownLi = document.querySelector('.hgm-language-switcher-drilldown.active');
229            if (drilldownLi) {
230              resetDrillDown(drilldownLi);
231            }
232          });
233        }
234
235        // Also reset when clicking outside the off-canvas (body click close).
236        var observer = new MutationObserver(function (mutations) {
237          mutations.forEach(function (mutation) {
238            if (mutation.attributeName === 'class' &&
239                !document.body.classList.contains('offcanvas-open')) {
240              var drilldownLi = document.querySelector('.hgm-language-switcher-drilldown.active');
241              if (drilldownLi) {
242                resetDrillDown(drilldownLi);
243              }
244            }
245          });
246        });
247        observer.observe(document.body, { attributes: true, attributeFilter: ['class'] });
248      }
249    }
250  };
251
252})(Drupal, drupalSettings);

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.