{
  "version": 3,
  "sources": ["place_picker.js"],
  "sourcesContent": ["/*\n * static/js/place_picker.js \u2014 SNOW-475 touch-friendly place-picker.\n *\n * Replaces the draggable-marker pattern favourites.js and report.js each\n * used independently (a maplibregl.Marker({draggable: true}) whose dragend\n * wrote the chosen coord) \u2014 on touch screens the dragged finger occludes\n * the pin it is supposed to be placing, making fine positioning unreliable.\n *\n * Instead, a single pin (#map-place-pin, added in\n * public/partials/_map_embed.html) is held at a fixed point on screen via\n * CSS (static/css/map.css). The user pans the map underneath it; the chosen\n * coordinate is read live from the map on every 'moveend', so the pin itself\n * never moves relative to the screen \u2014 only the map does.\n *\n * Pin lift (SNOW-538)\n * -------------------\n * That fixed point is the centre of the map area the calling sheet does NOT\n * cover, which is not the same as the centre of the map. Both sheets dock to\n * the bottom of the viewport and size themselves to their content, and the\n * report form (a location line, five problem buttons and Cancel) is tall\n * enough on a phone to reach past the map's vertical midpoint \u2014 so a pin\n * pinned at 50% sat behind the sheet and the user could not see the point\n * they were placing. Callers pass the sheet as ``occludedBy``; whenever it\n * covers where the pin would otherwise sit, the pin is lifted to the middle\n * of the strip of map still visible above it (``--map-place-pin-lift``,\n * read by .map-place-pin in static/css/map.css).\n *\n * The lift is mirrored into MapLibre's own ``padding`` (bottom = 2 \u00D7 lift),\n * which moves the transform's centre point to exactly where the pin now is.\n * That keeps MAP.getCenter() and MAP.setCenter() meaning \"the point under\n * the pin\" throughout, so the coordinate read and the recenterTo write both\n * stay one-liners \u2014 no screen-space arithmetic to get subtly wrong, and\n * nothing for callers to know about. Padding is snapshotted on activate()\n * and restored on deactivate().\n *\n * Setting padding re-anchors the map's centre LngLat at the new centre\n * point rather than changing it, which is also what makes a mid-flow lift\n * change safe: the map slides, the chosen coordinate does not.\n *\n * Exposes window.PlacePicker with three methods:\n *\n *   activate({ onChange, recenterTo, occludedBy })\n *     Clears the map down to the basemap (window.PlacementFocus.enter() \u2014\n *     static/js/map_placement_focus.js), reveals the pin and starts\n *     tracking the coordinate under it. onChange(lat, lon)\n *     fires once immediately (the point under the pin is always a valid\n *     coordinate \u2014 this is what lets callers unblock a \"confirm\" affordance\n *     right away, with no separate \"tap to place\" step) and again on every\n *     subsequent 'moveend'. recenterTo (optional [lon, lat]) puts that\n *     coordinate under the pin before the first read \u2014 used by report.js's\n *     GPS-refine flow so the pin starts on the GPS fix rather than wherever\n *     the map already was. occludedBy (optional element) is the sheet the\n *     pin must stay clear of; omit it and the pin stays at the centre.\n *\n *   deactivate()\n *     Hides the pin, stops tracking, and restores every layer the map was\n *     showing before activate() cleared it. Idempotent \u2014 safe to call\n *     whether or not activate() was ever called.\n *\n *   isActive()\n *     True between a call to activate() and the matching deactivate().\n *\n * Loaded before favourites.js/report.js on every page that includes either\n * surface (see _favourites_surface.html / _report_surface.html) \u2014 both\n * script tags are `defer`, so document-order execution guarantees\n * window.PlacePicker exists by the time those modules run.\n */\n\n(function () {\n  'use strict';\n\n  /** Module-level 'moveend' handler, so deactivate() can remove exactly\n   * the listener activate() registered.\n   * @type {(() => void)|null}\n   */\n  let moveEndHandler = null;\n\n  /** Whether the picker is currently armed. @type {boolean} */\n  let active = false;\n\n  /** The sheet the pin must stay clear of, from activate()'s occludedBy.\n   * @type {HTMLElement|null}\n   */\n  let occluder = null;\n\n  /** Pixels the pin currently sits above the map's vertical centre.\n   * @type {number}\n   */\n  let pinLift = 0;\n\n  /** The map's ``padding`` before activate() applied the lift, restored on\n   * deactivate(). Null whenever the picker is disarmed.\n   * @type {maplibregl.PaddingOptions|null}\n   */\n  let basePadding = null;\n\n  /** Callback handed to activate(), re-run whenever the pin's screen point\n   * or the map moves. @type {((lat: number, lon: number) => void)|null}\n   */\n  let changeHandler = null;\n\n  /** Observers that re-run the lift measurement when the sheet grows or the\n   * viewport changes shape. @type {ResizeObserver|null}\n   */\n  let sheetObserver = null;\n\n  /** Return the global MAP object if available, else null. Guards against\n   * the case where map.js has not yet initialised.\n   * @returns {maplibregl.Map|null}\n   */\n  function getMap() {\n    return typeof MAP !== 'undefined' ? MAP : null;\n  }\n\n  /** @returns {HTMLElement|null} The centre-pin element, if in the DOM. */\n  function getPin() {\n    return document.getElementById('map-place-pin');\n  }\n\n  /** Reveal the centre-pin element, if present in the DOM. */\n  function showPin() {\n    const pin = getPin();\n    if (pin) pin.removeAttribute('hidden');\n  }\n\n  /** Hide the centre-pin element, if present in the DOM. */\n  function hidePin() {\n    const pin = getPin();\n    if (pin) pin.setAttribute('hidden', '');\n  }\n\n  /**\n   * Pixels the pin must be lifted above the map's vertical centre to clear\n   * the occluding sheet: half the height the sheet steals from the map, so\n   * the pin lands in the middle of the strip still visible above it.\n   *\n   * Zero whenever the sheet does not actually cover the pin's default point\n   * \u2014 on a wide viewport it is a bottom-right card, and the middle of the\n   * map is in the clear \u2014 so desktop keeps the plain centred pin.\n   *\n   * @returns {number} Lift in CSS pixels; never negative.\n   */\n  function measurePinLift() {\n    const map = getMap();\n    if (!map || !occluder || occluder.hasAttribute('hidden')) return 0;\n\n    const container = map.getContainer && map.getContainer();\n    if (!container) return 0;\n    const mapRect = container.getBoundingClientRect();\n    const sheetRect = occluder.getBoundingClientRect();\n    if (!mapRect.height || !sheetRect.height) return 0;\n\n    const centreX = mapRect.left + mapRect.width / 2;\n    const centreY = mapRect.top + mapRect.height / 2;\n    const covered =\n      centreX >= sheetRect.left &&\n      centreX <= sheetRect.right &&\n      centreY >= sheetRect.top &&\n      centreY <= sheetRect.bottom;\n    if (!covered) return 0;\n\n    // Height of the map above the sheet's top edge, clamped into the map:\n    // a sheet taller than the map itself leaves nothing visible, and the\n    // clamp keeps the lift at most half the map's height.\n    const visibleHeight = Math.min(\n      Math.max(sheetRect.top - mapRect.top, 0),\n      mapRect.height,\n    );\n    return (mapRect.height - visibleHeight) / 2;\n  }\n\n  /**\n   * Adopt a new lift: move the pin (via the CSS custom property) and move\n   * MapLibre's centre point to match (via bottom padding of twice the lift,\n   * since padding shifts the centre by half its imbalance).\n   *\n   * Applying both from one place is what guarantees the pin and the\n   * coordinate can never drift apart. Setting padding re-anchors the map's\n   * centre LngLat at the new centre point rather than changing it, so a lift\n   * adopted mid-flow slides the map and leaves the chosen point under the\n   * pin \u2014 no compensating camera move needed.\n   *\n   * @param {number} lift \u2014 pixels above the map's vertical centre.\n   */\n  function applyPinLift(lift) {\n    pinLift = lift;\n    const pin = getPin();\n    if (pin) pin.style.setProperty('--map-place-pin-lift', lift + 'px');\n    const map = getMap();\n    if (map && basePadding) {\n      map.setPadding({ ...basePadding, bottom: basePadding.bottom + lift * 2 });\n    }\n  }\n\n  /** Report the coordinate under the pin to activate()'s onChange \u2014 which,\n   * thanks to the padding above, is simply the map's centre.\n   */\n  function readCoord() {\n    if (!changeHandler) return;\n    const map = getMap();\n    if (!map) return;\n    const at = map.getCenter();\n    changeHandler(at.lat, at.lng);\n  }\n\n  /**\n   * Re-measure the lift after a layout change (the sheet swapping in taller\n   * content, an orientation change, the on-screen keyboard opening). The\n   * point the user had chosen is held under the pin across the move, so a\n   * sheet growing under their finger never silently re-picks a different\n   * coordinate.\n   */\n  function refreshPinLift() {\n    if (!active) return;\n    const next = measurePinLift();\n    if (next === pinLift) return;\n    applyPinLift(next);\n    readCoord();\n  }\n\n  /** Start watching for the layout changes that move the pin. */\n  function observeLayout() {\n    window.addEventListener('resize', refreshPinLift);\n    // jsdom (and older Safari) has no ResizeObserver \u2014 the window listener\n    // above still covers orientation and keyboard changes without it.\n    if (occluder && typeof ResizeObserver === 'function') {\n      sheetObserver = new ResizeObserver(refreshPinLift);\n      sheetObserver.observe(occluder);\n    }\n  }\n\n  /** Stop watching for layout changes. Idempotent. */\n  function unobserveLayout() {\n    window.removeEventListener('resize', refreshPinLift);\n    if (sheetObserver) {\n      sheetObserver.disconnect();\n      sheetObserver = null;\n    }\n  }\n\n  window.PlacePicker = {\n    /**\n     * Arm the place-picker: show the pin and start reporting the coordinate\n     * under it on every pan.\n     * @param {object} options\n     * @param {(lat: number, lon: number) => void} options.onChange \u2014 called\n     *   immediately with the point under the pin, then again on every\n     *   'moveend'.\n     * @param {[number, number]} [options.recenterTo] \u2014 optional [lon, lat]\n     *   to move under the pin before the first onChange read.\n     * @param {HTMLElement} [options.occludedBy] \u2014 the sheet the pin must\n     *   stay clear of; without it the pin stays at the map's centre.\n     */\n    activate: function activate(options) {\n      const map = getMap();\n      if (!map) return;\n\n      const recenterTo = options && options.recenterTo;\n\n      // Guard against a stray previous activation (mirrors the\n      // disarmPlacement-before-arm pattern the old marker code used).\n      this.deactivate();\n\n      changeHandler = (options && options.onChange) || null;\n      occluder = (options && options.occludedBy) || null;\n\n      // Clear the map down to the basemap before anything moves, so the\n      // overlays are already gone by the time recenterTo pans \u2014 the user\n      // never sees the choropleth slide across the screen on its way out.\n      window.PlacementFocus?.enter();\n\n      // Measure and place the pin before recentring: with the padding in\n      // place, setCenter puts its coordinate under the pin rather than at\n      // the map's geometric centre.\n      showPin();\n      active = true;\n      basePadding = map.getPadding\n        ? map.getPadding()\n        : { top: 0, right: 0, bottom: 0, left: 0 };\n      applyPinLift(measurePinLift());\n\n      if (recenterTo) {\n        map.setCenter(recenterTo);\n      }\n\n      readCoord();\n\n      moveEndHandler = readCoord;\n      map.on('moveend', moveEndHandler);\n      observeLayout();\n    },\n\n    /** Disarm the place-picker: hide the pin and stop tracking the map.\n     * Idempotent.\n     */\n    deactivate: function deactivate() {\n      const map = getMap();\n      if (map && moveEndHandler) {\n        map.off('moveend', moveEndHandler);\n      }\n      moveEndHandler = null;\n      unobserveLayout();\n      active = false;\n      changeHandler = null;\n      occluder = null;\n      // Lift 0 also writes basePadding straight back, restoring whatever\n      // padding the map had before the placement started.\n      applyPinLift(0);\n      basePadding = null;\n      hidePin();\n      // Bring back every overlay enter() hid. Idempotent on its own side,\n      // so the unconditional call is safe on the never-activated path.\n      window.PlacementFocus?.exit();\n    },\n\n    /**\n     * @returns {boolean} True while the picker is armed.\n     */\n    isActive: function isActive() {\n      return active;\n    },\n  };\n}());\n"],
  "mappings": "CAoEC,UAAY,CACX,aAMA,IAAIA,EAAiB,KAGjBC,EAAS,GAKTC,EAAW,KAKXC,EAAU,EAMVC,EAAc,KAKdC,EAAgB,KAKhBC,EAAgB,KAMpB,SAASC,GAAS,CAChB,OAAO,OAAO,IAAQ,IAAc,IAAM,IAC5C,CAGA,SAASC,GAAS,CAChB,OAAO,SAAS,eAAe,eAAe,CAChD,CAGA,SAASC,GAAU,CACjB,MAAMC,EAAMF,EAAO,EACfE,GAAKA,EAAI,gBAAgB,QAAQ,CACvC,CAGA,SAASC,GAAU,CACjB,MAAMD,EAAMF,EAAO,EACfE,GAAKA,EAAI,aAAa,SAAU,EAAE,CACxC,CAaA,SAASE,GAAiB,CACxB,MAAMC,EAAMN,EAAO,EACnB,GAAI,CAACM,GAAO,CAACX,GAAYA,EAAS,aAAa,QAAQ,EAAG,MAAO,GAEjE,MAAMY,EAAYD,EAAI,cAAgBA,EAAI,aAAa,EACvD,GAAI,CAACC,EAAW,MAAO,GACvB,MAAMC,EAAUD,EAAU,sBAAsB,EAC1CE,EAAYd,EAAS,sBAAsB,EACjD,GAAI,CAACa,EAAQ,QAAU,CAACC,EAAU,OAAQ,MAAO,GAEjD,MAAMC,EAAUF,EAAQ,KAAOA,EAAQ,MAAQ,EACzCG,EAAUH,EAAQ,IAAMA,EAAQ,OAAS,EAM/C,GAAI,EAJFE,GAAWD,EAAU,MACrBC,GAAWD,EAAU,OACrBE,GAAWF,EAAU,KACrBE,GAAWF,EAAU,QACT,MAAO,GAKrB,MAAMG,EAAgB,KAAK,IACzB,KAAK,IAAIH,EAAU,IAAMD,EAAQ,IAAK,CAAC,EACvCA,EAAQ,MACV,EACA,OAAQA,EAAQ,OAASI,GAAiB,CAC5C,CAeA,SAASC,EAAaC,EAAM,CAC1BlB,EAAUkB,EACV,MAAMX,EAAMF,EAAO,EACfE,GAAKA,EAAI,MAAM,YAAY,uBAAwBW,EAAO,IAAI,EAClE,MAAMR,EAAMN,EAAO,EACfM,GAAOT,GACTS,EAAI,WAAW,CAAE,GAAGT,EAAa,OAAQA,EAAY,OAASiB,EAAO,CAAE,CAAC,CAE5E,CAKA,SAASC,GAAY,CACnB,GAAI,CAACjB,EAAe,OACpB,MAAMQ,EAAMN,EAAO,EACnB,GAAI,CAACM,EAAK,OACV,MAAMU,EAAKV,EAAI,UAAU,EACzBR,EAAckB,EAAG,IAAKA,EAAG,GAAG,CAC9B,CASA,SAASC,GAAiB,CACxB,GAAI,CAACvB,EAAQ,OACb,MAAMwB,EAAOb,EAAe,EACxBa,IAAStB,IACbiB,EAAaK,CAAI,EACjBH,EAAU,EACZ,CAGA,SAASI,GAAgB,CACvB,OAAO,iBAAiB,SAAUF,CAAc,EAG5CtB,GAAY,OAAO,gBAAmB,aACxCI,EAAgB,IAAI,eAAekB,CAAc,EACjDlB,EAAc,QAAQJ,CAAQ,EAElC,CAGA,SAASyB,GAAkB,CACzB,OAAO,oBAAoB,SAAUH,CAAc,EAC/ClB,IACFA,EAAc,WAAW,EACzBA,EAAgB,KAEpB,CAEA,OAAO,YAAc,CAanB,SAAU,SAAkBsB,EAAS,CACnC,MAAMf,EAAMN,EAAO,EACnB,GAAI,CAACM,EAAK,OAEV,MAAMgB,EAAaD,GAAWA,EAAQ,WAItC,KAAK,WAAW,EAEhBvB,EAAiBuB,GAAWA,EAAQ,UAAa,KACjD1B,EAAY0B,GAAWA,EAAQ,YAAe,KAK9C,OAAO,gBAAgB,MAAM,EAK7BnB,EAAQ,EACRR,EAAS,GACTG,EAAcS,EAAI,WACdA,EAAI,WAAW,EACf,CAAE,IAAK,EAAG,MAAO,EAAG,OAAQ,EAAG,KAAM,CAAE,EAC3CO,EAAaR,EAAe,CAAC,EAEzBiB,GACFhB,EAAI,UAAUgB,CAAU,EAG1BP,EAAU,EAEVtB,EAAiBsB,EACjBT,EAAI,GAAG,UAAWb,CAAc,EAChC0B,EAAc,CAChB,EAKA,WAAY,UAAsB,CAChC,MAAMb,EAAMN,EAAO,EACfM,GAAOb,GACTa,EAAI,IAAI,UAAWb,CAAc,EAEnCA,EAAiB,KACjB2B,EAAgB,EAChB1B,EAAS,GACTI,EAAgB,KAChBH,EAAW,KAGXkB,EAAa,CAAC,EACdhB,EAAc,KACdO,EAAQ,EAGR,OAAO,gBAAgB,KAAK,CAC9B,EAKA,SAAU,UAAoB,CAC5B,OAAOV,CACT,CACF,CACF,GAAE",
  "names": ["moveEndHandler", "active", "occluder", "pinLift", "basePadding", "changeHandler", "sheetObserver", "getMap", "getPin", "showPin", "pin", "hidePin", "measurePinLift", "map", "container", "mapRect", "sheetRect", "centreX", "centreY", "visibleHeight", "applyPinLift", "lift", "readCoord", "at", "refreshPinLift", "next", "observeLayout", "unobserveLayout", "options", "recenterTo"]
}
