/*! trentpower.fr · authored source */
/*
  trentpower.fr
  service-worker registration

  Role:
  registers /sw.js for deterministic offline navigation, and owns the
  tp-app Trusted Types policy. the page CSP enforces
    require-trusted-types-for 'script'; trusted-types tp-app;
  so the one TrustedScriptURL sink on the whole site —
  navigator.serviceWorker.register — must route through a policy.

  Source:
  edited here as sw-register.template.js; compiled to sw-register.js
  by generate_site.py (minified, no substitution).

  Constraints:
  - no fetch, no inline handlers, no third-party scripts
  - enhancement only: offline support; the site works without it
*/

(function () {
  'use strict';

  // tp-app trusted types policy. the only script-URL sink on the site
  // is the service-worker registration below, so createScriptURL is a
  // single exact-match gate — no prefix allowlist, no sanitiser.
  function _safeScriptURL(s) {
    if (s !== '/sw.js') {
      throw new Error('tp-app: script URL not allowed: ' + s);
    }
    return s;
  }
  var ttPolicy = (typeof window !== 'undefined' && window.trustedTypes &&
                  typeof window.trustedTypes.createPolicy === 'function')
    ? window.trustedTypes.createPolicy('tp-app', { createScriptURL: _safeScriptURL })
    : null;
  function trustedScriptURL(value) {
    return ttPolicy ? ttPolicy.createScriptURL(value) : value;
  }

  // tp-sw-meta · device-local timestamps for the /local/ diagnostics
  // page. sw.js postMessages { type: 'tp-sw-installed' | 'tp-sw-activated', at }
  // after the precache settles and after clients.claim() runs; we
  // capture both into a single localStorage key so /local/ can show
  // when the offline cache was first installed and when the current
  // SW edition activated. lastCheckedAt is updated by /local/ itself
  // every time it queries the registration.
  function _recordSwMeta(field, when) {
    try {
      var raw = localStorage.getItem('tp-sw-meta');
      var meta = raw ? JSON.parse(raw) : {};
      meta[field] = new Date(when || Date.now()).toISOString();
      localStorage.setItem('tp-sw-meta', JSON.stringify(meta));
    } catch (_) { /* storage disabled — silent skip */ }
  }
  if ('serviceWorker' in navigator) {
    try {
      navigator.serviceWorker.addEventListener('message', function (ev) {
        if (!ev || !ev.data) return;
        if (ev.data.type === 'tp-sw-installed') _recordSwMeta('installedAt', ev.data.at);
        else if (ev.data.type === 'tp-sw-activated') _recordSwMeta('activatedAt', ev.data.at);
      });
    } catch (_) {}
  }

  // defer registration until after `load` so it stays off the critical
  // render path. sw.js calls skipWaiting() + clients.claim(), so a
  // deploy reaches return visitors on the next navigation without a
  // manual reg.update() nudge. all errors are silent in production;
  // `?debug-sw=1` opts into operator-side diagnostic logging.
  if ('serviceWorker' in navigator && location.protocol === 'https:') {
    var swDebug = location.search.indexOf('debug-sw=1') !== -1;
    window.addEventListener('load', function () {
      try {
        navigator.serviceWorker.register(trustedScriptURL('/sw.js'), { scope: '/' })
          .then(function (reg) {
            if (swDebug) {
              try { console.info('[tp] service worker registered', reg && reg.scope); } catch (_) {}
            }
          })
          .catch(function (err) {
            if (swDebug) {
              try { console.warn('[tp] service worker registration skipped', err); } catch (_) {}
            }
          });
      } catch (err) {
        if (swDebug) {
          try { console.warn('[tp] service worker registration unavailable', err); } catch (_) {}
        }
      }
    }, { once: true });
  }

})();
