PageSourceSearch

https://slateworks.ai/js/page_overlay.js?v=979ea9dc

js slateworks.ai collected 2026-10-05 15:05:11 UTC 20,474 bytes, 498 lines download raw bytes

1/**
2 * page_overlay.js — client-side navigation over the live sky.
3 *
4 * The content pages (/what-we-do, /pricing, /privacy, /eula, /for/*)
5 * exist as real standalone documents — ad landings, app-store legal
6 * links, and crawlers get fast static pages with the CSS sky. But when
7 * a visitor navigates to one FROM the homepage, a full navigation
8 * would tear down the WebGL renderer and reboot the whole simulation.
9 * Instead, this module intercepts the click, fetches the target page,
10 * and injects its nav + main + footer into #page-layer, which scrolls
11 * over the still-running sky. history.pushState keeps the URL real, so
12 * deep links, reloads, and the back button all behave: a reload of
13 * /pricing simply serves the static page.
14 *
15 * The injected markup is styled by pages.css, whose rules are all
16 * scoped under .page-shell — #page-layer carries that class, so the
17 * content styles apply inside the layer and nothing leaks into the
18 * hero. The standalone-only ground (static sky, grain) hangs off
19 * body.page-shell and never comes along.
20 */
21(function () {
22  'use strict';
23
24  // Paths eligible for overlay navigation. Anything else (mailto:,
25  // external, /delete-account, unknown) falls through to a normal
26  // navigation.
27  var ROUTE_RE = /^\/(what-we-do|pricing|privacy|eula|contact|thanks|status|for\/[a-z0-9-]+)$/;
28
29  // Fragments worth transplanting from a fetched document, in render
30  // order. The skip-link is deliberately left behind — the layer keeps
31  // its own focus management.
32  var PARTS = ['nav.site-nav', 'main.page', 'footer.site-footer'];
33
34  var HOME_TITLE = document.title;
35  // This router runs on the homepage AND on the content pages (which
36  // boot the full homepage simulation stack). Only the homepage has a
37  // bare-sky state to return to. On a content page, the first click to
38  // "/" INSTALLS the homepage into this document — hero markup fetched
39  // from "/", style.css attached — so the running sky carries on
40  // instead of rebooting through a navigation. After that the page
41  // behaves exactly like the homepage.
42  var IS_HOME = !!document.querySelector('.hero-content');
43  var installing = false;
44  var layer = null;
45  var cache = {};   // path -> { title, html }
46  var isOpen = false;
47  var loadGeneration = 0; // invalidates slower responses from older routes
48
49  function ensureLayer() {
50    if (!layer) layer = document.getElementById('page-layer');
51    return layer;
52  }
53
54  function currentPath() {
55    var p = window.location.pathname.replace(/\/+$/, '');
56    return p === '' ? '/' : p;
57  }
58
59  function render(path, entry) {
60    var l = ensureLayer();
61    if (!l) { window.location.href = path; return; }
62    l.innerHTML = entry.html;
63    ensureTurnstile(l);
64    ensureStatus(l);
65    document.title = entry.title;
66    l.hidden = false;
67    l.scrollTop = 0;
68    document.body.classList.add('page-open');
69    window._pageOverlayOpen = true;   // divider_halo.js zeroes the shader halo
70    isOpen = true;
71    // Fade the incoming page in on EVERY route change, not just the
72    // first open. (When the layer carried a 30% scrim this re-fade
73    // blanked the whole viewport and read like a reload; scrimless,
74    // it's just the content fading in over the visible sky.)
75    l.style.animation = 'none';
76    void l.offsetWidth;
77    l.style.animation = '';
78    // Hand focus to the layer so keyboard/screen-reader users land on
79    // the new content, mirroring a real navigation.
80    l.focus({ preventScroll: true });
81  }
82
83  function close(restoreTitle) {
84    loadGeneration++;
85    if (!isOpen) return;
86    var l = ensureLayer();
87    if (l) { l.hidden = true; l.innerHTML = ''; }
88    if (window._statusPage) window._statusPage.unmount();
89    document.body.classList.remove('page-open');
90    window._pageOverlayOpen = false;
91    if (restoreTitle !== false) document.title = HOME_TITLE;
92    isOpen = false;
93  }
94
95  function extract(doc) {
96    var html = '';
97    for (var i = 0; i < PARTS.length; i++) {
98      var el = doc.querySelector(PARTS[i]);
99      if (el) html += el.outerHTML;
100    }
101    return { title: doc.title || HOME_TITLE, html: html };
102  }
103
104  function load(path, push) {
105    var generation = ++loadGeneration;
106    if (push) history.pushState({ slwOverlay: path }, '', path);
107    if (cache[path]) { render(path, cache[path]); return; }
108
109    // Extensionless first (Cloudflare Pages serves /pricing); fall back
110    // to the .html file so the router also works on dumb static servers
111    // (local python -m http.server previews).
112    fetch(path, { credentials: 'same-origin' })
113      .then(function (res) {
114        if (res.ok) return res.text();
115        return fetch(path + '.html', { credentials: 'same-origin' })
116          .then(function (res2) {
117            if (!res2.ok) throw new Error('fetch failed: ' + path);
118            return res2.text();
119          });
120      })
121      .then(function (text) {
122        // A later navigation won the race. Do not let this older response
123        // replace its markup/title or turn a Back action into a forward jump.
124        if (generation !== loadGeneration || currentPath() !== path) return;
125        var doc = new DOMParser().parseFromString(text, 'text/html');
126        var entry = extract(doc);
127        if (!entry.html) throw new Error('no content: ' + path);
128        cache[path] = entry;
129        render(path, entry);
130      })
131      .catch(function () {
132        if (generation !== loadGeneration || currentPath() !== path) return;
133        // Anything went wrong: do the navigation for real.
134        window.location.href = path;
135      });
136  }
137
138  // ==========================================================================
139  // HOME INSTALLATION (content pages only)
140  // ==========================================================================
141
142  // The deploy stamps every script URL with ?v=<sha>. Dynamic loads
143  // must carry the same stamp or they'd fetch unversioned (cacheable-
144  // stale) copies and mix builds.
145  function assetVersion() {
146    var s = document.querySelector('script[src*="page_overlay.js"]');
147    var m = s && s.getAttribute('src').match(/\?v=[^&]*/);
148    return m ? m[0] : '';
149  }
150
151  // Replica of index.html's head handshake: the shingled wordmark stays
152  // hidden until Yellowtail is loaded AND the boot brand state is
153  // settled (html.wm-ready). Content pages don't carry the inline
154  // original; by install time secret.js (loaded at page boot) has
155  // already settled the brand, so bootOk starts true. 3s backstop.
156  function setupWordmarkHandshake() {
157    var fontOk = false, bootOk = !!window._secretBooted, done = false;
158    function maybe() {
159      if (!done && fontOk && bootOk) {
160        done = true;
161        document.documentElement.classList.add('wm-ready');
162      }
163    }
164    function fontReady() { fontOk = true; maybe(); }
165    window._wmBootReady = function () { bootOk = true; maybe(); };
166    if (document.fonts && document.fonts.load) {
167      document.fonts.load('1em Yellowtail').then(function (faces) {
168        if (faces.length) fontReady(); else document.fonts.ready.then(fontReady);
169      }).catch(fontReady);
170      document.fonts.ready.then(fontReady);
171    } else {
172      fontOk = true;
173    }
174    setTimeout(function () { fontOk = true; bootOk = true; maybe(); }, 3000);
175  }
176
177  function installHome() {
178    installing = true;
179    return fetch('/', { credentials: 'same-origin' })
180      .then(function (res) {
181        if (!res.ok) throw new Error('home fetch failed');
182        return res.text();
183      })
184      .then(function (text) {
185        var doc = new DOMParser().parseFromString(text, 'text/html');
186        var hero = doc.querySelector('main.hero');
187        var foot = doc.querySelector('body > footer.site-footer');
188        if (!hero || !foot) throw new Error('home extract failed');
189
190        // The sky-page stylesheet. Its viewport pinning (fixed body,
191        // overflow hidden) is exactly what the home view needs; the
192        // overlay keeps scrolling internally.
193        var link = document.createElement('link');
194        link.rel = 'stylesheet';
195        link.href = '/style.css' + assetVersion();
196        document.head.appendChild(link);
197
198        // Retire this page's static article chrome permanently —
199        // REMOVED, not hidden, so module boot querySelectors (logo_fade
200        // grabs the first .site-footer) resolve to the home elements.
201        // All further navigation happens in #page-layer.
202        var l = ensureLayer();
203        var chrome = document.querySelectorAll(
204          'body >
204 .skip-link, body > .site-nav, body > main, body > .site-footer');
205        for (var i = 0; i < chrome.length; i++) {
206          chrome[i].parentNode.removeChild(chrome[i]);
207        }
208        // Drop the content-page body styling (static sky ground, page
209        // grain, the sky-live boot cover) — style.css owns body now.
210        document.body.classList.remove('page-shell');
211
212        document.body.insertBefore(document.importNode(hero, true), l);
213        document.body.insertBefore(document.importNode(foot, true), l);
214        HOME_TITLE = doc.title || HOME_TITLE;
215        setupWordmarkHandshake();
216
217        // Pinned viewport needs the homepage's iOS touch blocker.
218        document.addEventListener('touchmove', function (e) {
219          if (!e.target.closest ||
220              !(e.target.closest('#secret-panel') || e.target.closest('#page-layer'))) {
221            e.preventDefault();
222          }
223        }, { passive: false });
224
225        // Every module is already running (content pages boot the full
226        // homepage stack) — logo_fade just needs to re-resolve its
227        // managed elements, since the footer it grabbed at boot was
228        // removed above and the hero didn't exist yet. divider_halo
229        // re-queries per frame on its own; secret.js's footer knock is
230        // document-delegated.
231        if (window._logoFade && window._logoFade.rescan) {
232          window._logoFade.rescan();
233        }
234
235        IS_HOME = true;
236        installing = false;
237      });
238  }
239
240  function goHome() {
241    if (installing) return;
242    history.pushState({}, '', '/');
243    installHome()
244      .then(function () {
245        if (isOpen) close();
246        else document.title = HOME_TITLE;
247      })
248      .catch(function () {
249        // Install failed — fall back to the real navigation (the URL
250        // is already "/", so this is a plain load of the homepage).
251        window.location.href = '/';
252      });
253  }
254
255  document.addEventListener('click', function (e) {
256    if (e.defaultPrevented || e.button !== 0 ||
257        e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
258    if (!e.target.closest) return;
259    var a = e.target.closest('a[href]');
260    if (!a || a.target || a.hasAttribute('download')) return;
261    if (a.origin !== window.location.origin) return;
262
263    var path = a.pathname.replace(/\/+$/, '');
264    if (path === '' || path === '/index.html') path = '/';
265
266    // Brand link / home: drop back to the bare sky. On a content page
267    // the first trip installs the homepage into this document so the
268    // sky carries on (see installHome).
269    if (path === '/') {
270      if (!IS_HOME) {
271        e.preventDefault();
272        goHome();
273        return;
274      }
275      if (!isOpen) return;
276      e.preventDefault();
277      history.pushState({}, '', '/');
278      close();
279      return;
280    }
281
282    if (!ROUTE_RE.test(path)) return;
283    e.preventDefault();
284
285    // Same-page fragment link inside the open overlay: just scroll.
286    if (isOpen && path === currentPath() && a.hash) {
287      var target = ensureLayer().querySelector(a.hash);
288      if (target && target.scrollIntoView) target.scrollIntoView();
289      return;
290    }
291
292    load(path, true);
293  });
294
295  window.addEventListener('popstate', function () {
296    var path = currentPath();
297    if (ROUTE_RE.test(path)) load(path, false);
298    else close();
299  });
300
301  // Turnstile for injected forms: api.js auto-renders widgets present
302  // at ITS load time, but a .cf-turnstile div arriving later via
303  // innerHTML needs an explicit turnstile.render(). Tokens are
304  // single-use, so failed submits reset the widget for a retry.
305  var turnstileApi = null;
306  function ensureTurnstile(root) {
307    var els = root.querySelectorAll('.cf-turnstile:empty');
308    if (!els.length) return;
309    if (!turnstileApi) {
310      turnstileApi = new Promise(function (resolve, reject) {
311        if (window.turnstile) { resolve(); return; }
312        var s = document.createElement('script');
313        s.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
314        s.onload = resolve;
315        s.onerror = reject;
316        document.head.appendChild(s);
317      });
318    }
319    turnstileApi.then(function () {
320      for (var i = 0; i < els.length; i++) {
321        if (!els[i].hasChildNodes()) {
322          // Keep the ID so retries reset this form's widget, not whichever
323          // Turnstile instance happens to be first in the document.
324          els[i]._turnstileWidgetId = window.turnstile.render(els[i]);
325        }
326      }
327    }).catch(function () { /* widget absent; the server will refuse */ });
328  }
329
330  // /status renders from /api/status via js/status_page.js — a module
331  // rather than an inline script, because inline scripts in injected
332  // HTML never run. status.html loads it statically; every other page
333  // fetches it here on first use (deploy-stamped, like the home
334  // install's dynamic loads). mount() on a root without status markup
335  // is a no-op that also stops any previous route's poll loop.
336  var statusApi = null;
337  function ensureStatus(root) {
338    if (window._statusPage) { window._statusPage.mount(root); return; }
339    if (!root.querySelector('#status-components')) return;
340    if (!statusApi) {
341      statusApi = new Promise(function (resolve, reject) {
342        var s = document.createElement('script');
343        s.src = '/js/status_page.js' + assetVersion();
344        s.onload = resolve;
345        s.onerror = reject;
346        document.head.appendChild(s);
347      });
348    }
349    statusApi.then(function () {
350      // Still showing the same content? (A fast second click could
351      // have moved on; that render's own mount call wins.)
352      if (window._statusPage && root.querySelector('#status-components')) {
353        window._statusPage.mount(root);
354      }
355    }).catch(function () { /* headline stays "Checking…"; reload recovers */ });
356  }
357
358  // Phone mask: (xxx) xxx-xxxx as you type, 10 digits max. Delegated so
359  // it works on the static page and injected into the layer alike.
360  document.addEventListener('input', function (e) {
361    var t = e.target;
362    if (!t || t.id !== 'cf-phone') return;
363    var d = t.value.replace(/\D/g, '').slice(0, 10);
364    if (d.length > 6) t.value = '(' + d.slice(0, 3) + ') ' + d.slice(3, 6) + '-' + d.slice(6);
365    else if (d.length > 3) t.value = '(' + d.slice(0, 3) + ') ' + d.slice(3);
366    else if (d.length > 0) t.value = '(' + d;
367    else t.value = '';
368  });
369
370  // Marketing tags: a link posted somewhere carries utm_source /
371  // utm_medium / utm_campaign. Remember them for the session so a visitor
372  // who lands on the homepage and finds the contact page later still
373  // arrives labelled with the post that brought them.
374  var UTM_KEYS = ['utm_source', 'utm_medium', 'utm_campaign'];
375  var UTM_STORE = 'sw_utm';
376  function rememberUtm() {
377    try {
378      var params = new URLSearchParams(location.search);
379      var found = {};
380      var any = false;
381      UTM_KEYS.forEach(function (key) {
382        var value = (params.get(key) || '').trim().slice(0, 100);
383        if (value) { found[key] = value; any = true; }
384      });
385      if (any) {
386        sessionStorage.setItem(UTM_STORE, JSON.stringify(found));
387        reportArrival(found);
388      }
389    } catch (e) { /* storage unavailable: the tags just don't carry */ }
390  }
391  // Tell the pipeline a tagged link brought someone: once per set of tags
392  // per session, the tags and the page, nothing about the person.
393  function reportArrival(found) {
394    try {
395      var stamp = JSON.stringify(found);
396      if (sessionStorage.getItem('sw_utm_reported') === stamp) return;
397      sessionStorage.setItem('sw_utm_reported', stamp);
398    } catch (e) { /* fine: at worst it is reported twice */ }
399    var body = JSON.stringify({
400      utm_source: found.utm_source || '',
401      utm_medium: found.utm_medium || '',
402      utm_campaign: found.utm_campaign || '',
403      page: location.pathname
404    });
405    try {
406      if (navigator.sendBeacon) {
407        navigator.sendBeacon('/api/arrival', new Blob([body], { type: 'application/json' }));
408      } else {
409        fetch('/api/arrival', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: body, keepalive: true }).catch(function () {});
410      }
411    } catch (e) { /* fine */ }
412  }
413  function storedUtm() {
414    try { return JSON.parse(sessionStorage.getItem(UTM_STORE) || '{}') || {}; }
415    catch (e) { return {}; }
416  }
417  rememberUtm();
418
419  // "Other" under Trade, license jurisdiction, and referral source reveals a
420  // text box. Scoped to the select's own form: when the contact page is
421  // opened over a directly loaded one, both documents' forms are present.
422  var OTHER_WRAPS = { 'cf-trade': 'cf-trade-other-wrap', 'cf-source': 'cf-source-other-wrap', 'cf-license-state': 'cf-license-state-other-wrap' };
423  document.addEventListener('change', function (e) {
424    var t = e.target;
425    if (!t || !OTHER_WRAPS[t.id]) return;
426    var wrap = t.form && t.form.querySelector('[id="' + OTHER_WRAPS[t.id] + '"]');
427    if (wrap) wrap.hidden = t.value !== 'Other';
428  });
429
430  // Contact form: document-level delegation so it works both on the
431  // static /contact page and when that page is injected into the layer
432  // (inline scripts don't execute on injected HTML).
433  document.addEventListener('submit', function (e) {
434    var form = e.target;
435    if (!form || form.id !== 'contact-form') return;
436    e.preventDefault();
437
438    var button = form.querySelector('button[type="submit"]');
439    var errorNote = form.querySelector('.form-error');
440    var done = form.parentNode.querySelector('.contact-done');
441    var data = {};
442    var fd = new FormData(form);
443    fd.forEach(function (v, k) { data[k] = v; });
444    var utm = storedUtm();
445    UTM_KEYS.forEach(function (key) { if (!data[key] && utm[key]) data[key] = utm[key]; });
446
447    if (button) { button.disabled = true; button.textContent = 'Sending…'; }
448    if (errorNote) errorNote.hidden = true;
449
450    fetch('/api/contact', {
451      method: 'POST',
452      headers: { 'Content-Type': 'application/json' },
453      credentials: 'same-origin',
454      body: JSON.stringify(data)
455    })
456      .then(function (res) { return res.json(); })
457      .then(function (out) {
458        if (!out.ok) throw new Error(out.error || 'failed');
459        // A page of its own, not a message in place: it reads as done, a
460        // second click cannot resend, and /thanks is the address ad and
461        // analytics tools count as a conversion. Routed like any other
462        // page so the sky carries on underneath.
463        form.hidden = true;
464        if (done) done.hidden = false;
465        load('/thanks', true);
466      })
467      .catch(function (err) {
468        if (button) { button.disabled = false; button.textContent = 'Send it'; }
469        if (errorNote) {
470          errorNote.textContent = (err && err.message && err.message !== 'failed')
471            ? err.message
472            : 'That didn’t go through — give it another try, or come back in a minute.';
473          errorNote.hidden = false;
474        }
475        // Turnstile tokens are single-use; arm a fresh one for retry.
476        if (window.turnstile && window.turnstile.reset) {
477          try {
478            var widget = form.querySelector('.cf-turnstile');
479            var widgetId = widget && widget._turnstileWidgetId;
480            // Direct page loads are auto-rendered and do not expose an ID;
481            // there is only one protected surface, so the fallback is safe.
482            if (widgetId != null) window.turnstile.reset(widgetId);
483            else window.turnstile.reset();
484          } catch (e2) { /* fine */ }
485        }
486      });
487  });
488
489  // Esc returns to the sky (unless the secret panel is up — its own
490  // shortcuts own the keyboard while open).
491  document.addEventListener('keydown', function (e) {
492    if (e.key !== 'Escape' || !isOpen || !IS_HOME) return;
493    var sp = document.getElementById('secret-panel');
494    if (sp && !sp.classList.contains('hidden')) return;
495    history.pushState({}, '', '/');
496    close();
497  });
498})();

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.