@gemboss/website-ui 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/website.js CHANGED
@@ -1,186 +1,300 @@
1
- /*! @gemboss/website-ui website.js | spec: docs/markup-contract.md | load with defer */
1
+ /*! @gemboss/website-ui 0.5.0 website.js | GENERATED by scripts/build.mjs from js/website.mjs (the source, with its comments): edit that file | spec: docs/markup-contract.md | load with defer */
2
2
  (function () {
3
- 'use strict';
4
- if (window.gwWebsite) return;
5
- window.gwWebsite = { version: '0.3.0' };
6
- var doc = document, root = doc.documentElement;
7
- function mq(q) { return window.matchMedia ? matchMedia(q) : { matches: false }; }
8
- function watch(m, fn) { if (m.addEventListener) m.addEventListener('change', fn); }
9
- function all(sel, ctx) { return [].slice.call((ctx || doc).querySelectorAll(sel)); }
10
- function on(el, type, fn, opt) { el.addEventListener(type, fn, opt || false); }
11
- function attr(el, name, dflt) { return el.getAttribute(name) || dflt; }
12
- function esc(e) { return e.key === 'Escape'; }
13
- function link(e) { return e.target.closest('a[href]'); }
14
-
15
- // Mega menu. Opened by hover then clicked, a panel stays open: the click meant "open".
16
- function mega() {
17
- var fine = mq('(hover: hover) and (pointer: fine)'), open = null, timer = 0;
18
- var menus = all('[data-gw-mega]').map(function (t) {
19
- var p = doc.getElementById(attr(t, 'data-gw-mega', attr(t, 'aria-controls', '')));
20
- return p && { t: t, p: p, item: t.closest('.gw-nav__item') || t.parentNode };
21
- }).filter(Boolean);
22
- if (!menus.length) return;
23
- function set(m, show, mode) {
24
- m.p.hidden = !show;
25
- m.t.setAttribute('aria-expanded', String(show));
26
- m.item.classList.toggle('is-open', show);
27
- m.mode = mode;
28
- open = show ? m : open === m ? null : open;
29
- }
30
- function show(m, mode) {
31
- clearTimeout(timer);
32
- if (open === m) { if (mode === 'click') m.mode = mode; return; }
33
- if (open) set(open, false);
34
- set(m, true, mode);
35
- }
36
- function hide(focus) {
37
- var m = open;
38
- clearTimeout(timer);
39
- if (!m) return;
40
- set(m, false);
41
- if (focus === true) m.t.focus();
42
- }
43
- function inside(m, el) { return !!el && (m.item.contains(el) || m.p.contains(el)); }
44
- menus.forEach(function (m) {
45
- on(m.t, 'click', function () { if (open === m && m.mode !== 'hover') hide(); else show(m, 'click'); });
46
- (m.item.contains(m.p) ? [m.item] : [m.item, m.p]).forEach(function (el) {
47
- on(el, 'mouseenter', function () {
48
- if (fine.matches) { clearTimeout(timer); if (open !== m) timer = setTimeout(function () { show(m, 'hover'); }, 80); }
49
- });
50
- on(el, 'mouseleave', function () {
51
- if (fine.matches) { clearTimeout(timer); if (open === m && m.mode === 'hover') timer = setTimeout(hide, 160); }
52
- });
3
+ 'use strict';
4
+ if (window.gwWebsite) return;
5
+
6
+ var version = '0.5.0';
7
+ var api = { version: version, init: init, destroy: destroy };
8
+ var doc, root, mine, fine, sys, wide, live = [], megaOpen = null, megaTimer = 0, queued = false;
9
+ var READY = 'data-gw-ready';
10
+
11
+ function mq(q) { return window.matchMedia ? matchMedia(q) : { matches: false }; }
12
+ function watch(m, fn) { if (m.addEventListener) m.addEventListener('change', fn); }
13
+ function all(sel, ctx) { return [].slice.call((ctx || doc).querySelectorAll(sel)); }
14
+ function within(sel, ctx) { var l = all(sel, ctx); if (ctx.matches && ctx.matches(sel)) l.unshift(ctx); return l; }
15
+ function on(el, type, fn, opt) { el.addEventListener(type, fn, opt || false); }
16
+ function attr(el, name, dflt) { return el.getAttribute(name) || dflt; }
17
+ function esc(e) { return e.key === 'Escape'; }
18
+ function link(e) { return e.target.closest('a[href]'); }
19
+ function emit(el, type, detail) { if (window.CustomEvent) el.dispatchEvent(new CustomEvent(type, { bubbles: true, detail: detail })); }
20
+
21
+ function each(ctx, sel, kind, fn) { wire(within(sel, ctx), kind, fn); }
22
+ function wire(els, kind, fn) {
23
+ els.forEach(function (el) {
24
+ if (live.some(function (r) { return r.el === el && r.kind === kind; })) return;
25
+ var offs = [], r = fn(el, function (t, type, f, opt) {
26
+ on(t, type, f, opt);
27
+ offs.push(function () { t.removeEventListener(type, f, opt || false); });
28
+ });
29
+ if (!r) return offs.forEach(function (f) { f(); });
30
+ r.el = el; r.kind = kind; r.offs = offs;
31
+ mark(el, kind, true);
32
+ live.push(r);
33
+ });
34
+ }
35
+ function mark(el, kind, add) {
36
+ var l = attr(el, READY, '').split(' ').filter(function (k) { return k && k !== kind; });
37
+ if (add) l.push(kind);
38
+ if (l.length) el.setAttribute(READY, l.join(' ')); else el.removeAttribute(READY);
39
+ }
40
+ function drop(r) {
41
+ if (r.stop) r.stop();
42
+ r.offs.forEach(function (f) { f(); });
43
+ mark(r.el, r.kind);
44
+ live.splice(live.indexOf(r), 1);
45
+ }
46
+ function records(kind, fn) { live.slice().forEach(function (r) { if (r.kind === kind) fn(r); }); }
47
+
48
+ function setMega(m, show, mode) {
49
+ m.p.hidden = !show;
50
+ m.t.setAttribute('aria-expanded', String(show));
51
+ m.item.classList.toggle('is-open', show);
52
+ m.mode = mode;
53
+ megaOpen = show ? m : megaOpen === m ? null : megaOpen;
54
+ emit(m.t, 'gw:mega', { id: m.p.id, open: show });
55
+ }
56
+ function showMega(m, mode) {
57
+ clearTimeout(megaTimer);
58
+ if (megaOpen === m) { if (mode === 'click') m.mode = mode; return; }
59
+ if (megaOpen) setMega(megaOpen, false);
60
+ setMega(m, true, mode);
61
+ }
62
+ function hideMega(focus) {
63
+ var m = megaOpen;
64
+ clearTimeout(megaTimer);
65
+ if (!m) return;
66
+ setMega(m, false);
67
+ if (focus === true) m.t.focus();
68
+ }
69
+ function inside(m, el) { return !!el && (m.item.contains(el) || m.p.contains(el)); }
70
+ function mega(ctx) {
71
+ each(ctx, '[data-gw-mega]', 'mega', function (t, on) {
72
+ var p = doc.getElementById(attr(t, 'data-gw-mega', attr(t, 'aria-controls', '')));
73
+ if (!p) return;
74
+ var m = { t: t, p: p, item: t.closest('.gw-nav__item') || t.parentNode };
75
+ on(t, 'click', function () { if (megaOpen === m && m.mode !== 'hover') hideMega(); else showMega(m, 'click'); });
76
+ (m.item.contains(p) ? [m.item] : [m.item, p]).forEach(function (el) {
77
+ on(el, 'mouseenter', function () {
78
+ if (fine.matches) { clearTimeout(megaTimer); if (megaOpen !== m) megaTimer = setTimeout(function () { showMega(m, 'hover'); }, 80); }
79
+ });
80
+ on(el, 'mouseleave', function () {
81
+ if (fine.matches) { clearTimeout(megaTimer); if (megaOpen === m && m.mode === 'hover') megaTimer = setTimeout(hideMega, 160); }
53
82
  });
54
- on(m.item, 'focusout', function (e) { if (open === m && e.relatedTarget && !inside(m, e.relatedTarget)) hide(); });
55
- on(m.p, 'click', function (e) { if (link(e)) hide(); });
56
83
  });
57
- on(doc, 'click', function (e) { if (open && !inside(open, e.target)) hide(); });
58
- on(doc, 'keydown', function (e) { if (open && esc(e)) hide(inside(open, doc.activeElement) || open.mode === 'click'); });
59
- }
60
-
61
- // Mobile menu. Labels: data-gw-label-open/-close, else the markup's aria-label, else English.
62
- function mobile() {
63
- var btn = doc.querySelector('[data-gw-menu-toggle]');
64
- var menu = btn && doc.getElementById(attr(btn, 'aria-controls', 'gw-mobile-menu'));
84
+ on(m.item, 'focusout', function (e) { if (megaOpen === m && e.relatedTarget && !inside(m, e.relatedTarget)) hideMega(); });
85
+ on(p, 'click', function (e) { if (link(e)) hideMega(); });
86
+ m.stop = function () { if (megaOpen === m) hideMega(); };
87
+ return m;
88
+ });
89
+ }
90
+
91
+ function mobile(ctx) {
92
+ each(ctx, '[data-gw-menu-toggle]', 'menu', function (btn, on) {
93
+ var menu = doc.getElementById(attr(btn, 'aria-controls', 'gw-mobile-menu'));
65
94
  if (!menu) return;
66
95
  var label = [attr(btn, 'data-gw-label-open', attr(btn, 'aria-label', 'Open menu')), attr(btn, 'data-gw-label-close', 'Close menu')];
67
- var wide = mq('(min-width: 1080px)');
68
- function isOpen() { return !menu.hidden; }
69
- function set(o, focus) {
70
- menu.hidden = !o;
71
- btn.setAttribute('aria-expanded', String(o));
72
- btn.setAttribute('aria-label', label[+o]);
73
- root.classList.toggle('is-menu-open', o);
74
- if (focus) btn.focus();
75
- }
76
- on(btn, 'click', function () { set(!isOpen()); });
77
- on(doc, 'keydown', function (e) { if (esc(e) && isOpen()) set(false, true); });
78
- on(menu, 'click', function (e) { if (link(e)) set(false); }); // same-page anchors do not reload
79
- watch(wide, function () { if (wide.matches && isOpen()) set(false); });
80
- on(window, 'pageshow', function (e) { if (e.persisted && isOpen()) set(false); });
81
- }
82
-
83
- // Theme toggle. Labels: data-gw-label-dark/-light, else the markup's aria-label, else English.
84
- function theme() {
85
- var sys = mq('(prefers-color-scheme: dark)'), stored = null;
86
- try { stored = localStorage.getItem('gw-theme'); } catch (e) { /* blocked */ }
87
- if (!root.hasAttribute('data-theme') && /^(light|dark)$/.test(stored)) root.setAttribute('data-theme', stored);
88
- var btns = all('[data-gw-theme-toggle]'), labels = btns.map(function (b) {
89
- return [attr(b, 'data-gw-label-dark', attr(b, 'aria-label', 'Switch to dark theme')), attr(b, 'data-gw-label-light', 'Switch to light theme')];
96
+ var r = {
97
+ open: function () { return !menu.hidden; },
98
+ set: function (o, focus) {
99
+ menu.hidden = !o;
100
+ btn.setAttribute('aria-expanded', String(o));
101
+ btn.setAttribute('aria-label', label[+o]);
102
+ root.classList.toggle('is-menu-open', o);
103
+ if (focus) btn.focus();
104
+ },
105
+ stop: function () { if (r.open()) r.set(false); }
106
+ };
107
+ on(btn, 'click', function () { r.set(!r.open()); });
108
+ on(menu, 'click', function (e) { if (link(e)) r.set(false); });
109
+ return r;
110
+ });
111
+ }
112
+ function closeMenus(focus) { records('menu', function (r) { if (r.open()) r.set(false, focus); }); }
113
+
114
+ function dark() { var t = root.getAttribute('data-theme'); return t ? t === 'dark' : sys.matches; }
115
+ function relabel() { records('theme', function (r) { r.el.setAttribute('aria-label', r.labels[+dark()]); }); }
116
+ function theme(ctx) {
117
+ each(ctx, '[data-gw-theme-toggle]', 'theme', function (b, on) {
118
+ var r = { labels: [attr(b, 'data-gw-label-dark', attr(b, 'aria-label', 'Switch to dark theme')), attr(b, 'data-gw-label-light', 'Switch to light theme')] };
119
+ on(b, 'click', function () {
120
+ var next = dark() ? 'light' : 'dark';
121
+ root.setAttribute('data-theme', next);
122
+ try { localStorage.setItem('gw-theme', next); } catch (e) { /* blocked */ }
123
+ relabel();
124
+ emit(root, 'gw:theme', { theme: next });
90
125
  });
91
- function dark() { var t = root.getAttribute('data-theme'); return t ? t === 'dark' : sys.matches; }
92
- function relabel() { btns.forEach(function (b, i) { b.setAttribute('aria-label', labels[i][+dark()]); }); }
93
- btns.forEach(function (b) {
94
- on(b, 'click', function () {
95
- var next = dark() ? 'light' : 'dark';
96
- root.setAttribute('data-theme', next);
97
- try { localStorage.setItem('gw-theme', next); } catch (e) { /* blocked */ }
98
- relabel();
126
+ r.stop = function () { b.setAttribute('aria-label', r.labels[0]); };
127
+ return r;
128
+ });
129
+ relabel();
130
+ }
131
+
132
+ function reveal(ctx) {
133
+ var els = [], io = null;
134
+ each(ctx, '.gw-reveal', 'reveal', function (el) { els.push(el); return { stop: function () { if (io) io.unobserve(el); } }; });
135
+ function showAll() { els.forEach(function (el) { el.classList.add('is-in'); }); if (io) io.disconnect(); }
136
+ if (!els.length) return;
137
+ if (!window.IntersectionObserver || mq('(prefers-reduced-motion: reduce)').matches) return showAll();
138
+ io = new IntersectionObserver(function (list) {
139
+ list.forEach(function (en) { if (en.isIntersecting) { en.target.classList.add('is-in'); io.unobserve(en.target); } });
140
+ }, { threshold: 0.15 });
141
+ els.forEach(function (el) { io.observe(el); });
142
+ setTimeout(showAll, 2500);
143
+ }
144
+
145
+ function scrolled() {
146
+ var s = window.scrollY > 8;
147
+ queued = false;
148
+ records('header', function (r) { if (r.s !== s) r.el.classList.toggle('is-scrolled', (r.s = s)); });
149
+ }
150
+ function header(ctx) {
151
+ each(ctx, '[data-gw-header]', 'header', function (h) { return { s: false, stop: function () { h.classList.remove('is-scrolled'); } }; });
152
+ scrolled();
153
+ }
154
+
155
+ function current(ctx) {
156
+ var links = within('.gw-nav__link[href], .gw-mega__link[href]', ctx);
157
+ within('#gw-mobile-menu, .gw-mobile-menu', ctx).forEach(function (m) { links = links.concat(all('a[href]', m)); });
158
+ function norm(p) { return p.replace(/\/index\.html?$/, '/').replace(/\.html?$/, '').replace(/\/+$/, '') || '/'; }
159
+ links.forEach(function (a) { if (mine.has(a)) { mine.delete(a); a.removeAttribute('aria-current'); } });
160
+ if (links.some(function (a) { return a.hasAttribute('aria-current'); })) return;
161
+ links.forEach(function (a) {
162
+ if (a.classList.contains('gw-btn') || a.hash || a.host !== location.host) return;
163
+ if (norm(a.pathname) === norm(location.pathname)) { a.setAttribute('aria-current', 'page'); mine.add(a); }
164
+ });
165
+ }
166
+
167
+ function segmented(ctx) {
168
+ each(ctx, '[data-gw-segmented]', 'segmented', function (g, on) {
169
+ var name = attr(g, 'data-gw-segmented', ''), opts = all('[role="radio"]', g);
170
+ if (!opts.length) return;
171
+ function pick(o, focus, said) {
172
+ var v = attr(o, 'value', '');
173
+ opts.forEach(function (x) { x.setAttribute('aria-checked', String(x === o)); x.setAttribute('tabindex', x === o ? '0' : '-1'); });
174
+ if (focus) o.focus();
175
+ all('[data-gw-show]').forEach(function (el) {
176
+ var own = attr(el, 'data-gw-show', '').split(/\s+/).filter(function (t) { return t.split(':')[0] === name; });
177
+ if (own.length) el.hidden = own.indexOf(name + ':' + v) < 0;
99
178
  });
100
- });
101
- watch(sys, relabel);
102
- relabel();
103
- }
104
-
105
- // Reveal at 15% visible, once. Failsafe: all shown after 2.5s, or at once without IntersectionObserver.
106
- function reveal() {
107
- var els = all('.gw-reveal'), io = null;
108
- function showAll() { els.forEach(function (el) { el.classList.add('is-in'); }); if (io) io.disconnect(); }
109
- if (!els.length) return;
110
- if (!window.IntersectionObserver || mq('(prefers-reduced-motion: reduce)').matches) return showAll();
111
- io = new IntersectionObserver(function (list) {
112
- list.forEach(function (en) { if (en.isIntersecting) { en.target.classList.add('is-in'); io.unobserve(en.target); } });
113
- }, { threshold: 0.15 });
114
- els.forEach(function (el) { io.observe(el); });
115
- setTimeout(showAll, 2500);
116
- }
117
-
118
- // Header: .is-scrolled past 8px, read once per frame.
119
- function header() {
120
- var h = doc.querySelector('[data-gw-header]'), state = false, queued = false;
121
- if (!h) return;
122
- function update() {
123
- var s = window.scrollY > 8;
124
- queued = false;
125
- if (s !== state) h.classList.toggle('is-scrolled', (state = s));
179
+ if (said) emit(g, 'gw:segmented', { name: name, value: v });
126
180
  }
127
- on(window, 'scroll', function () { if (!queued) { queued = true; requestAnimationFrame(update); } }, { passive: true });
128
- update();
129
- }
130
-
131
- // aria-current="page" on nav links to this path, unless the markup marked one.
132
- function current() {
133
- var menu = doc.getElementById('gw-mobile-menu');
134
- var links = all('.gw-nav__link[href], .gw-mega__link[href]').concat(menu ? all('a[href]', menu) : []);
135
- function norm(p) { return p.replace(/\/index\.html?$/, '/').replace(/\.html?$/, '').replace(/\/+$/, '') || '/'; }
136
- if (links.some(function (a) { return a.hasAttribute('aria-current'); })) return;
137
- links.forEach(function (a) {
138
- if (a.classList.contains('gw-btn') || a.hash || a.host !== location.host) return;
139
- if (norm(a.pathname) === norm(location.pathname)) a.setAttribute('aria-current', 'page');
140
- });
141
- }
142
-
143
- // Segmented control: a radio group, one Tab stop; arrows (Home, End) move and choose. What one option shows carries
144
- // data-gw-show="group:value" and is hidden unless that option is chosen; without the script the markup's own choice shows.
145
- function segmented() {
146
- all('[data-gw-segmented]').forEach(function (g) {
147
- var name = attr(g, 'data-gw-segmented', ''), opts = all('[role="radio"]', g);
148
- if (!opts.length) return;
149
- function pick(o, focus, said) {
150
- var v = attr(o, 'value', '');
151
- opts.forEach(function (x) { x.setAttribute('aria-checked', String(x === o)); x.setAttribute('tabindex', x === o ? '0' : '-1'); });
152
- if (focus) o.focus();
153
- all('[data-gw-show]').forEach(function (el) {
154
- var mine = attr(el, 'data-gw-show', '').split(/\s+/).filter(function (t) { return t.split(':')[0] === name; });
155
- if (mine.length) el.hidden = mine.indexOf(name + ':' + v) < 0;
156
- });
157
- if (said && window.CustomEvent) g.dispatchEvent(new CustomEvent('gw:segmented', { bubbles: true, detail: { name: name, value: v } }));
158
- }
159
- opts.forEach(function (o, i) {
160
- on(o, 'click', function () { pick(o, false, true); });
161
- on(o, 'keydown', function (e) {
162
- var n = opts.length, k = { ArrowRight: i + 1, ArrowDown: i + 1, ArrowLeft: i - 1 + n, ArrowUp: i - 1 + n, Home: 0, End: n - 1 }[e.key];
163
- if (k === undefined || e.altKey || e.ctrlKey || e.metaKey) return;
164
- e.preventDefault();
165
- pick(opts[k % n], true, true);
166
- });
181
+ opts.forEach(function (o, i) {
182
+ on(o, 'click', function () { pick(o, false, true); });
183
+ on(o, 'keydown', function (e) {
184
+ var n = opts.length, k = { ArrowRight: i + 1, ArrowDown: i + 1, ArrowLeft: i - 1 + n, ArrowUp: i - 1 + n, Home: 0, End: n - 1 }[e.key];
185
+ if (k === undefined || e.altKey || e.ctrlKey || e.metaKey) return;
186
+ e.preventDefault();
187
+ pick(opts[k % n], true, true);
167
188
  });
168
- pick(opts.filter(function (o) { return attr(o, 'aria-checked', '') === 'true'; })[0] || opts[0]);
169
189
  });
170
- }
171
-
172
- // A busy button (aria-busy="true") ignores presses, by mouse or keyboard: no double submit. It keeps its focus and name.
173
- function busy() {
174
- on(doc, 'click', function (e) {
175
- var b = e.target.closest && e.target.closest('.gw-btn[aria-busy="true"]');
176
- if (b) { e.preventDefault(); e.stopPropagation(); }
177
- }, true);
178
- }
179
-
180
- function init() {
181
- [mega, mobile, theme, reveal, header, current, segmented, busy].forEach(function (f) {
182
- try { f(); } catch (e) { console.error('[gw-website]', e); }
183
- });
184
- }
185
- if (doc.readyState === 'loading') on(doc, 'DOMContentLoaded', init); else init();
190
+ pick(opts.filter(function (o) { return attr(o, 'aria-checked', '') === 'true'; })[0] || opts[0]);
191
+ return {};
192
+ });
193
+ }
194
+
195
+ function toc(ctx) {
196
+ each(ctx, '[data-gw-toc]', 'toc', function (nav, on) {
197
+ var box = nav.querySelector('details'), own = !!(box && box.open), cur = null, io = null;
198
+ var items = all('a[href]', nav).map(function (a) {
199
+ var id = attr(a, 'href', '');
200
+ if (id.charAt(0) !== '#') return null;
201
+ try { id = decodeURIComponent(id.slice(1)); } catch (e) { id = id.slice(1); }
202
+ return { a: a, h: doc.getElementById(id) };
203
+ }).filter(function (x) { return x && x.h; });
204
+ if (!items.length) return;
205
+ function update() {
206
+ var line = window.innerHeight * 0.3, top = -Infinity, next = null;
207
+ items.forEach(function (x) { var t = x.h.getBoundingClientRect().top; if (t <= line && t > top) { top = t; next = x; } });
208
+ if (next === cur) return;
209
+ if (cur) cur.a.removeAttribute('aria-current');
210
+ if (next) next.a.setAttribute('aria-current', 'true');
211
+ cur = next;
212
+ emit(nav, 'gw:toc', { id: next ? next.h.id : '' });
213
+ }
214
+ var r = {
215
+ fit: function () { if (box) box.open = wide.matches || own; },
216
+ stop: function () { if (io) io.disconnect(); if (cur) cur.a.removeAttribute('aria-current'); if (box) box.open = own; }
217
+ };
218
+ on(nav, 'click', function (e) { if (box && !wide.matches && link(e)) box.open = false; });
219
+ if (window.IntersectionObserver) {
220
+ io = new IntersectionObserver(update, { rootMargin: '100000px 0px -70% 0px' });
221
+ items.forEach(function (x) { io.observe(x.h); });
222
+ }
223
+ r.fit();
224
+ return r;
225
+ });
226
+ }
227
+
228
+ function words(el) { return el ? el.textContent.replace(/\s+/g, ' ').trim() : ''; }
229
+ function tables(ctx) {
230
+ var list = [];
231
+ within('.gw-prose', ctx).forEach(function (p) {
232
+ list = list.concat([].filter.call(p.children, function (t) { return t.tagName === 'TABLE'; }));
233
+ });
234
+ wire(list, 'table', function (t) {
235
+ var name = words(t.querySelector('caption')) || words(t.querySelector('th')) || words(t.querySelector('td'));
236
+ if (!name) return;
237
+ var box = doc.createElement('div'), had = t.classList.contains('is-wrapped');
238
+ box.className = 'gw-prose__table';
239
+ box.setAttribute('role', 'region');
240
+ box.setAttribute('tabindex', '0');
241
+ box.setAttribute('aria-label', name);
242
+ t.parentNode.insertBefore(box, t);
243
+ box.appendChild(t);
244
+ t.classList.add('is-wrapped');
245
+ return { stop: function () {
246
+ var p = box.parentNode;
247
+ if (p) { p.insertBefore(t, box); p.removeChild(box); }
248
+ if (!had) t.classList.remove('is-wrapped');
249
+ } };
250
+ });
251
+ }
252
+
253
+ function install() {
254
+ doc = document; root = doc.documentElement; mine = new WeakSet();
255
+ fine = mq('(hover: hover) and (pointer: fine)'); sys = mq('(prefers-color-scheme: dark)');
256
+ var stored = null;
257
+ wide = mq('(min-width: 1080px)');
258
+ on(doc, 'click', function (e) { if (megaOpen && !inside(megaOpen, e.target)) hideMega(); });
259
+ on(doc, 'keydown', function (e) { if (megaOpen && esc(e)) hideMega(inside(megaOpen, doc.activeElement) || megaOpen.mode === 'click'); });
260
+ on(doc, 'keydown', function (e) { if (esc(e)) closeMenus(true); });
261
+ watch(wide, function () { if (wide.matches) closeMenus(); records('toc', function (r) { r.fit(); }); });
262
+ on(window, 'pageshow', function (e) { if (e.persisted) closeMenus(); });
263
+ try { stored = localStorage.getItem('gw-theme'); } catch (e) { /* blocked */ }
264
+ if (!root.hasAttribute('data-theme') && /^(light|dark)$/.test(stored)) root.setAttribute('data-theme', stored);
265
+ watch(sys, function () { relabel(); if (!root.hasAttribute('data-theme')) emit(root, 'gw:theme', { theme: dark() ? 'dark' : 'light' }); });
266
+ on(window, 'scroll', function () { if (!queued) { queued = true; requestAnimationFrame(scrolled); } }, { passive: true });
267
+ on(doc, 'click', function (e) {
268
+ var b = e.target.closest && e.target.closest('.gw-btn[aria-busy="true"]');
269
+ if (b) { e.preventDefault(); e.stopPropagation(); }
270
+ }, true);
271
+ }
272
+
273
+ function run(fns, ctx) {
274
+ fns.forEach(function (f) { try { f(ctx); } catch (e) { console.error('[gw-website]', e); } });
275
+ }
276
+ function other() { var g = typeof window != 'undefined' && window.gwWebsite; return g && g !== api && g; }
277
+
278
+ /** Wires the kit's markup under root (default: the whole document). Safe to call again: wires only what is new. */
279
+ function init(ctx) {
280
+ if (typeof document == 'undefined') return;
281
+ var g = other();
282
+ if (g) return g.init && g.init(ctx);
283
+ window.gwWebsite = api;
284
+ if (!doc) run([install]);
285
+ live.slice().forEach(function (r) { if (r.el.isConnected === false) drop(r); });
286
+ run([mega, mobile, theme, reveal, header, current, segmented, toc, tables], ctx && ctx.querySelectorAll ? ctx : doc);
287
+ }
288
+
289
+ /** Takes back what init() wired under root (default: the whole document): listeners and data-gw-ready marks. */
290
+ function destroy(ctx) {
291
+ var g = other();
292
+ if (g) return g.destroy && g.destroy(ctx);
293
+ if (!doc) return;
294
+ ctx = ctx && ctx.querySelectorAll ? ctx : doc;
295
+ live.slice().forEach(function (r) { if (ctx === doc || ctx.contains(r.el)) drop(r); });
296
+ }
297
+
298
+ window.gwWebsite = api;
299
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { init(); }); else init();
186
300
  })();