PageSourceSearch

https://wyzard.ai/blog-chrome/chrome.js?ver=1.2.0

js wyzard.ai collected 2026-09-24 09:42:04 UTC 4,823 bytes, 122 lines download raw bytes

1/**
2 * wyzard blog chrome.js — AUTO-GENERATED
3 * Handles theme toggle, scroll-bg, mega-menu, and mobile menu.
4 * localStorage key 'wyz-theme' is shared with the marketing Next.js site
5 * (ThemeProvider.tsx) so toggling on either surface persists across both.
6 *
7 * Sets window.__wyzChromeActive = true so theme.js (fallback) can bail early
8 * and avoid double-registering the same event handlers.
9 */
10(function () {
11  'use strict';
12
13  // Signal to theme.js that chrome.js is handling all interactions.
14  window.__wyzChromeActive = true;
15
16  var STORAGE_KEY = 'wyz-theme';
17  var root = document.documentElement;
18
19  /* ── Theme helpers ── */
20  function readTheme() {
21    try { return localStorage.getItem(STORAGE_KEY) === 'light' ? 'light' : 'dark'; }
22    catch (_) { return root.dataset.theme === 'light' ? 'light' : 'dark'; }
23  }
24
25  function applyTheme(theme) {
26    root.dataset.theme = theme;
27    root.classList.toggle('light', theme === 'light');
28    root.classList.toggle('dark',  theme === 'dark');
29    try { localStorage.setItem(STORAGE_KEY, theme); } catch (_) { /* private mode */ }
30    document.querySelectorAll('[data-wyz-theme-toggle]').forEach(function (btn) {
31      var next = theme === 'dark' ? 'light' : 'dark';
32      btn.setAttribute('aria-pressed', theme === 'light' ? 'true' : 'false');
33      btn.setAttribute('aria-label', 'Switch to ' + next + ' mode');
34    });
35  }
36
37  /* Apply ASAP — before paint — to avoid FOUC */
38  applyTheme(readTheme());
39
40  /* ── Capture-phase delegates — registered immediately, before any tracker ── */
41  document.addEventListener('click', function (e) {
42    var btn = e.target && e.target.closest && e.target.closest('[data-wyz-theme-toggle]');
43    if (!btn) return;
44    e.preventDefault();
45    e.stopPropagation();
46    e.stopImmediatePropagation();
47    applyTheme(root.dataset.theme === 'light' ? 'dark' : 'light');
48  }, true);
49
50  document.addEventListener('click', function (e) {
51    var btn = e.target && e.target.closest && e.target.closest('[data-wyz-menu-toggle]');
52    if (!btn) return;
53    e.preventDefault();
54    e.stopPropagation();
55    e.stopImmediatePropagation();
56    var mobileMenu = document.getElementById('wyz-mobile-menu');
57    var isOpen = btn.getAttribute('aria-expanded') === 'true';
58    btn.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
59    btn.setAttribute('aria-label', isOpen ? 'Open menu' : 'Close menu');
60    if (mobileMenu) mobileMenu.hidden = isOpen;
61    document.body.style.overflow = isOpen ? '' : 'hidden';
62    btn.classList.toggle('is-open', !isOpen);
63  }, true);
64
65  document.addEventListener('DOMContentLoaded', function () {
66    var header     = document.getElementById('wyz-header');
67    var mobileMenu = document.getElementById('wyz-mobile-menu');
68    var menuToggle = document.querySelector('[data-wyz-menu-toggle]');
69
70    /* ── Scroll-bg ── */
71    function syncHeaderScroll() {
72      if (header) header.classList.toggle('is-scrolled', window.scrollY > 12);
73    }
74    syncHeaderScroll();
75    window.addEventListener('scroll', syncHeaderScroll, { passive: true });
76
77    /* ── Mobile menu link — close on nav ── */
78    if (menuToggle && mobileMenu) {
79      mobileMenu.querySelectorAll('a').forEach(function (link) {
80        link.addEventListener('click', function () {
81          menuToggle.setAttribute('aria-expanded', 'false');
82          menuToggle.setAttribute('aria-label', 'Open menu');
83          mobileMenu.hidden = true;
84          document.body.style.overflow = '';
85          menuToggle.classList.remove('is-open');
86        });
87      });
88    }
89
90    /* ── Mega-menus (desktop) ── */
91    document.querySelectorAll('[data-wyz-mega]').forEach(function (item) {
92      var trigger = item.querySelector('.wyz-nav__trigger');
93      var panel   = item.querySelector('.wyz-mega');
94      if (!trigger || !panel) return;
95
96      var closeTimer;
97
98      function openMenu() {
99        clearTimeout(closeTimer);
100        panel.hidden = false;
101        trigger.setAttribute('aria-expanded', 'true');
102        trigger.querySelector('.wyz-nav__chevron').style.transform = 'rotate(180deg)';
103      }
104      function closeMenu() {
105        closeTimer = setTimeout(function () {
106          panel.hidden = true;
107          trigger.setAttribute('aria-expanded', 'false');
108          trigger.querySelector('.wyz-nav__chevron').style.transform = '';
109        }, 80);
110      }
111
112      item.addEventListener('mouseenter', openMenu);
113      item.addEventListener('mouseleave', closeMenu);
114      trigger.addEventListener('click', function () {
115        panel.hidden ? openMenu() : closeMenu();
116      });
117      /* Keep open when mouse re-enters the panel */
118      panel.addEventListener('mouseenter', function () { clearTimeout(closeTimer); });
119      panel.addEventListener('mouseleave', closeMenu);
120    });
121  });
122})();

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.