{
  "version": 3,
  "sources": ["routes_offline.js"],
  "sourcesContent": ["/*\n * static/js/routes_offline.js \u2014 the user's own saved routes, kept readable\n * away from signal (SNOW-950).\n *\n * The map's routes panel lazy-loads its rows from ``routes:list`` over\n * HTMX, and ``sw.js`` classifies that endpoint network-only. Offline the\n * panel therefore opened onto a failure line \u2014 \"Your routes couldn't be\n * loaded\" \u2014 for routes the map was ALREADY drawing as lines beside it,\n * because SNOW-687 caches the routes GeoJSON in ``data:map_overlays``. The\n * lines were there and the user's own list was not, and the offline content\n * report answered Yes for the same surface the panel said no to.\n *\n * This is the \u00A712.6 relaxation SNOW-418 established for favourites and\n * SNOW-661 applied to field observations, applied to that list. A route is\n * a file the user uploaded, not a forecast that expires, so the cached rows\n * render as themselves with a line saying where they came from.\n *\n * Why the RENDERED BODY rather than a record per route\n * -----------------------------------------------------\n * Nothing is rebuilt from the overlay's GeoJSON, which holds geometry for\n * the line and not the row this list renders. Nor is a JSON record per\n * route the right shape: a row carries a server-translated name, distance\n * and ascent line, and rebuilding it would mean assembling a translated,\n * markup-bearing sentence in JavaScript \u2014 precisely the class of string\n * ``i18n-lint`` exists to catch. SNOW-879's ``panel_rows_cache.js`` already\n * holds this endpoint's exact response body, in memory, for one page load.\n * Persisting the same bytes keeps every translation and every measurement\n * verbatim, and leaves this module with no prose of its own: the one line\n * the user reads offline is ``routes.js``'s, from the template's strings\n * block.\n *\n * Two ways in\n * ------------\n * A body reaches ``write()`` either from a panel OPEN \u2014 ``htmx:beforeSwap``\n * on the container this module owns \u2014 or from the idle WARM ``routes.js``\n * fires at every map page load. Both are needed, and the second is the one\n * that matters: a warm is a plain fetch raising no htmx event, so on the\n * open path alone the rows would be stored only for a user who had opened\n * the sheet while online, and not for the user who loads the map with\n * signal and opens the panel in the backcountry. The warm reaches here\n * through ``panel_rows_cache.js``'s ``onWarmed()`` hook, registered below\n * for this key only.\n *\n * Storage only. This module writes and reads; ``routes.js`` decides when to\n * paint, because it owns everything else the user sees in that panel \u2014 and\n * it is ``routes.js`` that takes the online-only controls back off a cached\n * row.\n *\n * Row shape in ``data:panel_rows`` (``db.js`` schema v6):\n *\n *     { key: 'routes', body: '<the rendered rows>', cached_at, principal }\n *\n * ``principal`` partitions the row by the signed-in account, exactly as\n * SNOW-493 does for the account-specific overlays: these are the user's own\n * routes, and a row cached under one account must never repaint into\n * another's session on a shared browser. A row whose principal does not\n * match reads back as ``null``, and so does one written before principals\n * existed.\n *\n * No exports beyond ``window.pwaRoutesOffline``.\n */\n\n(function routesOfflineInit() {\n  'use strict';\n\n  const STORE = 'data:panel_rows';\n  const KEY = 'routes';\n\n  /**\n   * True when ``window.pwaDb`` is present and the app is not in the terminal\n   * Reset Required state. Every DB access below is guarded by this first,\n   * mirroring observations_offline.js and map_overlay_offline_cache.js.\n   *\n   * @returns {boolean}\n   */\n  function dbReady() {\n    return typeof window.pwaDb === 'object' && !window.pwaDb.isResetRequired();\n  }\n\n  /**\n   * The current principal \u2014 ``<meta name=\"pwa-user-id\">`` read through\n   * ``window.pwaDb.context().user_id``, with an empty string or a missing\n   * value normalised to ``null`` (anonymous). Mirrors\n   * ``map_overlay_offline_cache.js``'s ``_currentPrincipal``.\n   *\n   * @returns {string|null}\n   */\n  function currentPrincipal() {\n    try {\n      if (typeof window.pwaDb !== 'object') return null;\n      const userId = window.pwaDb.context().user_id;\n      return userId === undefined || userId === '' ? null : userId;\n    } catch (_e) {\n      return null;\n    }\n  }\n\n  /**\n   * Persist the panel's rendered rows.\n   *\n   * Best effort throughout: a failed write leaves the panel exactly as it\n   * behaved before this module existed, and must never break the swap it\n   * rides on.\n   *\n   * @param {string} body The response body, verbatim.\n   * @returns {Promise<void>}\n   */\n  async function write(body) {\n    if (!dbReady() || typeof body !== 'string') return;\n    try {\n      await window.pwaDb.put(STORE, {\n        key: KEY,\n        body: body,\n        cached_at: new Date().toISOString(),\n        principal: currentPrincipal(),\n      });\n    } catch (_e) {\n      // Non-fatal \u2014 the cache is best effort.\n    }\n  }\n\n  /**\n   * Read the cached rows back, or ``null`` when there is nothing to paint.\n   *\n   * Null covers four cases the caller cannot tell apart and does not need\n   * to \u2014 the DB is unavailable, nothing has been cached, the row belongs to\n   * a different account, or the read threw. All of them mean the same thing\n   * to the panel: draw the failure line.\n   *\n   * @returns {Promise<{body: string, cached_at: string}|null>}\n   */\n  async function read() {\n    if (!dbReady()) return null;\n    try {\n      const record = await window.pwaDb.get(STORE, KEY);\n      if (!record || typeof record.body !== 'string') return null;\n      // A row with no principal at all was written before this partitioning\n      // existed, and never matches \u2014 the same rule SNOW-493 applies to the\n      // account-specific overlay rows. The stored side is compared\n      // untouched, exactly as map_overlay_offline_cache.js does it:\n      // normalising an absent key to ``null`` would make such a row match an\n      // anonymous reader, whose own principal is null.\n      if (record?.principal !== currentPrincipal()) return null;\n      return record;\n    } catch (_e) {\n      return null;\n    }\n  }\n\n  // Delegated from document.body because htmx raises the lifecycle events\n  // for an ``htmx.ajax`` call with no source element against document.body \u2014\n  // the same bind point, for the same reason, as panel_rows_cache.js and\n  // each panel's own error handlers.\n  document.body?.addEventListener('htmx:beforeSwap', function (ev) {\n    const detail = ev.detail;\n    if (!detail || !detail.target) return;\n    // SNOW-722's ownership lesson: this listener sees EVERY swap on the\n    // page, so it writes only for the container it owns. Matching on the\n    // target rather than on the request path means the observations and\n    // favourites panels \u2014 whose list endpoints look much alike \u2014 cannot be\n    // mistaken for this one.\n    if (!detail.target.matches || !detail.target.matches('[data-routes-rows]')) {\n      return;\n    }\n    // An error response says nothing about the rows; caching it would serve\n    // the failure back as content on the next open.\n    if (detail.isError) return;\n    write(detail.serverResponse).catch(function () {\n      // Swallowed \u2014 see write().\n    });\n  });\n\n  // The other half of the write-through, and the one that serves the user\n  // this module was written for. ``routes.js`` warms the panel's rows at\n  // module init on every map page load, and that warm is a plain fetch\n  // raising no htmx event (``panel_rows_cache.js``'s header says why), so\n  // the listener above never sees it. Without this the rows would be\n  // persisted only by a panel OPEN while online \u2014 leaving the far commoner\n  // case, loading the map with signal and opening the panel later without\n  // it, on the failure line the ticket exists to remove.\n  //\n  // Naming the key here rather than in panel_rows_cache.js is SNOW-722's\n  // boundary: the shared cache serves three panels and must not know which\n  // of them keeps its rows.\n  window.pwaPanelRows?.onWarmed(KEY, write);\n\n  Object.defineProperty(window, 'pwaRoutesOffline', {\n    value: Object.freeze({ read: read, write: write }),\n    writable: false,\n    configurable: false,\n  });\n}());\n"],
  "mappings": "CA8DC,UAA6B,CAC5B,aAEA,MAAMA,EAAQ,kBACRC,EAAM,SASZ,SAASC,GAAU,CACjB,OAAO,OAAO,OAAO,OAAU,UAAY,CAAC,OAAO,MAAM,gBAAgB,CAC3E,CAUA,SAASC,GAAmB,CAC1B,GAAI,CACF,GAAI,OAAO,OAAO,OAAU,SAAU,OAAO,KAC7C,MAAMC,EAAS,OAAO,MAAM,QAAQ,EAAE,QACtC,OAAOA,IAAW,QAAaA,IAAW,GAAK,KAAOA,CACxD,MAAa,CACX,OAAO,IACT,CACF,CAYA,eAAeC,EAAMC,EAAM,CACzB,GAAI,GAACJ,EAAQ,GAAK,OAAOI,GAAS,UAClC,GAAI,CACF,MAAM,OAAO,MAAM,IAAIN,EAAO,CAC5B,IAAKC,EACL,KAAMK,EACN,UAAW,IAAI,KAAK,EAAE,YAAY,EAClC,UAAWH,EAAiB,CAC9B,CAAC,CACH,MAAa,CAEb,CACF,CAYA,eAAeI,GAAO,CACpB,GAAI,CAACL,EAAQ,EAAG,OAAO,KACvB,GAAI,CACF,MAAMM,EAAS,MAAM,OAAO,MAAM,IAAIR,EAAOC,CAAG,EAQhD,MAPI,CAACO,GAAU,OAAOA,EAAO,MAAS,UAOlCA,GAAQ,YAAcL,EAAiB,EAAU,KAC9CK,CACT,MAAa,CACX,OAAO,IACT,CACF,CAMA,SAAS,MAAM,iBAAiB,kBAAmB,SAAUC,EAAI,CAC/D,MAAMC,EAASD,EAAG,OACd,CAACC,GAAU,CAACA,EAAO,QAMnB,CAACA,EAAO,OAAO,SAAW,CAACA,EAAO,OAAO,QAAQ,oBAAoB,GAKrEA,EAAO,SACXL,EAAMK,EAAO,cAAc,EAAE,MAAM,UAAY,CAE/C,CAAC,CACH,CAAC,EAcD,OAAO,cAAc,SAAST,EAAKI,CAAK,EAExC,OAAO,eAAe,OAAQ,mBAAoB,CAChD,MAAO,OAAO,OAAO,CAAE,KAAME,EAAM,MAAOF,CAAM,CAAC,EACjD,SAAU,GACV,aAAc,EAChB,CAAC,CACH,GAAE",
  "names": ["STORE", "KEY", "dbReady", "currentPrincipal", "userId", "write", "body", "read", "record", "ev", "detail"]
}
