PageSourceSearch

https://drbronner.ch/wp-content/themes/drbronner-classic/assets/js/header.js?ver=1775414191

js drbronner.ch collected 2026-10-07 03:11:23 UTC 13,866 bytes, 514 lines download raw bytes

1(function () {
2  "use strict";
3
4  var header = document.getElementById("site-header");
5  if (!header) {
6    return;
7  }
8
9  var initFrenchLinkGuard = function () {
10    var path = window.location.pathname || "/";
11    var isFrenchPath = path === "/fr" || path === "/fr/" || path.indexOf("/fr/") === 0;
12    if (!isFrenchPath) {
13      return;
14    }
15
16    var blockedPrefixes = ["/wp-admin/", "/wp-content/", "/wp-includes/", "/wp-json/", "/xmlrpc.php"];
17    var frenchPathMap = {
18      "/": "/fr/",
19      "/shop/": "/fr/shop/",
20      "/mein-konto/": "/fr/mon-compte/",
21      "/my-account/": "/fr/mon-compte/",
22      "/warenkorb/": "/fr/panier-dachat/",
23      "/cart/": "/fr/panier-dachat/",
24      "/kasse/": "/fr/caisse/",
25      "/checkout/": "/fr/caisse/",
26      "/ueber-uns/": "/fr/a-propos/",
27      "/kontakt/": "/fr/contact/",
28      "/faq/": "/fr/faq/",
29      "/impressum/": "/fr/mentions-legales/",
30      "/agb/": "/fr/conditions-generales/",
31      "/versand/": "/fr/expedition/",
32      "/datenschutzerklaerung/": "/fr/declaration-de-confidentialite/",
33      "/all-one-blog/": "/fr/all-one-blog/",
34      "/store-locator/": "/fr/store-locator/",
35      "/fairtrade-projekte/": "/fr/fairtrade-projekte/"
36    };
37
38    var normalizePath = function (value) {
39      if (!value) {
40        return "/";
41      }
42      var result = value.charAt(0) === "/" ? value : "/" + value;
43      if (result !== "/" && result.charAt(result.length - 1) !== "/") {
44        result += "/";
45      }
46      return result.toLowerCase();
47    };
48
49    var localizeUrl = function (href) {
50      if (!href) {
51        return href;
52      }
53
54      if (/^(?:#|mailto:|tel:|javascript:|\?)/i.test(href)) {
55        return href;
56      }
57
58      var parsed;
59      try {
60        parsed = new URL(href, window.location.href);
61      } catch (error) {
62        return href;
63      }
64
65      if (parsed.host !== window.location.host) {
66        return href;
67      }
68
69      var normalized = normalizePath(parsed.pathname);
70      if (normalized === "/fr/" || normalized.indexOf("/fr/") === 0) {
71        return parsed.toString();
72      }
73
74      for (var i = 0; i < blockedPrefixes.length; i += 1) {
75        if (normalized.indexOf(blockedPrefixes[i]) === 0) {
76          return href;
77        }
78      }
79
80      var targetPath = frenchPathMap[normalized] || "/fr" + normalized;
81      parsed.pathname = targetPath;
82      return parsed.toString();
83    };
84
85    var shouldSkipAnchor = function (anchor) {
86      if (!anchor) {
87        return true;
88      }
89
90      var href = anchor.getAttribute("href") || "";
91      if ("" === href || "#" === href) {
92        return true;
93      }
94
95      var text = ((anchor.textContent || "") + " " + (anchor.getAttribute("aria-label") || "")).trim().toUpperCase();
96      if ("DE" === text) {
97        return true;
98      }
99
100      return false;
101    };
102
103    var localizeAnchor = function (anchor) {
104      if (shouldSkipAnchor(anchor)) {
105        return;
106      }
107
108      var original = anchor.getAttribute("href") || "";
109      var localized = localizeUrl(original);
110      if (localized && localized !== original) {
111        anchor.setAttribute("href", localized);
112      }
113    };
114
115    var localizeAllAnchors = function (root) {
116      var scope = root || document;
117      var anchors = scope.querySelectorAll ? scope.querySelectorAll("a[href]") : [];
118      anchors.forEach(localizeAnchor);
119      if (scope.matches && scope.matches("a[href]")) {
120        localizeAnchor(scope);
121      }
122    };
123
124    localizeAllAnchors(document);
125
126    var observer = new MutationObserver(function (mutations) {
127      mutations.forEach(function (mutation) {
128        mutation.addedNodes.forEach(function (node) {
129          if (!node || node.nodeType !== 1) {
130            return;
131          }
132          localizeAllAnchors(node);
133        });
134      });
135    });
136
137    observer.observe(document.body, { childList: true, subtree: true });
138  };
139
140  initFrenchLinkGuard();
141
142  var initTouchDesktopDropdowns = function () {
143    var desktopNav = header.querySelector(".site-nav-desktop .site-navigation");
144    if (!desktopNav || !window.matchMedia) {
145      return;
146    }
147
148    var dropdownItems = Array.prototype.slice.call(
149      desktopNav.querySelectorAll(".site-nav__item.site-nav--has-dropdown, .site-nav__item.site-nav--language")
150    );
151    if (!dropdownItems.length) {
152      return;
153    }
154
155    var touchQuery = window.matchMedia("(hover: none), (pointer: coarse)");
156
157    var isTouchContext = function (event) {
158      if (touchQuery.matches) {
159        return true;
160      }
161
162      if (!event || typeof event !== "object") {
163        return false;
164      }
165
166      if (event.pointerType && event.pointerType.toLowerCase() === "touch") {
167        return true;
168      }
169
170      return false;
171    };
172
173    var closeAll = function (exceptItem) {
174      dropdownItems.forEach(function (item) {
175        var trigger = item.querySelector(":scope > .site-nav__link--has-dropdown");
176        var shouldStayOpen = !!exceptItem && item === exceptItem;
177        item.classList.toggle("is-touch-open", shouldStayOpen);
178        if (trigger) {
179          trigger.setAttribute("aria-expanded", shouldStayOpen ? "true" : "false");
180        }
181      });
182    };
183
184    dropdownItems.forEach(function (item) {
185      var trigger = item.querySelector(":scope > .site-nav__link--has-dropdown");
186      if (!trigger) {
187        return;
188      }
189
190      trigger.setAttribute("aria-haspopup", "true");
191      trigger.setAttribute("aria-expanded", "false");
192
193      trigger.addEventListener("click", function (event) {
194        if (!isTouchContext(event)) {
195          return;
196        }
197
198        event.preventDefault();
199        event.stopPropagation();
200
201        var isOpen = item.classList.contains("is-touch-open");
202        closeAll(isOpen ? null : item);
203      });
204
205      var dropdownLinks = item.querySelectorAll(".site-nav__dropdown a");
206      dropdownLinks.forEach(function (link) {
207        link.addEventListener("click", function () {
208          closeAll();
209        });
210      });
211    });
212
213    document.addEventListener("pointerdown", function (event) {
214      if (!isTouchContext(event)) {
215        return;
216      }
217
218      var clickedInside = dropdownItems.some(function (item) {
219        return item.contains(event.target);
220      });
221
222      if (!clickedInside) {
223        closeAll();
224      }
225    });
226
227    document.addEventListener("keydown", function (event) {
228      if (event.key === "Escape") {
229        closeAll();
230      }
231    });
232
233    var handleContextChange = function () {
234      closeAll();
235    };
236
237    if (typeof touchQuery.addEventListener === "function") {
238      touchQuery.addEventListener("change", handleContextChange);
239    } else if (typeof touchQuery.addListener === "function") {
240      touchQuery.addListener(handleContextChange);
241    }
242  };
243
244  initTouchDesktopDropdowns();
245
246  var initAnnouncementBar = function () {
247    var bars = document.querySelectorAll(".js-announcement-bar");
248    if (!bars.length) {
249      return;
250    }
251
252    var resizeTimer = null;
253    var FONT_LOAD_TIMEOUT = 1500;
254    var mobileQuery = window.matchMedia("(max-width: 767px)");
255
256    var getViewportNode = function (bar) {
257      return bar.querySelector(".announcement__link") || bar.querySelector(".announcement") || bar;
258    };
259
260    var getBarGap = function (bar) {
261      var styles = window.getComputedStyle(bar);
262      var parsed = Number.parseFloat(styles.getPropertyValue("--announcement-gap"));
263      return Number.isFinite(parsed) && parsed >= 0 ? parsed : 80;
264    };
265
266    var clearGeneratedCopies = function (track) {
267      var generated = track.querySelectorAll("[data-announcement-generated='1']");
268      if (!generated.length) {
269        return;
270      }
271
272      generated.forEach(function (node) {
273        if (node && node.parentNode) {
274          node.parentNode.removeChild(node);
275        }
276      });
277    };
278
279    var updateBar = function (bar) {
280      var track = bar.querySelector(".js-announcement-track");
281      var copy = bar.querySelector(".js-announcement-copy");
282      var viewport = getViewportNode(bar);
283      var availableWidth;
284      var contentWidth;
285      var gap;
286      var distance;
287      var itemSpan;
288      var minTrackWidth;
289      var requiredCopies;
290      var currentCopies;
291      var speed;
292      var duration;
293      var clone;
294
295      if (!track || !copy) {
296        return;
297      }
298
299      clearGeneratedCopies(track);
300      bar.classList.remove("is-marquee");
301
302      if (!mobileQuery.matches) {
303        return;
304      }
305
306      availableWidth = Math.max(0, Math.ceil(viewport.getBoundingClientRect().width));
307      contentWidth = Math.max(0, Math.ceil(copy.getBoundingClientRect().width));
308      if (availableWidth < 1 || contentWidth < 1) {
309        return;
310      }
311
312      gap = getBarGap(bar);
313      itemSpan = contentWidth + gap;
314      minTrackWidth = availableWidth + itemSpan * 2;
315      requiredCopies = Math.max(2, Math.ceil(minTrackWidth / Math.max(1, itemSpan)));
316      currentCopies = 1;
317
318      while (currentCopies < requiredCopies) {
319        clone = copy.cloneNode(true);
320        clone.classList.add("announcement__copy--clone");
321        clone.setAttribute("aria-hidden", "true");
322        clone.setAttribute("data-announcement-generated", "1");
323        track.appendChild(clone);
324        currentCopies += 1;
325      }
326
327      distance = itemSpan;
328      speed = Number(bar.getAttribute("data-speed")) || Number.parseFloat(window.getComputedStyle(bar).getPropertyValue("--announcement-speed")) || 45;
329      duration = Math.max(8, distance / Math.max(20, speed));
330      bar.style.setProperty("--announcement-duration", duration.toFixed(2) + "s");
331      bar.style.setProperty("--announcement-shift", "-" + String(Math.round(distance)) + "px");
332      bar.classList.add("is-marquee");
333    };
334
335    var updateAllBars = function () {
336      bars.forEach(updateBar);
337    };
338
339    window.addEventListener(
340      "resize",
341      function () {
342        if (resizeTimer) {
343          window.clearTimeout(resizeTimer);
344        }
345        resizeTimer = window.setTimeout(updateAllBars, 140);
346      },
347      { passive: true }
348    );
349
350    if (typeof mobileQuery.addEventListener === "function") {
351      mobileQuery.addEventListener("change", updateAllBars);
352    } else if (typeof mobileQuery.addListener === "function") {
353      mobileQuery.addListener(updateAllBars);
354    }
355
356    window.addEventListener(
357      "load",
358      function () {
359        updateAllBars();
360      },
361      { once: true, passive: true }
362    );
363
364    if (document.fonts && document.fonts.ready && typeof document.fonts.ready.then === "function") {
365      Promise.race([
366        document.fonts.ready,
367        new Promise(function (resolve) {
368          window.setTimeout(resolve, FONT_LOAD_TIMEOUT);
369        })
370      ])
371        .then(function () {
372          updateAllBars();
373        })
374        .catch(function () {
375          updateAllBars();
376        });
377    }
378
379    updateAllBars();
380  };
381
382  initAnnouncementBar();
383
384  var searchPanel = header.querySelector("[data-header-search]");
385  var searchToggles = header.querySelectorAll("[data-header-search-toggle]");
386
387  if (!searchPanel || !searchToggles.length) {
388    return;
389  }
390
391  var searchCloseTimer = null;
392  var scrollTicking = false;
393  var barVisible = true;
394  var lastY = 0;
395
396  var setSearchToggleState = function (isOpen) {
397    searchToggles.forEach(function (toggle) {
398      toggle.setAttribute("aria-expanded", String(isOpen));
399    });
400  };
401
402  var isSearchOpen = function () {
403    return !searchPanel.hidden && searchPanel.classList.contains("is-open");
404  };
405
406  var openSearch = function () {
407    if (searchCloseTimer) {
408      window.clearTimeout(searchCloseTimer);
409      searchCloseTimer = null;
410    }
411
412    searchPanel.hidden = false;
413    window.requestAnimationFrame(function () {
414      searchPanel.classList.add("is-open");
415    });
416    setSearchToggleState(true);
417
418    var input = searchPanel.querySelector('input[type="search"]');
419    if (input) {
420      input.focus();
421    }
422  };
423
424  var closeSearch = function () {
425    if (searchPanel.hidden && !searchPanel.classList.contains("is-open")) {
426      return;
427    }
428
429    searchPanel.classList.remove("is-open");
430    setSearchToggleState(false);
431
432    if (searchCloseTimer) {
433      window.clearTimeout(searchCloseTimer);
434    }
435
436    searchCloseTimer = window.setTimeout(function () {
437      searchPanel.hidden = true;
438      searchCloseTimer = null;
439    }, 220);
440  };
441
442  var syncScrollState = function () {
443    var y = Math.max(0, Math.round(window.scrollY || 0));
444
445    if (barVisible && y > 56 && y > lastY) {
446      barVisible = false;
447      header.classList.add("is-scrolled");
448    } else if (!barVisible && y <= 0) {
449      barVisible = true;
450      header.classList.remove("is-scrolled");
451    }
452
453    lastY = y;
454    scrollTicking = false;
455  };
456
457  searchToggles.forEach(function (toggle) {
458    toggle.addEventListener("click", function (event) {
459      event.preventDefault();
460      event.stopPropagation();
461
462      if (isSearchOpen()) {
463        closeSearch();
464        return;
465      }
466
467      openSearch();
468    });
469  });
470
471  document.addEventListener("click", function (event) {
472    if (!isSearchOpen()) {
473      return;
474    }
475
476    if (searchPanel.contains(event.target)) {
477      return;
478    }
479
480    var clickedToggle = Array.prototype.some.call(searchToggles, function (toggle) {
481      return toggle.contains(event.target);
482    });
483
484    if (clickedToggle) {
485      return;
486    }
487
488    closeSearch();
489  });
490
491  document.addEventListener("keydown", function (event) {
492    if (event.key === "Escape") {
493      closeSearch();
494    }
495  });
496
497  window.addEventListener(
498    "scroll",
499    function () {
500      if (scrollTicking) {
501        return;
502      }
503
504      scrollTicking = true;
505      window.requestAnimationFrame(syncScrollState);
506    },
507    { passive: true }
508  );
509
510  barVisible = (window.scrollY || 0) <= 0;
511  lastY = Math.max(0, Math.round(window.scrollY || 0));
512  header.classList.toggle("is-scrolled", !barVisible);
513  syncScrollState();
514})();

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.