PageSourceSearch

https://freysoft.com/blog/wp-content/themes/freysoft/assets/js/blog-nav.js?ver=1783804204

js freysoft.com collected 2026-10-04 21:48:37 UTC 5,899 bytes, 174 lines download raw bytes

1(function () {
2  'use strict';
3
4  /* ── Mobile burger menu ──────────────────────────────── */
5  var header = document.querySelector('[data-header]');
6  if (header) {
7    var burger = header.querySelector('[data-burger]');
8    if (burger) {
9      burger.addEventListener('click', function () {
10        var open = header.classList.toggle('is-open');
11        burger.setAttribute('aria-expanded', open ? 'true' : 'false');
12        document.body.style.overflow = open ? 'hidden' : '';
13      });
14    }
15    header.addEventListener('click', function (e) {
16      if (e.target.closest('.site-header__overlay a')) {
17        header.classList.remove('is-open');
18        document.body.style.overflow = '';
19      }
20    });
21  }
22
23  /* ── Category bar dropdown ────────────────────────────── */
24  var catBar = document.querySelector('[data-cat-bar]');
25  if (catBar) {
26    var toggle = catBar.querySelector('[data-cat-toggle]');
27    var closeCat = function () {
28      catBar.classList.remove('is-open');
29      if (toggle) toggle.setAttribute('aria-expanded', 'false');
30    };
31    if (toggle) {
32      toggle.addEventListener('click', function () {
33        var open = catBar.classList.toggle('is-open');
34        toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
35      });
36    }
37    catBar.addEventListener('click', function (e) {
38      if (e.target.closest('[data-cat-close]')) closeCat();
39    });
40  }
41
42  /* ── Book a call modal (HubSpot meetings embed) ───────── */
43  var modal = document.querySelector('[data-book-modal]');
44  if (modal) {
45    var HUBSPOT_SRC =
46      'https://static.hsappstatic.net/MeetingsEmbed/ex/MeetingsEmbedCode.js';
47    var loaded = false;
48
49    var openModal = function () {
50      modal.classList.add('is-open');
51      modal.setAttribute('aria-hidden', 'false');
52      document.body.style.overflow = 'hidden';
53      if (!loaded) {
54        var s = document.createElement('script');
55        s.src = HUBSPOT_SRC;
56        s.type = 'text/javascript';
57        s.async = true;
58        document.body.appendChild(s);
59        loaded = true;
60      }
61    };
62
63    var closeModal = function () {
64      modal.classList.remove('is-open');
65      modal.setAttribute('aria-hidden', 'true');
66      document.body.style.overflow = '';
67    };
68
69    document.addEventListener('click', function (e) {
70      if (e.target.closest('[data-book-call]')) {
71        e.preventDefault();
72        openModal();
73      } else if (e.target.closest('[data-book-close]')) {
74        closeModal();
75      }
76    });
77
78    document.addEventListener('keydown', function (e) {
79      if (e.key === 'Escape') closeModal();
80    });
81  }
82
83  /* ── Table-of-contents scroll spy ─────────────────────── */
84  var tocLinks = document.querySelectorAll('[data-toc-link]');
85  if (tocLinks.length && 'IntersectionObserver' in window) {
86    var linkById = {};
87    var headings = [];
88    Array.prototype.forEach.call(tocLinks, function (l) {
89      var id = l.getAttribute('data-toc-link');
90      linkById[id] = l;
91      var h = document.getElementById(id);
92      if (h) headings.push(h);
93    });
94
95    var setActive = function (id) {
96      Array.prototype.forEach.call(tocLinks, function (l) {
97        l.classList.remove('is-active');
98      });
99      if (linkById[id]) linkById[id].classList.add('is-active');
100    };
101
102    var observer = new IntersectionObserver(
103      function (entries) {
104        entries.forEach(function (entry) {
105          if (entry.isIntersecting) setActive(entry.target.id);
106        });
107      },
108      { rootMargin: '-110px 0px -70% 0px', threshold: 0 }
109    );
110    headings.forEach(function (h) {
111      observer.observe(h);
112    });
113  }
114
115  /* ── Drag / swipe horizontal carousel ─────────────────── */
116  Array.prototype.forEach.call(
117    document.querySelectorAll('[data-drag-scroll]'),
118    function (track) {
119      var down = false;
120      var moved = false;
121      var startX = 0;
122      var startScroll = 0;
123
124      // Edge fade only when something is actually hidden on that side.
125      var updateFade = function () {
126        var max = track.scrollWidth - track.clientWidth;
127        track.classList.toggle('at-start', track.scrollLeft <= 1);
128        track.classList.toggle('at-end', track.scrollLeft >= max - 1);
129      };
130      updateFade();
131      track.addEventListener('scroll', updateFade, { passive: true });
132      window.addEventListener('resize', updateFade);
133
134      track.addEventListener('pointerdown', function (e) {
135        down = true;
136        moved = false;
137        startX = e.pageX;
138        startScroll = track.scrollLeft;
139        // NOTE: capture is deferred to the first real move — capturing on
140        // pointerdown retargets the follow-up click to the track, which
141        // swallows plain clicks on the cards (desktop navigation broke).
142      });
143      track.addEventListener('pointermove', function (e) {
144        if (!down) return;
145        var dx = e.pageX - startX;
146        if (!moved && Math.abs(dx) > 4) {
147          moved = true;
148          track.classList.add('is-dragging');
149          try {
150            track.setPointerCapture(e.pointerId);
151          } catch (err) {}
152        }
153        if (moved) track.scrollLeft = startScroll - dx;
154      });
155      var end = function () {
156        down = false;
157        track.classList.remove('is-dragging');
158      };
159      track.addEventListener('pointerup', end);
160      track.addEventListener('pointercancel', end);
161      // suppress the click that follows a drag (a plain click never sets moved)
162      track.addEventListener(
163        'click',
164        function (e) {
165          if (moved) {
166            e.preventDefault();
167            e.stopPropagation();
168          }
169        },
170        true
171      );
172    }
173  );
174})();

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.