{
  "version": 3,
  "sources": ["share.js"],
  "sourcesContent": ["/*\n * static/js/share.js \u2014 the one \"hand this to someone\" helper (SNOW-764).\n *\n * Publishes ``window.pwaShare`` with three functions:\n *\n *   shareOrCopy(value, title, kind)\n *                            open the native share sheet, or fall back to\n *                            the clipboard. ``kind`` is 'url' (default) or\n *                            'text', for a payload that contains a URL\n *                            rather than being one.\n *   createShare(url, csrf)   POST to a share-minting endpoint and resolve\n *                            with the link it answers.\n *   claim(url, csrf)         POST to a claim endpoint and resolve with the\n *                            HTML fragment it answers.\n *\n * WHY THIS FILE EXISTS. ``shareOrCopy`` was written inline in\n * ``public/bulletin.html`` for SNOW-217 and has been the only sharing\n * behaviour on the site since. SNOW-764 gives a route row a Share control\n * that wants exactly the same three branches \u2014 native sheet, user\n * cancelled, platform refused \u2014 and copying thirty lines of it into\n * ``routes.js`` would leave two implementations of a fiddly API to keep in\n * step. So the helper is lifted here verbatim, the bulletin page loads it,\n * and its inline copy is gone.\n *\n * THE THREE BRANCHES, and why none of them can be dropped:\n *\n *   * the platform can share \u2014 hand it the payload and stop. Anything\n *     further would be a second action the user did not ask for.\n *   * the user CANCELLED the sheet (``AbortError``) \u2014 do nothing. Copying\n *     to the clipboard here is the classic bug: they closed the sheet\n *     because they changed their mind, and a \"link copied\" toast tells\n *     them the thing they just declined happened anyway.\n *   * anything else rejected \u2014 fall back to the clipboard. Chrome on\n *     desktop exposes ``navigator.share`` and may reject when the platform\n *     cannot actually show a sheet (the page is not an installed PWA), so\n *     a share that silently no-ops is the failure this branch prevents.\n *\n * ``canShare`` is consulted where it exists, because a platform that\n * already knows it cannot take this payload should not be asked. Where it\n * does not exist the call is attempted and the rejection branch above\n * catches the refusal \u2014 feature detection, never user-agent sniffing.\n *\n * NO USER-FACING STRINGS. This module writes nothing to the DOM: the\n * caller owns its own toast and reads its own copy from a strings\n * <template> (docs/i18n.md). ``title`` is passed in for the same reason \u2014\n * it is the page's or the row's, not this module's.\n *\n * NO TELEMETRY EITHER. What is worth counting differs per surface (a\n * bulletin share and a route share are different events), so the caller\n * emits its own after the promise settles.\n *\n * Dependency-free and DOM-light by design, like i18n_strings.js: every\n * function takes plain values and the platform APIs are read off\n * ``window``/``navigator`` at call time rather than captured at parse\n * time, so a test can substitute either (tests/js/test_share.js).\n */\n\n(function () {\n  'use strict';\n\n  /**\n   * Copy a URL to the clipboard.\n   *\n   * Resolves either way. A clipboard write can be refused \u2014 an insecure\n   * origin, a permissions policy, a browser that has no Clipboard API at\n   * all \u2014 and the caller's next move is the same in every one of those\n   * cases: the share did not happen, and there is nothing further to try.\n   * The boolean says which, so a caller can decide whether to show its\n   * \"link copied\" toast.\n   *\n   * @param {string} url The URL to copy.\n   * @returns {Promise<boolean>} True when the text reached the clipboard.\n   */\n  function copyToClipboard(url) {\n    const nav = window.navigator;\n    if (!nav || !nav.clipboard || typeof nav.clipboard.writeText !== 'function') {\n      return Promise.resolve(false);\n    }\n    return nav.clipboard\n      .writeText(url)\n      .then(function () {\n        return true;\n      })\n      .catch(function () {\n        return false;\n      });\n  }\n\n  /**\n   * Hand a URL \u2014 or a line of text \u2014 to the share sheet, or the clipboard.\n   *\n   * See this file's header for why there are three branches and why the\n   * cancellation one must do nothing.\n   *\n   * ``kind`` decides ONE thing: which key the ``navigator.share`` payload\n   * carries. Everything else \u2014 ``canShare``, the four outcomes, the\n   * AbortError branch, the clipboard fallback \u2014 is identical, and the\n   * clipboard writes ``value`` either way. A second function would have\n   * duplicated that contract, which is the whole reason this module\n   * exists (SNOW-887).\n   *\n   * ``'text'`` is for a payload that CONTAINS a URL rather than being one:\n   * a favourite is shared as \"<name> - <link>\", and handing that to\n   * ``{url: \u2026}`` gets it rejected by ``canShare`` or mangled by the\n   * platform.\n   *\n   * @param {string} value The URL, or the text, to share.\n   * @param {string} [title] Sheet title. Defaults to the document's own.\n   * @param {string} [kind] ``'url'`` (default) or ``'text'``.\n   * @returns {Promise<string>} How it ended: ``'shared'`` (the platform\n   *   took it), ``'cancelled'`` (the user dismissed the sheet),\n   *   ``'copied'`` (the clipboard has it), or ``'failed'`` (neither\n   *   worked). The caller decides what, if anything, to say about each.\n   */\n  function shareOrCopy(value, title, kind) {\n    const nav = window.navigator;\n    const payload = { title: title || document.title };\n    if (kind === 'text') payload.text = value;\n    else payload.url = value;\n    const canShare =\n      nav &&\n      typeof nav.share === 'function' &&\n      (typeof nav.canShare !== 'function' || nav.canShare(payload));\n\n    if (!canShare) {\n      return copyToClipboard(value).then(function (copied) {\n        return copied ? 'copied' : 'failed';\n      });\n    }\n\n    return nav\n      .share(payload)\n      .then(function () {\n        return 'shared';\n      })\n      .catch(function (err) {\n        // The user closed the sheet. They declined; do not then do the\n        // thing they declined by another route.\n        if (err && err.name === 'AbortError') return 'cancelled';\n        return copyToClipboard(value).then(function (copied) {\n          return copied ? 'copied' : 'failed';\n        });\n      });\n  }\n\n  /**\n   * POST to a share-minting endpoint and resolve with the link.\n   *\n   * Both of this project's minting endpoints answer the same JSON shape \u2014\n   * ``{\"url\": \"\u2026\"}`` from ``apps.public.api.share_create`` and from\n   * ``apps.routes.views.route_share_create`` \u2014 which is what lets one\n   * helper serve both.\n   *\n   * Rejects rather than resolving with null on a non-2xx: the caller has\n   * to say something different for a failure than for a success, and a\n   * null that flows on into ``shareOrCopy`` would open a share sheet for\n   * the string \"null\".\n   *\n   * @param {string} url The minting endpoint.\n   * @param {string} csrfToken The CSRF token for the POST.\n   * @returns {Promise<string>} The absolute share URL.\n   */\n  function createShare(url, csrfToken) {\n    return fetch(url, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'X-CSRFToken': csrfToken,\n      },\n    })\n      .then(function (resp) {\n        if (!resp.ok) return Promise.reject(resp);\n        return resp.json();\n      })\n      .then(function (data) {\n        if (!data || !data.url) return Promise.reject(data);\n        return data.url;\n      });\n  }\n\n  /**\n   * POST to a claim endpoint and resolve with the fragment it answers.\n   *\n   * ``HX-Request`` is sent by hand because the claim endpoint is\n   * ``@require_htmx`` and this is a plain fetch, exactly as\n   * ``routes.js``'s upload does for ``routes:create``. CSRF rides in the\n   * header plus the same-origin session cookie (fetch defaults to\n   * ``credentials: 'same-origin'``).\n   *\n   * Rejects with the RESPONSE on a non-2xx rather than with an error, so\n   * the caller can branch on ``status`` \u2014 a 409 at the route cap needs a\n   * different line from a 404 on a link that has expired.\n   *\n   * @param {string} url The claim endpoint.\n   * @param {string} csrfToken The CSRF token for the POST.\n   * @returns {Promise<string>} The rendered row's HTML.\n   */\n  function claim(url, csrfToken) {\n    return fetch(url, {\n      method: 'POST',\n      headers: {\n        'HX-Request': 'true',\n        'X-CSRFToken': csrfToken,\n      },\n    }).then(function (resp) {\n      if (!resp.ok) return Promise.reject(resp);\n      return resp.text();\n    });\n  }\n\n  // Frozen, like every other window.pwa* bridge in this tree: the surface\n  // is a contract other modules read, not a namespace they extend.\n  window.pwaShare = Object.freeze({\n    shareOrCopy: shareOrCopy,\n    createShare: createShare,\n    claim: claim,\n  });\n}());\n"],
  "mappings": "CAyDC,UAAY,CACX,aAeA,SAASA,EAAgBC,EAAK,CAC5B,MAAMC,EAAM,OAAO,UACnB,MAAI,CAACA,GAAO,CAACA,EAAI,WAAa,OAAOA,EAAI,UAAU,WAAc,WACxD,QAAQ,QAAQ,EAAK,EAEvBA,EAAI,UACR,UAAUD,CAAG,EACb,KAAK,UAAY,CAChB,MAAO,EACT,CAAC,EACA,MAAM,UAAY,CACjB,MAAO,EACT,CAAC,CACL,CA4BA,SAASE,EAAYC,EAAOC,EAAOC,EAAM,CACvC,MAAMJ,EAAM,OAAO,UACbK,EAAU,CAAE,MAAOF,GAAS,SAAS,KAAM,EAQjD,OAPIC,IAAS,OAAQC,EAAQ,KAAOH,EAC/BG,EAAQ,IAAMH,EAEjBF,GACA,OAAOA,EAAI,OAAU,aACpB,OAAOA,EAAI,UAAa,YAAcA,EAAI,SAASK,CAAO,GAQtDL,EACJ,MAAMK,CAAO,EACb,KAAK,UAAY,CAChB,MAAO,QACT,CAAC,EACA,MAAM,SAAUC,EAAK,CAGpB,OAAIA,GAAOA,EAAI,OAAS,aAAqB,YACtCR,EAAgBI,CAAK,EAAE,KAAK,SAAUK,EAAQ,CACnD,OAAOA,EAAS,SAAW,QAC7B,CAAC,CACH,CAAC,EAjBMT,EAAgBI,CAAK,EAAE,KAAK,SAAUK,EAAQ,CACnD,OAAOA,EAAS,SAAW,QAC7B,CAAC,CAgBL,CAmBA,SAASC,EAAYT,EAAKU,EAAW,CACnC,OAAO,MAAMV,EAAK,CAChB,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,cAAeU,CACjB,CACF,CAAC,EACE,KAAK,SAAUC,EAAM,CACpB,OAAKA,EAAK,GACHA,EAAK,KAAK,EADI,QAAQ,OAAOA,CAAI,CAE1C,CAAC,EACA,KAAK,SAAUC,EAAM,CACpB,MAAI,CAACA,GAAQ,CAACA,EAAK,IAAY,QAAQ,OAAOA,CAAI,EAC3CA,EAAK,GACd,CAAC,CACL,CAmBA,SAASC,EAAMb,EAAKU,EAAW,CAC7B,OAAO,MAAMV,EAAK,CAChB,OAAQ,OACR,QAAS,CACP,aAAc,OACd,cAAeU,CACjB,CACF,CAAC,EAAE,KAAK,SAAUC,EAAM,CACtB,OAAKA,EAAK,GACHA,EAAK,KAAK,EADI,QAAQ,OAAOA,CAAI,CAE1C,CAAC,CACH,CAIA,OAAO,SAAW,OAAO,OAAO,CAC9B,YAAaT,EACb,YAAaO,EACb,MAAOI,CACT,CAAC,CACH,GAAE",
  "names": ["copyToClipboard", "url", "nav", "shareOrCopy", "value", "title", "kind", "payload", "err", "copied", "createShare", "csrfToken", "resp", "data", "claim"]
}
