web-mojo 2.2.69 → 2.2.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/dist/admin.cjs.js +1 -1
  2. package/dist/admin.cjs.js.map +1 -1
  3. package/dist/admin.es.js +1 -1
  4. package/dist/admin.es.js.map +1 -1
  5. package/dist/auth.cjs.js +1 -1
  6. package/dist/auth.es.js +1 -1
  7. package/dist/charts.cjs.js +1 -1
  8. package/dist/charts.cjs.js.map +1 -1
  9. package/dist/charts.es.js +1 -1
  10. package/dist/charts.es.js.map +1 -1
  11. package/dist/chunks/ChatView-DH42WXgV.js +2 -0
  12. package/dist/chunks/{ChatView-Dw-iVmht.js.map → ChatView-DH42WXgV.js.map} +1 -1
  13. package/dist/chunks/ChatView-_8eQTETQ.js +2 -0
  14. package/dist/chunks/{ChatView-CZ3Key2k.js.map → ChatView-_8eQTETQ.js.map} +1 -1
  15. package/dist/chunks/Collection-BUv4E9op.js +2 -0
  16. package/dist/chunks/Collection-BUv4E9op.js.map +1 -0
  17. package/dist/chunks/Collection-r1ACzUeh.js +2 -0
  18. package/dist/chunks/Collection-r1ACzUeh.js.map +1 -0
  19. package/dist/chunks/ContextMenu-BFxliZ03.js +2 -0
  20. package/dist/chunks/{ContextMenu-8vTiZZQV.js.map → ContextMenu-BFxliZ03.js.map} +1 -1
  21. package/dist/chunks/ContextMenu-BwJJ4QJE.js +2 -0
  22. package/dist/chunks/{ContextMenu-DBw0WMTO.js.map → ContextMenu-BwJJ4QJE.js.map} +1 -1
  23. package/dist/chunks/DataView-DMpNXerv.js +2 -0
  24. package/dist/chunks/{DataView-DyJKgOn3.js.map → DataView-DMpNXerv.js.map} +1 -1
  25. package/dist/chunks/DataView-_CACqzRt.js +2 -0
  26. package/dist/chunks/{DataView-BEovBggn.js.map → DataView-_CACqzRt.js.map} +1 -1
  27. package/dist/chunks/Dialog-BVCCpLPw.js +3 -0
  28. package/dist/chunks/Dialog-BVCCpLPw.js.map +1 -0
  29. package/dist/chunks/Dialog-BYiynSW-.js +3 -0
  30. package/dist/chunks/Dialog-BYiynSW-.js.map +1 -0
  31. package/dist/chunks/FormView-Dw7HDwzy.js +3 -0
  32. package/dist/chunks/{FormView-BRHAIawp.js.map → FormView-Dw7HDwzy.js.map} +1 -1
  33. package/dist/chunks/FormView-OgrZ7x0z.js +3 -0
  34. package/dist/chunks/{FormView-B1CXO2t8.js.map → FormView-OgrZ7x0z.js.map} +1 -1
  35. package/dist/chunks/ListView-2M4I8KHF.js +2 -0
  36. package/dist/chunks/{ListView-CMZpwyyC.js.map → ListView-2M4I8KHF.js.map} +1 -1
  37. package/dist/chunks/ListView-B0QbqSPv.js +2 -0
  38. package/dist/chunks/{ListView-BLFFK_Ir.js.map → ListView-B0QbqSPv.js.map} +1 -1
  39. package/dist/chunks/MetricsCountryMapView-DDdDJQFA.js +2 -0
  40. package/dist/chunks/{MetricsCountryMapView-B0kWK-Js.js.map → MetricsCountryMapView-DDdDJQFA.js.map} +1 -1
  41. package/dist/chunks/MetricsCountryMapView-DIlezla0.js +2 -0
  42. package/dist/chunks/{MetricsCountryMapView-DuBKO7gz.js.map → MetricsCountryMapView-DIlezla0.js.map} +1 -1
  43. package/dist/chunks/MetricsMiniChartWidget-Dt2V0eXP.js +2 -0
  44. package/dist/chunks/{MetricsMiniChartWidget-Dg1e6EQJ.js.map → MetricsMiniChartWidget-Dt2V0eXP.js.map} +1 -1
  45. package/dist/chunks/MetricsMiniChartWidget-_N4kzNY_.js +2 -0
  46. package/dist/chunks/{MetricsMiniChartWidget-D1w608Jy.js.map → MetricsMiniChartWidget-_N4kzNY_.js.map} +1 -1
  47. package/dist/chunks/PDFViewer-BruR1RFn.js +2 -0
  48. package/dist/chunks/{PDFViewer-D_3V8QJe.js.map → PDFViewer-BruR1RFn.js.map} +1 -1
  49. package/dist/chunks/PDFViewer-CyGFVcvX.js +2 -0
  50. package/dist/chunks/{PDFViewer-CDeV9OBs.js.map → PDFViewer-CyGFVcvX.js.map} +1 -1
  51. package/dist/chunks/TableView-CxYpxZvr.js +2 -0
  52. package/dist/chunks/{TableView-CI_7a-kD.js.map → TableView-CxYpxZvr.js.map} +1 -1
  53. package/dist/chunks/TableView-DemRVhnX.js +2 -0
  54. package/dist/chunks/{TableView-CWk5k4LQ.js.map → TableView-DemRVhnX.js.map} +1 -1
  55. package/dist/chunks/TokenManager-BFaxNsXO.js +2 -0
  56. package/dist/chunks/{TokenManager-sZgt--C9.js.map → TokenManager-BFaxNsXO.js.map} +1 -1
  57. package/dist/chunks/TokenManager-IlBEFXqZ.js +2 -0
  58. package/dist/chunks/{TokenManager-ien2XzwO.js.map → TokenManager-IlBEFXqZ.js.map} +1 -1
  59. package/dist/chunks/UserProfileView-9vkfCPsp.js +2 -0
  60. package/dist/chunks/{UserProfileView-kupeq2rN.js.map → UserProfileView-9vkfCPsp.js.map} +1 -1
  61. package/dist/chunks/UserProfileView-tcBT6XcE.js +2 -0
  62. package/dist/chunks/{UserProfileView-DnVMHcLH.js.map → UserProfileView-tcBT6XcE.js.map} +1 -1
  63. package/dist/chunks/WebApp-BFR1zozS.js +2 -0
  64. package/dist/chunks/{WebApp-CcVF73yg.js.map → WebApp-BFR1zozS.js.map} +1 -1
  65. package/dist/chunks/WebApp-C82womPC.js +2 -0
  66. package/dist/chunks/{WebApp-Bti0Gqqo.js.map → WebApp-C82womPC.js.map} +1 -1
  67. package/dist/chunks/WebSocketClient-Ibi7mLQu.js +2 -0
  68. package/dist/chunks/{WebSocketClient-Bh0Mmtje.js.map → WebSocketClient-Ibi7mLQu.js.map} +1 -1
  69. package/dist/chunks/WebSocketClient-QaCUN3EQ.js +2 -0
  70. package/dist/chunks/{WebSocketClient-CLgYPxWX.js.map → WebSocketClient-QaCUN3EQ.js.map} +1 -1
  71. package/dist/chunks/index-BaPQHxbL.js +2 -0
  72. package/dist/chunks/{index-Da9sT-tE.js.map → index-BaPQHxbL.js.map} +1 -1
  73. package/dist/chunks/index-BdfwxVMZ.js +2 -0
  74. package/dist/chunks/{index-Aq9ke4vg.js.map → index-BdfwxVMZ.js.map} +1 -1
  75. package/dist/chunks/{version-XmirKYWA.js → version-C2yYRyPn.js} +2 -2
  76. package/dist/chunks/{version-XmirKYWA.js.map → version-C2yYRyPn.js.map} +1 -1
  77. package/dist/chunks/{version-D8JjsPW0.js → version-CaiqhdME.js} +2 -2
  78. package/dist/chunks/{version-D8JjsPW0.js.map → version-CaiqhdME.js.map} +1 -1
  79. package/dist/docit.cjs.js +1 -1
  80. package/dist/docit.cjs.js.map +1 -1
  81. package/dist/docit.es.js +1 -1
  82. package/dist/docit.es.js.map +1 -1
  83. package/dist/index.cjs.js +1 -1
  84. package/dist/index.cjs.js.map +1 -1
  85. package/dist/index.es.js +1 -1
  86. package/dist/index.es.js.map +1 -1
  87. package/dist/lightbox.cjs.js +1 -1
  88. package/dist/lightbox.cjs.js.map +1 -1
  89. package/dist/lightbox.es.js +1 -1
  90. package/dist/lightbox.es.js.map +1 -1
  91. package/dist/map.cjs.js +1 -1
  92. package/dist/map.cjs.js.map +1 -1
  93. package/dist/map.es.js +1 -1
  94. package/dist/map.es.js.map +1 -1
  95. package/dist/timeline.cjs.js +1 -1
  96. package/dist/timeline.cjs.js.map +1 -1
  97. package/dist/timeline.es.js +1 -1
  98. package/dist/timeline.es.js.map +1 -1
  99. package/dist/user-profile.cjs.js +1 -1
  100. package/dist/user-profile.es.js +1 -1
  101. package/dist/web-mojo.lite.iife.js +5435 -5435
  102. package/dist/web-mojo.lite.iife.js.map +1 -1
  103. package/dist/web-mojo.lite.iife.min.js +68 -68
  104. package/dist/web-mojo.lite.iife.min.js.map +1 -1
  105. package/package.json +1 -1
  106. package/dist/chunks/ChatView-CZ3Key2k.js +0 -2
  107. package/dist/chunks/ChatView-Dw-iVmht.js +0 -2
  108. package/dist/chunks/Collection-BWKmydl5.js +0 -2
  109. package/dist/chunks/Collection-BWKmydl5.js.map +0 -1
  110. package/dist/chunks/Collection-CmjTsmrP.js +0 -2
  111. package/dist/chunks/Collection-CmjTsmrP.js.map +0 -1
  112. package/dist/chunks/ContextMenu-8vTiZZQV.js +0 -2
  113. package/dist/chunks/ContextMenu-DBw0WMTO.js +0 -2
  114. package/dist/chunks/DataView-BEovBggn.js +0 -2
  115. package/dist/chunks/DataView-DyJKgOn3.js +0 -2
  116. package/dist/chunks/Dialog-Dhqtd9Yz.js +0 -2
  117. package/dist/chunks/Dialog-Dhqtd9Yz.js.map +0 -1
  118. package/dist/chunks/Dialog-t_9l2Mou.js +0 -2
  119. package/dist/chunks/Dialog-t_9l2Mou.js.map +0 -1
  120. package/dist/chunks/Files-6eRT5k3r.js +0 -2
  121. package/dist/chunks/Files-6eRT5k3r.js.map +0 -1
  122. package/dist/chunks/Files-Dh_5PFBn.js +0 -2
  123. package/dist/chunks/Files-Dh_5PFBn.js.map +0 -1
  124. package/dist/chunks/FormView-B1CXO2t8.js +0 -3
  125. package/dist/chunks/FormView-BRHAIawp.js +0 -3
  126. package/dist/chunks/ListView-BLFFK_Ir.js +0 -2
  127. package/dist/chunks/ListView-CMZpwyyC.js +0 -2
  128. package/dist/chunks/MetricsCountryMapView-B0kWK-Js.js +0 -2
  129. package/dist/chunks/MetricsCountryMapView-DuBKO7gz.js +0 -2
  130. package/dist/chunks/MetricsMiniChartWidget-D1w608Jy.js +0 -2
  131. package/dist/chunks/MetricsMiniChartWidget-Dg1e6EQJ.js +0 -2
  132. package/dist/chunks/PDFViewer-CDeV9OBs.js +0 -2
  133. package/dist/chunks/PDFViewer-D_3V8QJe.js +0 -2
  134. package/dist/chunks/Rest-B1eUyLX5.js +0 -2
  135. package/dist/chunks/Rest-B1eUyLX5.js.map +0 -1
  136. package/dist/chunks/Rest-BJ3Mvx1L.js +0 -2
  137. package/dist/chunks/Rest-BJ3Mvx1L.js.map +0 -1
  138. package/dist/chunks/TableView-CI_7a-kD.js +0 -2
  139. package/dist/chunks/TableView-CWk5k4LQ.js +0 -2
  140. package/dist/chunks/ToastService-C2tTooFn.js +0 -3
  141. package/dist/chunks/ToastService-C2tTooFn.js.map +0 -1
  142. package/dist/chunks/ToastService-nUaGVpSl.js +0 -3
  143. package/dist/chunks/ToastService-nUaGVpSl.js.map +0 -1
  144. package/dist/chunks/TokenManager-ien2XzwO.js +0 -2
  145. package/dist/chunks/TokenManager-sZgt--C9.js +0 -2
  146. package/dist/chunks/User-BL9M_PWB.js +0 -2
  147. package/dist/chunks/User-BL9M_PWB.js.map +0 -1
  148. package/dist/chunks/User-DqHG5Gr1.js +0 -2
  149. package/dist/chunks/User-DqHG5Gr1.js.map +0 -1
  150. package/dist/chunks/UserProfileView-DnVMHcLH.js +0 -2
  151. package/dist/chunks/UserProfileView-kupeq2rN.js +0 -2
  152. package/dist/chunks/WebApp-Bti0Gqqo.js +0 -2
  153. package/dist/chunks/WebApp-CcVF73yg.js +0 -2
  154. package/dist/chunks/WebSocketClient-Bh0Mmtje.js +0 -2
  155. package/dist/chunks/WebSocketClient-CLgYPxWX.js +0 -2
  156. package/dist/chunks/index-Aq9ke4vg.js +0 -2
  157. package/dist/chunks/index-Da9sT-tE.js +0 -2
package/dist/map.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/MetricsCountryMapView-B0kWK-Js.js"),t=require("./chunks/Rest-B1eUyLX5.js"),s=require("./chunks/Dialog-Dhqtd9Yz.js"),i=require("./chunks/FormPlugins-CEjco_Hb.js"),n=require("./chunks/Collection-CmjTsmrP.js");class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,s){const i=await t.rest.GET(this.fullPath(e),s||{});return i&&void 0!==i.data?i.data:i}async jsonPost(e,s){const i=await t.rest.POST(this.fullPath(e),s??{},{},{});return i&&void 0!==i.data?i.data:i}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function o(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function b(){p||l.value.trim().length>=n&&f()}function _(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function g(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",b),l.addEventListener("blur",_),window.addEventListener("resize",g),window.addEventListener("scroll",g,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",b)}catch(e){}try{l.removeEventListener("blur",_)}catch(e){}try{window.removeEventListener("resize",g)}catch(e){}try{window.removeEventListener("scroll",g,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends t.View{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e.MapView({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}class LocationPickerView extends t.View{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(n){}let s;const i=()=>{s=o(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(n){}try{s&&s()}catch(n){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const n=s?.type===this.fieldTypeName,a="address"===t?.getAttribute?.(this.attributeSelector);if(n||a){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const n=()=>{s=o(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{n()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};n()}}catch(n){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}exports.MapLibreView=e.MapLibreView,exports.MapView=e.MapView,exports.MetricsCountryMapView=e.MetricsCountryMapView,exports.View=t.View,exports.Collection=n.Collection,exports.Model=n.Model,exports.LocationClient=LocationClient,exports.LocationDetailsView=LocationDetailsView,exports.LocationFormPlugin=LocationFormPlugin,exports.registerLocationPlugin=function(e={}){const t=new LocationFormPlugin(e);return i.FormPlugins.register(t)},exports.showLocationDetailsDialog=async function({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:i=null,id:n=null,title:o="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(i||n))try{const t=await e.placeDetails({place_id:i,id:n,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:a,tileLayer:r});return s.default.showDialog({title:o,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})},exports.showLocationPickerDialog=async function({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:i=3,debounceMs:n=200,placeholder:o="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:i,debounceMs:n,placeholder:o,height:r,tileLayer:l});return"ok"===await s.default.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:a,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null},exports.useLocationAutocomplete=o;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/MetricsCountryMapView-DDdDJQFA.js"),t=require("./chunks/Collection-BUv4E9op.js"),s=require("./chunks/Dialog-BYiynSW-.js"),i=require("./chunks/FormPlugins-CEjco_Hb.js");class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,s){const i=await t.rest.GET(this.fullPath(e),s||{});return i&&void 0!==i.data?i.data:i}async jsonPost(e,s){const i=await t.rest.POST(this.fullPath(e),s??{},{},{});return i&&void 0!==i.data?i.data:i}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function n(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function b(){p||l.value.trim().length>=n&&f()}function g(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function _(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",b),l.addEventListener("blur",g),window.addEventListener("resize",_),window.addEventListener("scroll",_,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",b)}catch(e){}try{l.removeEventListener("blur",g)}catch(e){}try{window.removeEventListener("resize",_)}catch(e){}try{window.removeEventListener("scroll",_,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends t.View{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e.MapView({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}class LocationPickerView extends t.View{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(o){}let s;const i=()=>{s=n(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(o){}try{s&&s()}catch(o){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const o=s?.type===this.fieldTypeName,a="address"===t?.getAttribute?.(this.attributeSelector);if(o||a){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const o=()=>{s=n(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{o()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};o()}}catch(o){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}exports.MapLibreView=e.MapLibreView,exports.MapView=e.MapView,exports.MetricsCountryMapView=e.MetricsCountryMapView,exports.Collection=t.Collection,exports.Model=t.Model,exports.View=t.View,exports.LocationClient=LocationClient,exports.LocationDetailsView=LocationDetailsView,exports.LocationFormPlugin=LocationFormPlugin,exports.registerLocationPlugin=function(e={}){const t=new LocationFormPlugin(e);return i.FormPlugins.register(t)},exports.showLocationDetailsDialog=async function({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:i=null,id:n=null,title:o="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(i||n))try{const t=await e.placeDetails({place_id:i,id:n,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:a,tileLayer:r});return s.Dialog.showDialog({title:o,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})},exports.showLocationPickerDialog=async function({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:i=3,debounceMs:n=200,placeholder:o="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:i,debounceMs:n,placeholder:o,height:r,tileLayer:l});return"ok"===await s.Dialog.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:a,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null},exports.useLocationAutocomplete=n;
2
2
  //# sourceMappingURL=map.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"map.cjs.js","sources":["../src/extensions/map/location/LocationClient.js","../src/extensions/map/location/useLocationAutocomplete.js","../src/extensions/map/location/LocationDetailsView.js","../src/extensions/map/location/LocationDialogs.js","../src/extensions/map/location/LocationPlugin.js"],"sourcesContent":["/**\n * LocationClient - minimal client for Location & Address API\n *\n * Endpoints (default paths):\n * - POST /location/address/validate\n * - GET /location/address/suggestions ?input=...&session_token=...\n * - GET /location/address/place-details ?place_id=...&session_token=...\n * - POST /location/address/geocode\n * - GET /location/address/reverse-geocode ?lat=...&lng=...\n * - GET /location/timezone ?lat=...&lng=...\n *\n * This client is framework-friendly and can be used anywhere in a MOJO app.\n *\n * Examples:\n *\n * // Zero-config defaults (uses basePath '/api')\n * import LocationClient from '@ext/map/location/LocationClient.js';\n * const loc = new LocationClient();\n * const res = await loc.autocomplete('1600 Amphitheatre');\n * const details = await loc.placeDetails({ place_id: res?.data?.[0]?.place_id });\n *\n * // Custom base path (still works with core Rest base URL configuration)\n * const loc2 = new LocationClient({ basePath: '/api' });\n *\n * // Optional auth header (if your API requires auth)\n * loc2.setAuthHeader(() => `Bearer ${token}`);\n */\nimport rest from '@core/Rest.js';\nexport default class LocationClient {\n /**\n * @param {Object} options\n * @param {string} [options.basePath='/api'] - API base path prefix (e.g., '/api')\n * @param {string|(() => string|null)} [options.authHeader] - Authorization header string or function returning one\n * @param {Object} [options.endpoints] - Override endpoint paths\n * @param {Function} [options.fetchImpl] - Custom fetch implementation, defaults to global fetch\n */\n constructor({\n basePath = '/api',\n endpoints = {}\n } = {}) {\n this.basePath = String(basePath || '');\n\n this.sessionToken = null; // used for autocomplete session cost optimization\n\n // Default endpoint paths (relative to base path)\n this.endpoints = {\n validate: '/location/address/validate',\n autocomplete: '/location/address/suggestions',\n details: '/location/address/place-details',\n geocode: '/location/address/geocode',\n reverse: '/location/address/reverse-geocode',\n timezone: '/location/timezone',\n ...endpoints\n };\n\n\n }\n\n /**\n * Optional helper: supply or change auth header at runtime\n * @param {string|(() => string|null)} header\n */\n setAuthHeader(header) {\n this._authHeader = header;\n }\n\n /**\n * Compute headers for a request.\n * @param {Object} [extra]\n * @returns {Record<string,string>}\n */\n headers(extra) {\n let auth = null;\n if (typeof this._authHeader === 'function') {\n try { auth = this._authHeader(); } catch { auth = null; }\n } else {\n auth = this._authHeader;\n }\n const h = { 'Content-Type': 'application/json', ...(extra || {}) };\n if (auth) h.Authorization = auth;\n return h;\n }\n\n /**\n * Perform JSON GET with query params\n * @param {string} path - relative path starting with '/'\n * @param {Record<string, any>} [params]\n */\n async jsonGet(path, params) {\n const resp = await rest.GET(this.fullPath(path), params || {});\n // Unwrap Rest wrapper to return server JSON body (spec shape)\n return (resp && resp.data !== undefined) ? resp.data : resp;\n }\n\n /**\n * Perform JSON POST\n * @param {string} path - relative path starting with '/'\n * @param {any} body\n */\n async jsonPost(path, body) {\n const resp = await rest.POST(this.fullPath(path), body ?? {}, {}, {});\n // Unwrap Rest wrapper to return server JSON body (spec shape)\n return (resp && resp.data !== undefined) ? resp.data : resp;\n }\n\n fullPath(path) {\n return `${this.basePath}${path}`;\n }\n\n async _safeJson(res) {\n try {\n return await res.json();\n } catch {\n return null;\n }\n }\n\n // ----------------------------\n // API Methods\n // ----------------------------\n\n /**\n * Address Validation\n * @param {Object} address\n * @param {string} address.address1\n * @param {string} [address.address2]\n * @param {string} address.city\n * @param {string} address.state\n * @param {string} address.postal_code\n * @param {string} [address.provider] - e.g., 'usps' (if API supports provider selection)\n * @returns {Promise<any>} API response\n */\n validateAddress(address) {\n return this.jsonPost(this.endpoints.validate, address);\n }\n\n /**\n * Address Autocomplete (GET)\n * Maintains a session token for repeated queries to optimize provider cost.\n * @param {string} query\n * @param {Object} [opts] - e.g., { country, state, ... }\n * @returns {Promise<any>} { success, session_token, data: [{ id, place_id, description, ... }], ... }\n */\n async autocomplete(query, opts = {}) {\n if (!query || String(query).trim().length === 0) {\n return { success: true, data: [], size: 0, count: 0 };\n }\n if (!this.sessionToken) {\n this.sessionToken = this._createSessionToken();\n }\n const params = { input: query, session_token: this.sessionToken, ...opts };\n const result = await this.jsonGet(this.endpoints.autocomplete, params);\n // Persist returned session_token for subsequent calls if provided by API\n if (result && result.session_token) {\n this.sessionToken = result.session_token;\n }\n return result;\n }\n\n /**\n * Place Details (GET)\n * @param {Object} params\n * @param {string} [params.place_id]\n * @param {string} [params.id]\n * @returns {Promise<any>} { success, address: { formatted_address, latitude, longitude, ... } }\n */\n placeDetails({ place_id, session_token, id } = {}) {\n const q = {};\n const pid = place_id || id || null;\n if (pid) q.place_id = pid;\n if (session_token) q.session_token = session_token;\n return this.jsonGet(this.endpoints.details, q);\n }\n\n /**\n * Geocode (POST)\n * @param {string|Object} address - string or object { address1, city, state, postal_code }\n * @returns {Promise<any>} { success, latitude, longitude, formatted_address, place_id, address_components... }\n */\n geocode(address) {\n return this.jsonPost(this.endpoints.geocode, { address });\n }\n\n /**\n * Reverse Geocoding (GET)\n * @param {Object} coords\n * @param {number|string} coords.lat\n * @param {number|string} coords.lng\n * @returns {Promise<any>} { success, formatted_address, place_id, address_components... }\n */\n reverseGeocode({ lat, lng }) {\n return this.jsonGet(this.endpoints.reverse, { lat, lng });\n }\n\n /**\n * Timezone Lookup (GET)\n * @param {Object} coords\n * @param {number|string} coords.lat\n * @param {number|string} coords.lng\n * @returns {Promise<any>} { success, timezone_id, timezone_name, raw_offset, dst_offset, total_offset }\n */\n timezone({ lat, lng }) {\n return this.jsonGet(this.endpoints.timezone, { lat, lng });\n }\n\n /**\n * Reset session token (useful when user starts a new autocomplete flow).\n */\n resetSessionToken() {\n this.sessionToken = null;\n }\n\n /**\n * Basic parser for suggestion item to extract display info.\n * @param {Object} suggestion - item from autocomplete response\n */\n normalizeSuggestion(suggestion) {\n return {\n id: suggestion?.id || suggestion?.place_id || null,\n place_id: suggestion?.place_id || suggestion?.id || null,\n description: suggestion?.description || '',\n main_text: suggestion?.main_text || '',\n secondary_text: suggestion?.secondary_text || '',\n types: suggestion?.types || []\n };\n }\n\n _createSessionToken() {\n // Prefer crypto.randomUUID if available\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`;\n }\n}\n\nexport { LocationClient };","/**\n * useLocationAutocomplete - attach address autocomplete and details population to a FormView field.\n *\n * Minimal, framework-agnostic. Expects a LocationClient-like object with:\n * - autocomplete(input, opts?) -> Promise<{ success: boolean, session_token?: string, data: Array<{ id, place_id, description, main_text, secondary_text, types }> }>\n * - placeDetails({ place_id, session_token? }) -> Promise<{ success: boolean, address: { formatted_address, latitude, longitude, ... } }>\n *\n * Usage:\n * import { useLocationAutocomplete } from '@ext/map/location/useLocationAutocomplete.js';\n * import LocationClient from '@ext/map/location/LocationClient.js';\n *\n * const client = new LocationClient({ basePath: '/api' });\n * const dispose = useLocationAutocomplete(this, {\n * client,\n * field: 'address1',\n * mapping: {\n * address1: 'address1',\n * city: 'city',\n * state_code: 'state',\n * postal_code: 'postal_code',\n * country_code: 'country',\n * latitude: 'lat',\n * longitude: 'lng',\n * formatted_address: 'formatted_address',\n * place_id: 'place_id'\n * }\n * });\n *\n * @param {Object} formView - Instance of FormView (must expose element and optionally setFieldValue(name, value))\n * @param {Object} options\n * @param {Object} options.client - Location API client with autocomplete and placeDetails methods\n * @param {string|HTMLElement} options.field - Field name or input element to attach to (default: 'address1')\n * @param {string} [options.dropdownClass='loc-suggest'] - CSS class for the suggestions dropdown\n * @param {number} [options.minChars=3] - Minimum characters before triggering suggestions\n * @param {number} [options.debounceMs=200] - Debounce in ms for typing\n * @param {Object} [options.mapping] - Map of sourceKey(from API address) -> formFieldName\n * @param {Function} [options.onSelect] - Callback(details) when a suggestion is selected\n * @returns {Function} dispose() - Cleanup handler\n */\nexport function useLocationAutocomplete(formView, {\n client,\n field = 'address1',\n dropdownClass = 'loc-suggest',\n minChars = 3,\n debounceMs = 200,\n mapping = {\n address1: 'address1',\n city: 'city',\n state_code: 'state',\n postal_code: 'postal_code',\n country_code: 'country',\n latitude: 'latitude',\n longitude: 'longitude',\n formatted_address: 'formatted_address',\n place_id: 'place_id'\n },\n onSelect\n} = {}) {\n if (!formView || !formView.element) {\n console.warn('[useLocationAutocomplete] Missing formView or formView.element');\n return () => {};\n }\n if (!client || typeof client.autocomplete !== 'function' || typeof client.placeDetails !== 'function') {\n console.warn('[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails().');\n return () => {};\n }\n\n // Resolve target input\n const inputEl = (typeof field === 'string')\n ? (formView.element.querySelector(`input[name=\"${field}\"], #${field}`) || null)\n : (field instanceof HTMLElement ? field : null);\n\n if (!inputEl) {\n // Quietly no-op if field isn't present\n return () => {};\n }\n\n // Create dropdown\n const dd = document.createElement('div');\n dd.className = dropdownClass || 'loc-suggest';\n dd.style.position = 'absolute';\n dd.style.zIndex = '10000';\n dd.style.display = 'none';\n dd.style.background = '#fff';\n dd.style.border = '1px solid #e5e7eb';\n dd.style.borderRadius = '8px';\n dd.style.boxShadow = '0 8px 24px rgba(0,0,0,.08)';\n dd.style.padding = '4px 0';\n dd.style.maxHeight = '280px';\n dd.style.overflowY = 'auto';\n dd.style.minWidth = '240px';\n dd.setAttribute('role', 'listbox');\n dd.setAttribute('aria-label', 'Address suggestions');\n\n let open = false;\n let timer = null;\n let suppress = false;\n\n function placeDropdown() {\n if (!open) return;\n const r = inputEl.getBoundingClientRect();\n dd.style.minWidth = `${r.width}px`;\n dd.style.left = `${r.left + window.scrollX}px`;\n dd.style.top = `${r.bottom + window.scrollY + 4}px`;\n }\n\n function openDropdown() {\n if (!open) {\n open = true;\n dd.style.display = 'block';\n document.body.appendChild(dd);\n placeDropdown();\n }\n }\n\n function closeDropdown() {\n open = false;\n dd.style.display = 'none';\n dd.innerHTML = '';\n if (dd.parentNode) {\n dd.parentNode.removeChild(dd);\n }\n }\n\n function createRow(item, index) {\n const row = document.createElement('div');\n row.setAttribute('role', 'option');\n row.setAttribute('tabindex', '-1');\n row.style.padding = '8px 12px';\n row.style.cursor = 'pointer';\n row.style.display = 'flex';\n row.style.flexDirection = 'column';\n row.dataset.index = String(index);\n\n const main = document.createElement('div');\n main.style.fontWeight = '600';\n main.style.color = '#111827';\n main.textContent = item.main_text || item.description || '';\n\n const sub = document.createElement('div');\n sub.style.fontSize = '12px';\n sub.style.color = '#6b7280';\n sub.textContent = item.secondary_text || '';\n\n row.appendChild(main);\n if (sub.textContent) row.appendChild(sub);\n\n row.addEventListener('mouseenter', () => { row.style.background = '#f3f4f6'; });\n row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; });\n\n row.addEventListener('mousedown', (e) => {\n // Use mousedown to select without losing focus before click\n e.preventDefault();\n selectSuggestion(item);\n });\n\n return row;\n }\n\n async function renderSuggestions(list) {\n dd.innerHTML = '';\n if (!list || list.length === 0) {\n const empty = document.createElement('div');\n empty.style.padding = '8px 12px';\n empty.style.color = '#6b7280';\n empty.textContent = 'No results';\n dd.appendChild(empty);\n return;\n }\n list.forEach((item, idx) => dd.appendChild(createRow(item, idx)));\n }\n\n async function selectSuggestion(item) {\n try {\n const id = item.place_id || item.id;\n let details = null;\n if (id) {\n const res = await client.placeDetails({ place_id: id, id, session_token: client.sessionToken });\n details = res?.address || null;\n }\n\n // Set input to readable formatted address\n // Suppress immediate re-query from dispatched input/focus\n suppress = true;\n clearTimeout(timer);\n if (details?.formatted_address) {\n inputEl.value = details.formatted_address;\n // Trigger a native input event if consumers rely on it\n inputEl.dispatchEvent(new Event('input', { bubbles: true }));\n } else if (item.description) {\n inputEl.value = item.description;\n inputEl.dispatchEvent(new Event('input', { bubbles: true }));\n }\n // Hide dropdown and blur to avoid onFocus reopen\n try { inputEl.blur(); } catch {}\n closeDropdown();\n // Lift suppression after debounce window\n setTimeout(() => { suppress = false; }, debounceMs + 50);\n\n // Apply mapping to populate other fields\n if (details && mapping && typeof mapping === 'object') {\n Object.entries(mapping).forEach(([srcKey, formField]) => {\n if (!formField) return;\n const val = details[srcKey];\n if (val !== undefined && val !== null) {\n // Update FormView model and DOM field\n try {\n if (typeof formView.setFieldValue === 'function') {\n formView.setFieldValue(formField, String(val));\n }\n } catch (err) {\n // setFieldValue is optional; ignore errors\n }\n const targetEl = formView.element.querySelector(`input[name=\"${formField}\"], #${formField}, textarea[name=\"${formField}\"], select[name=\"${formField}\"]`);\n if (targetEl) {\n targetEl.value = String(val);\n targetEl.dispatchEvent(new Event('input', { bubbles: true }));\n targetEl.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n });\n }\n\n if (typeof onSelect === 'function') {\n onSelect(details || null);\n }\n } catch (err) {\n console.warn('[useLocationAutocomplete] placeDetails error:', err);\n } finally {\n closeDropdown();\n }\n }\n\n async function handleInput() {\n const q = inputEl.value.trim();\n if (q.length < minChars) {\n closeDropdown();\n return;\n }\n\n try {\n const res = await client.autocomplete(q);\n const list = Array.isArray(res?.data) ? res.data : [];\n const items = list.map(x => ({\n id: x.id,\n place_id: x.place_id,\n description: x.description,\n main_text: x.main_text,\n secondary_text: x.secondary_text,\n types: x.types\n }));\n openDropdown();\n placeDropdown();\n await renderSuggestions(items);\n } catch (err) {\n console.warn('[useLocationAutocomplete] autocomplete error:', err);\n closeDropdown();\n }\n }\n\n function onInput() {\n if (suppress) return;\n clearTimeout(timer);\n timer = setTimeout(handleInput, debounceMs);\n }\n\n function onFocus() {\n if (suppress) return;\n if (inputEl.value.trim().length >= minChars) {\n onInput();\n }\n }\n\n function onBlur() {\n // defer close to allow mousedown on dropdown items to register\n setTimeout(() => {\n if (!dd.contains(document.activeElement)) {\n closeDropdown();\n }\n }, 120);\n }\n\n function onWindowMove() {\n if (open) placeDropdown();\n }\n\n function onDocumentClick(e) {\n if (!dd.contains(e.target) && e.target !== inputEl) {\n closeDropdown();\n }\n }\n\n // Attach listeners\n inputEl.addEventListener('input', onInput);\n inputEl.addEventListener('focus', onFocus);\n inputEl.addEventListener('blur', onBlur);\n window.addEventListener('resize', onWindowMove);\n window.addEventListener('scroll', onWindowMove, true);\n document.addEventListener('click', onDocumentClick);\n\n // Return disposer for cleanup\n return function dispose() {\n clearTimeout(timer);\n try { inputEl.removeEventListener('input', onInput); } catch (e) { /* ignore */ }\n try { inputEl.removeEventListener('focus', onFocus); } catch (e) { /* ignore */ }\n try { inputEl.removeEventListener('blur', onBlur); } catch (e) { /* ignore */ }\n try { window.removeEventListener('resize', onWindowMove); } catch (e) { /* ignore */ }\n try { window.removeEventListener('scroll', onWindowMove, true); } catch (e) { /* ignore */ }\n try { document.removeEventListener('click', onDocumentClick); } catch (e) { /* ignore */ }\n try { closeDropdown(); } catch (e) { /* ignore */ }\n };\n}\n\nexport default useLocationAutocomplete;","/**\n * LocationDetailsView - Displays a formatted address and an optional map preview.\n *\n * Usage:\n * new LocationDetailsView({\n * details: {\n * formatted_address: '1600 Amphitheatre Pkwy...',\n * latitude: 37.422,\n * longitude: -122.084,\n * place_id: '...'\n * },\n * height: 260,\n * tileLayer: 'osm'\n * });\n */\n\nimport View from '@core/View.js';\nimport MapView from '../MapView.js';\n\nexport default class LocationDetailsView extends View {\n /**\n * @param {Object} options\n * @param {Object} [options.details] - Address details (formatted_address, latitude, longitude, place_id, etc.)\n * @param {number} [options.height=260] - Map height (px)\n * @param {string} [options.tileLayer='osm'] - Tile layer key for MapView\n */\n constructor({ details = {}, height = 260, tileLayer = 'osm' } = {}) {\n super({\n className: 'location-details-view'\n });\n\n this.details = details || {};\n this.height = Number.isFinite(height) ? height : 260;\n this.tileLayer = tileLayer || 'osm';\n\n // Expose flattened fields for Mustache (view is the template context)\n this.formatted_address = this.details.formatted_address || '';\n this.place_id = this.details.place_id || '';\n this.latitude =\n this._toNumber(this.details.latitude ?? this.details.lat ?? null);\n this.longitude =\n this._toNumber(this.details.longitude ?? this.details.lng ?? null);\n\n this.hasCoords = Number.isFinite(this.latitude) && Number.isFinite(this.longitude);\n\n // Keep a reference to child map view (if created)\n this._mapView = null;\n\n // Simple, framework-friendly template with a child container for the map\n this.template = `\n <div class=\"loc-details\">\n {{#formatted_address}}\n <div class=\"mb-2 fw-semibold\" style=\"word-break: break-word;\">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class=\"text-muted small mb-2\">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class=\"text-muted small mb-2\">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container=\"map\"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class=\"text-muted small\" style=\"border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;\">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n `;\n }\n\n async onInit() {\n // Create child map view only if we have coordinates\n if (this.hasCoords) {\n const markers = [{\n lat: this.latitude,\n lng: this.longitude,\n popup: this.formatted_address || ''\n }];\n\n this._mapView = new MapView({\n markers,\n center: [this.latitude, this.longitude],\n zoom: 13,\n height: this.height,\n tileLayer: this.tileLayer,\n showLayerControl: true,\n containerId: 'map'\n });\n\n this.addChild(this._mapView);\n }\n }\n\n _toNumber(v) {\n if (v === null || v === undefined || v === '') return null;\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n }\n}","/**\n * LocationDialogs - helpers for showing location details and a picker dialog.\n *\n * Exports:\n * - showLocationDetailsDialog({ client?, details?, place_id?, id?, title?, height?, tileLayer? })\n * -> Shows a dialog with a formatted address + map. If details not provided, fetches via place_id/id.\n * - showLocationPickerDialog({ client?, title?, minChars?, debounceMs?, placeholder?, confirmText?, height?, tileLayer? })\n * -> Opens a dialog with an address search box, suggestion list, and live preview map. Returns the chosen details or null.\n */\n\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport View from '@core/View.js';\nimport LocationClient from './LocationClient.js';\nimport LocationDetailsView from './LocationDetailsView.js';\n\n/**\n * Show a dialog with location details + map\n * @param {Object} options\n * @param {LocationClient} [options.client] - Optional LocationClient instance\n * @param {Object} [options.details] - If provided, dialog renders immediately without fetching\n * @param {string} [options.place_id] - Place id to fetch details\n * @param {string} [options.id] - Alternative id to fetch details\n * @param {string} [options.title='Location Details'] - Dialog title\n * @param {number} [options.height=260] - Map height\n * @param {string} [options.tileLayer='osm'] - Tile layer\n * @returns {Promise<any>} Dialog result (from Dialog.showView), primarily useful for awaiting close\n */\nexport async function showLocationDetailsDialog({\n client = new LocationClient({ basePath: '/api' }),\n details = null,\n place_id = null,\n id = null,\n title = 'Location Details',\n height = 260,\n tileLayer = 'osm'\n} = {}) {\n let resolved = details;\n\n if (!resolved && (place_id || id)) {\n try {\n const res = await client.placeDetails({ place_id, id, session_token: client.sessionToken });\n resolved = res?.address || null;\n } catch (err) {\n // Fallback to an error placeholder\n resolved = {\n formatted_address: 'Unable to load place details',\n error: err?.message || 'Unknown error'\n };\n }\n }\n\n const view = new LocationDetailsView({\n details: resolved || {},\n height,\n tileLayer\n });\n\n return Dialog.showDialog({\n title,\n body: view,\n size: 'md',\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true, value: 'close' }]\n });\n}\n\n/**\n * Internal view class to implement a picker UI:\n * - Search input with debounced autocomplete\n * - Suggestions dropdown\n * - Live details preview with map\n */\nclass LocationPickerView extends View {\n constructor({\n client,\n minChars = 3,\n debounceMs = 200,\n placeholder = 'Search address',\n height = 240,\n tileLayer = 'osm'\n } = {}) {\n super({\n className: 'location-picker-view',\n template: '<div class=\"location-picker-view-root\"></div>'\n });\n this.client = client;\n this.minChars = minChars;\n this.debounceMs = debounceMs;\n this.placeholder = placeholder;\n this.height = height;\n this.tileLayer = tileLayer;\n\n this._selected = null; // Selected details object\n this._timer = null; // Debounce timer\n this._suggestions = []; // Cached suggestions\n this._elements = {}; // refs to elements\n this._previewView = null; // LocationDetailsView instance for live preview\n }\n\n getSelected() {\n return this._selected;\n }\n\n async onAfterRender() {\n // Build UI\n const root = document.createElement('div');\n root.style.display = 'grid';\n root.style.gap = '10px';\n\n // Search input\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'form-control';\n input.placeholder = this.placeholder || 'Search address';\n input.setAttribute('aria-label', 'Address search');\n root.appendChild(input);\n\n // Suggestions container\n const dd = document.createElement('div');\n dd.style.border = '1px solid #e5e7eb';\n dd.style.borderRadius = '8px';\n dd.style.background = '#fff';\n dd.style.boxShadow = '0 8px 24px rgba(0,0,0,.08)';\n dd.style.maxHeight = '260px';\n dd.style.overflowY = 'auto';\n dd.style.display = 'none';\n root.appendChild(dd);\n\n // Preview container for details + map\n const previewHost = document.createElement('div');\n root.appendChild(previewHost);\n\n // Mount tree\n this.element.appendChild(root);\n\n // Store refs\n this._elements = { root, input, dd, previewHost };\n\n // Wire events\n input.addEventListener('input', () => {\n clearTimeout(this._timer);\n this._timer = setTimeout(() => this._handleInput(), this.debounceMs);\n });\n\n input.addEventListener('focus', () => {\n if ((input.value || '').trim().length >= this.minChars) {\n this._handleInput();\n }\n });\n\n // Render empty preview on init\n await this._renderPreview(null);\n }\n\n async _handleInput() {\n const q = (this._elements.input.value || '').trim();\n if (q.length < this.minChars) {\n this._elements.dd.style.display = 'none';\n this._elements.dd.innerHTML = '';\n return;\n }\n\n try {\n const res = await this.client.autocomplete(q);\n const items = Array.isArray(res?.data) ? res.data : [];\n this._suggestions = items.map(x => ({\n id: x.id,\n place_id: x.place_id,\n description: x.description,\n main_text: x.main_text,\n secondary_text: x.secondary_text,\n types: x.types\n }));\n await this._renderSuggestions();\n } catch (err) {\n // show empty state\n this._suggestions = [];\n await this._renderSuggestions();\n }\n }\n\n async _renderSuggestions() {\n const dd = this._elements.dd;\n dd.innerHTML = '';\n\n if (!this._suggestions.length) {\n const empty = document.createElement('div');\n empty.style.padding = '8px 12px';\n empty.style.color = '#6b7280';\n empty.textContent = 'No results';\n dd.appendChild(empty);\n dd.style.display = 'block';\n return;\n }\n\n this._suggestions.forEach((item, idx) => {\n const row = document.createElement('div');\n row.style.padding = '8px 12px';\n row.style.cursor = 'pointer';\n row.style.display = 'flex';\n row.style.flexDirection = 'column';\n row.addEventListener('mouseenter', () => { row.style.background = '#f3f4f6'; });\n row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; });\n\n const main = document.createElement('div');\n main.style.fontWeight = '600';\n main.style.color = '#111827';\n main.textContent = item.main_text || item.description || '';\n\n const sub = document.createElement('div');\n sub.style.fontSize = '12px';\n sub.style.color = '#6b7280';\n sub.textContent = item.secondary_text || '';\n\n row.appendChild(main);\n if (sub.textContent) row.appendChild(sub);\n\n row.addEventListener('mousedown', (e) => {\n e.preventDefault(); // select without losing focus\n this._selectSuggestion(item);\n });\n\n dd.appendChild(row);\n });\n\n dd.style.display = 'block';\n }\n\n async _selectSuggestion(item) {\n try {\n const id = item.place_id || item.id;\n if (!id) return;\n\n const res = await this.client.placeDetails({ place_id: id, id, session_token: this.client.sessionToken });\n const details = res?.address || null;\n\n // Update input to readable value and keep selected\n if (details?.formatted_address) {\n this._elements.input.value = details.formatted_address;\n } else if (item.description) {\n this._elements.input.value = item.description;\n }\n\n this._selected = details || null;\n\n // Hide suggestions after selection\n this._elements.dd.style.display = 'none';\n this._elements.dd.innerHTML = '';\n\n // Update preview\n await this._renderPreview(this._selected);\n } catch (err) {\n // keep selection unchanged on error\n }\n }\n\n async _renderPreview(details) {\n // Clear previous preview\n this._elements.previewHost.innerHTML = '';\n\n // If nothing selected, render a small placeholder\n if (!details) {\n const ph = document.createElement('div');\n ph.style.border = '1px dashed #e5e7eb';\n ph.style.borderRadius = '8px';\n ph.style.padding = '12px';\n ph.style.color = '#6b7280';\n ph.textContent = 'No location selected';\n this._elements.previewHost.appendChild(ph);\n return;\n }\n\n // Render a LocationDetailsView\n try {\n this._previewView = new LocationDetailsView({\n details,\n height: this.height,\n tileLayer: this.tileLayer\n });\n await this._previewView.render(true, this._elements.previewHost);\n } catch {\n const fallback = document.createElement('div');\n fallback.style.border = '1px dashed #e5e7eb';\n fallback.style.borderRadius = '8px';\n fallback.style.padding = '12px';\n fallback.textContent = details?.formatted_address || 'Selected location';\n this._elements.previewHost.appendChild(fallback);\n }\n }\n\n async onBeforeDestroy() {\n clearTimeout(this._timer);\n await super.onBeforeDestroy();\n }\n}\n\n/**\n * Show a picker dialog for choosing a location.\n * Returns the chosen location details or null if canceled.\n *\n * @param {Object} options\n * @param {LocationClient} [options.client] - Optional client instance\n * @param {string} [options.title='Pick a Location'] - Dialog title\n * @param {number} [options.minChars=3] - Minimum characters before suggestions\n * @param {number} [options.debounceMs=200] - Typing debounce\n * @param {string} [options.placeholder='Search address'] - Input placeholder\n * @param {string} [options.confirmText='Select'] - Confirm button text\n * @param {number} [options.height=240] - Preview map height\n * @param {string} [options.tileLayer='osm'] - Tile layer\n * @returns {Promise<Object|null>} Selected details or null\n */\nexport async function showLocationPickerDialog({\n client = new LocationClient({ basePath: '/api' }),\n title = 'Pick a Location',\n minChars = 3,\n debounceMs = 200,\n placeholder = 'Search address',\n confirmText = 'Select',\n height = 240,\n tileLayer = 'osm'\n} = {}) {\n const view = new LocationPickerView({\n client,\n minChars,\n debounceMs,\n placeholder,\n height,\n tileLayer\n });\n\n const result = await Dialog.showDialog({\n title,\n body: view,\n size: 'md',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', dismiss: true, value: 'cancel' },\n { text: confirmText, class: 'btn-primary', value: 'ok' }\n ]\n });\n\n // If user confirmed, return the selected details; otherwise null\n if (result === 'ok') {\n return view.getSelected() || null;\n }\n return null;\n}\n\nexport default {\n showLocationDetailsDialog,\n showLocationPickerDialog\n};","/**\n * LocationPlugin - Form extension for address autocomplete and place details\n *\n * This plugin integrates with the core FormPlugins registry to:\n * - Optionally add a new field type \"address\" (rendered as a text input)\n * - Auto-wire autocomplete and details population for address fields\n *\n * Nothing is active unless you explicitly register this plugin.\n *\n * Usage:\n * import { registerLocationPlugin } from '@ext/map/location/LocationPlugin.js';\n *\n * // Register once at app startup\n * const unregister = registerLocationPlugin({\n * basePath: '/api', // optional prefix for location endpoints (works with Rest baseURL)\n * fieldTypeName: 'address',\n * registerFieldType: true,\n * mapping: {\n * address1: 'address1',\n * city: 'city',\n * state_code: 'state',\n * postal_code: 'postal_code',\n * country_code: 'country',\n * latitude: 'lat',\n * longitude: 'lng',\n * formatted_address: 'formatted_address',\n * place_id: 'place_id'\n * }\n * });\n *\n * // Later (optional):\n * // unregister();\n */\n\nimport { FormPlugins } from '@core/forms/FormPlugins.js';\nimport LocationClient from './LocationClient.js';\nimport { useLocationAutocomplete } from './useLocationAutocomplete.js';\n\nexport class LocationFormPlugin {\n /**\n * @param {Object} options\n * @param {string} [options.basePath] - API base path prefix (e.g., '/api') used with core Rest\n * @param {Object} [options.mapping] - Mapping from API address keys -> form field names\n * @param {boolean} [options.registerFieldType=true] - Register custom field type (address)\n * @param {string} [options.fieldTypeName='address'] - Field type name to register\n * @param {string} [options.attributeSelector='data-location'] - Attribute to opt-in on any text input (e.g., data-location=\"address\")\n * @param {number} [options.minChars=3] - Minimum characters before triggering autocomplete\n * @param {number} [options.debounceMs=200] - Typing debounce for autocomplete\n */\n constructor({\n basePath = '/api',\n mapping,\n registerFieldType = true,\n fieldTypeName = 'address',\n attributeSelector = 'data-location',\n minChars = 3,\n debounceMs = 200,\n\n // Browser autofill/autocomplete suppression (important for suggestion inputs)\n // Chrome often ignores autocomplete=\"off\" for address-like fields, so we default\n // to a more reliable value.\n suppressBrowserAutocomplete = true,\n autocompleteValue = 'new-password'\n } = {}) {\n this.id = 'location';\n this.client = new LocationClient({ basePath });\n\n this.mapping = mapping || {\n address1: 'address1',\n city: 'city',\n state_code: 'state',\n postal_code: 'postal_code',\n country_code: 'country',\n latitude: 'latitude',\n longitude: 'longitude',\n formatted_address: 'formatted_address',\n place_id: 'place_id'\n };\n\n this.fieldTypeName = fieldTypeName;\n this.attributeSelector = attributeSelector;\n this.minChars = minChars;\n this.debounceMs = debounceMs;\n\n this.suppressBrowserAutocomplete = suppressBrowserAutocomplete !== false;\n this.autocompleteValue = autocompleteValue || 'new-password';\n\n // Optional field type registration\n if (registerFieldType) {\n this.fieldTypes = {\n [this.fieldTypeName]: (builder, field) => this.renderAddressField(builder, field)\n };\n }\n }\n\n /**\n * Custom renderer for the \"address\" field type.\n * Leverages existing FormBuilder input rendering for consistency (text input).\n */\n renderAddressField(builder, field) {\n const suppressAttrs = this.suppressBrowserAutocomplete\n ? `autocomplete=\"${this.autocompleteValue}\" autocapitalize=\"off\" autocorrect=\"off\" spellcheck=\"false\" inputmode=\"search\"`\n : '';\n\n const f = {\n ...field,\n type: 'text',\n placeholder: field.placeholder || 'Start typing an address',\n attrs: this.mergeAttrs(\n field.attrs,\n `${this.attributeSelector}=\"address\" ${suppressAttrs} aria-autocomplete=\"list\" role=\"combobox\"`\n )\n };\n // Prefer builder.renderTextField if available, otherwise fallback to generic input\n if (typeof builder.renderTextField === 'function') {\n return builder.renderTextField(f);\n }\n if (typeof builder.renderInputField === 'function') {\n return builder.renderInputField(f, 'text');\n }\n // Minimal fallback if neither method exists (should not happen)\n const id = builder.getFieldId?.(f.name) || `field_${f.name}`;\n return `\n <div class=\"mojo-form-control\">\n ${f.label ? `<label for=\"${id}\" class=\"${builder.options?.labelClass || 'form-label'}\">${f.label}</label>` : ''}\n <input type=\"text\" id=\"${id}\" name=\"${f.name}\" class=\"${builder.options?.inputClass || 'form-control'}\"\n placeholder=\"${f.placeholder || ''}\" ${this.attributeSelector}=\"address\" ${suppressAttrs} />\n </div>\n `;\n }\n\n /**\n * Helper to merge existing attrs with an additional attribute string\n */\n mergeAttrs(existing, add) {\n const base = (existing || '').trim();\n const extra = (add || '').trim();\n if (!base) return extra;\n if (!extra) return base;\n return `${base} ${extra}`;\n }\n\n /**\n * Hook: called when FormView is initialized\n * You can read application config here if needed.\n */\n onFormViewInit(_formView) {\n // no-op by default\n }\n\n /**\n * Hook: called after FormView finished rendering and initializing components\n * This is a good spot to opt-in based on attributes (e.g., data-location=\"address\") on any text input.\n */\n onAfterRender(formView) {\n if (!formView?.element) return;\n\n try {\n const selector = `input[${this.attributeSelector}=\"address\"]`;\n const inputs = formView.element.querySelectorAll(selector);\n inputs.forEach((inputEl) => {\n // Ensure we don't double-bind if a field-level init already handled it\n if (inputEl.dataset && inputEl.dataset._locationBound === '1') return;\n\n // Enforce suppression on any opt-in input (not just our field type renderer)\n if (this.suppressBrowserAutocomplete) {\n try {\n inputEl.setAttribute('autocomplete', this.autocompleteValue);\n inputEl.setAttribute('autocapitalize', 'off');\n inputEl.setAttribute('autocorrect', 'off');\n inputEl.setAttribute('spellcheck', 'false');\n inputEl.setAttribute('inputmode', 'search');\n } catch (e) {\n // best-effort: some environments may block setting attributes\n }\n }\n\n let dispose;\n const rebind = () => {\n dispose = useLocationAutocomplete(formView, {\n client: this.client,\n field: inputEl,\n mapping: this.mapping,\n minChars: this.minChars,\n debounceMs: this.debounceMs,\n onSelect: (_details) => {\n // Prevent immediate re-open by removing listeners during debounce window\n try { inputEl.blur(); } catch (e) { /* best-effort */ }\n try { dispose && dispose(); } catch (e) { /* best-effort */ }\n setTimeout(() => {\n rebind();\n }, this.debounceMs + 50);\n }\n });\n inputEl.dataset._locationBound = '1';\n this._trackDisposer(formView, dispose);\n };\n rebind();\n });\n } catch (err) {\n // best-effort\n }\n }\n\n /**\n * Hook: called for each field element with its config after FormView initialization\n * If the field is our \"address\" type (or opt-in via attribute), attach autocomplete.\n */\n onFieldInit(formView, fieldEl, fieldConfig) {\n try {\n const isAddressType = fieldConfig?.type === this.fieldTypeName;\n const hasAttr = fieldEl?.getAttribute?.(this.attributeSelector) === 'address';\n\n if (isAddressType || hasAttr) {\n // Avoid double-binding\n if (fieldEl.dataset && fieldEl.dataset._locationBound === '1') return;\n\n // Enforce suppression on field-type and attribute-bound inputs\n if (this.suppressBrowserAutocomplete) {\n try {\n fieldEl.setAttribute('autocomplete', this.autocompleteValue);\n fieldEl.setAttribute('autocapitalize', 'off');\n fieldEl.setAttribute('autocorrect', 'off');\n fieldEl.setAttribute('spellcheck', 'false');\n fieldEl.setAttribute('inputmode', 'search');\n } catch (e) {\n // best-effort: some environments may block setting attributes\n }\n }\n\n let dispose;\n const rebind = () => {\n dispose = useLocationAutocomplete(formView, {\n client: this.client,\n field: fieldEl,\n mapping: this.mapping,\n minChars: this.minChars,\n debounceMs: this.debounceMs,\n onSelect: (_details) => {\n // Prevent immediate re-open by removing listeners during debounce window\n try { fieldEl.blur(); } catch (e) { /* best-effort */ }\n try { dispose && dispose(); } catch (e) { /* best-effort */ }\n setTimeout(() => {\n rebind();\n }, this.debounceMs + 50);\n }\n });\n\n fieldEl.dataset._locationBound = '1';\n this._trackDisposer(formView, dispose);\n };\n rebind();\n }\n } catch (err) {\n // best-effort\n }\n }\n\n /**\n * Hook: field change notification (no-op for now)\n */\n onFieldChange(_formView, _name, _value) {\n // Could add logic to validate or re-resolve coordinates based on user edits\n }\n\n /**\n * Track disposers for cleanup. If the FormView provides an event API, attach to destroy/before-destroy.\n */\n _trackDisposer(formView, dispose) {\n if (!dispose || typeof dispose !== 'function') return;\n if (!formView) return;\n\n if (!formView._locationDisposers) {\n Object.defineProperty(formView, '_locationDisposers', {\n configurable: true,\n enumerable: false,\n writable: true,\n value: []\n });\n }\n formView._locationDisposers.push(dispose);\n\n // Attempt to hook into a lifecycle if available\n // Many MOJO View classes expose an event emitter; if not, this is still fine (garbage-collected on page change).\n const tryBind = (eventName) => {\n if (typeof formView.on === 'function') {\n try {\n formView.on(eventName, () => {\n try {\n formView._locationDisposers?.forEach(fn => {\n try { fn(); } catch { /* ignore dispose errors */ }\n });\n } finally {\n formView._locationDisposers = [];\n }\n });\n return true;\n } catch { /* ignore */ }\n }\n return false;\n };\n\n // Bind to a best-effort lifecycle event if present\n if (!tryBind('before:destroy')) {\n tryBind('destroy');\n }\n }\n}\n\n/**\n * Register the location plugin with the core FormPlugins registry.\n * @param {ConstructorParameters<typeof LocationFormPlugin>[0]} options\n * @returns {Function} unregister function\n */\nexport function registerLocationPlugin(options = {}) {\n const plugin = new LocationFormPlugin(options);\n return FormPlugins.register(plugin);\n}\n\nexport default LocationFormPlugin;"],"names":["LocationClient","constructor","basePath","endpoints","this","String","sessionToken","validate","autocomplete","details","geocode","reverse","timezone","setAuthHeader","header","_authHeader","headers","extra","auth","h","Authorization","jsonGet","path","params","resp","rest","GET","fullPath","data","jsonPost","body","POST","_safeJson","res","json","validateAddress","address","query","opts","trim","length","success","size","count","_createSessionToken","input","session_token","result","placeDetails","place_id","id","q","pid","reverseGeocode","lat","lng","resetSessionToken","normalizeSuggestion","suggestion","description","main_text","secondary_text","types","crypto","randomUUID","Date","now","Math","random","toString","slice","useLocationAutocomplete","formView","client","field","dropdownClass","minChars","debounceMs","mapping","address1","city","state_code","postal_code","country_code","latitude","longitude","formatted_address","onSelect","element","console","warn","inputEl","querySelector","HTMLElement","dd","document","createElement","className","style","position","zIndex","display","background","border","borderRadius","boxShadow","padding","maxHeight","overflowY","minWidth","setAttribute","open","timer","suppress","placeDropdown","r","getBoundingClientRect","width","left","window","scrollX","top","bottom","scrollY","closeDropdown","innerHTML","parentNode","removeChild","async","handleInput","value","items","Array","isArray","map","x","appendChild","list","empty","color","textContent","forEach","item","idx","index","row","cursor","flexDirection","dataset","main","fontWeight","sub","fontSize","addEventListener","e","preventDefault","clearTimeout","dispatchEvent","Event","bubbles","blur","setTimeout","Object","entries","srcKey","formField","val","setFieldValue","err","targetEl","selectSuggestion","createRow","renderSuggestions","onInput","onFocus","onBlur","contains","activeElement","onWindowMove","onDocumentClick","target","removeEventListener","LocationDetailsView","View","height","tileLayer","super","Number","isFinite","_toNumber","hasCoords","_mapView","template","onInit","markers","popup","MapView","center","zoom","showLayerControl","containerId","addChild","v","n","LocationPickerView","placeholder","_selected","_timer","_suggestions","_elements","_previewView","getSelected","onAfterRender","root","gap","type","previewHost","_handleInput","_renderPreview","_renderSuggestions","_selectSuggestion","ph","render","fallback","onBeforeDestroy","LocationFormPlugin","registerFieldType","fieldTypeName","attributeSelector","suppressBrowserAutocomplete","autocompleteValue","fieldTypes","builder","renderAddressField","suppressAttrs","f","attrs","mergeAttrs","renderTextField","renderInputField","getFieldId","name","label","options","labelClass","inputClass","existing","add","base","onFormViewInit","_formView","selector","querySelectorAll","_locationBound","dispose","rebind","_details","_trackDisposer","onFieldInit","fieldEl","fieldConfig","isAddressType","hasAttr","getAttribute","onFieldChange","_name","_value","_locationDisposers","defineProperty","configurable","enumerable","writable","push","tryBind","eventName","on","fn","plugin","FormPlugins","register","title","resolved","error","message","view","Dialog","showDialog","buttons","text","class","dismiss","confirmText"],"mappings":"yTA4Be,MAAMA,eAQnB,WAAAC,EAAYC,SACVA,EAAW,OAAAC,UACXA,EAAY,CAAA,GACV,IACFC,KAAKF,SAAWG,OAAOH,GAAY,IAEnCE,KAAKE,aAAe,KAGpBF,KAAKD,UAAY,CACfI,SAAU,6BACVC,aAAc,gCACdC,QAAS,kCACTC,QAAS,4BACTC,QAAS,oCACTC,SAAU,wBACPT,EAIP,CAMA,aAAAU,CAAcC,GACZV,KAAKW,YAAcD,CACrB,CAOA,OAAAE,CAAQC,GACN,IAAIC,EAAO,KACX,GAAgC,mBAArBd,KAAKW,YACd,IAAMG,EAAOd,KAAKW,aAAe,CAAA,MAAUG,EAAO,IAAM,MAExDA,EAAOd,KAAKW,YAEd,MAAMI,EAAI,CAAE,eAAgB,sBAAwBF,GAAS,CAAA,GAE7D,OADIC,MAAQE,cAAgBF,GACrBC,CACT,CAOA,aAAME,CAAQC,EAAMC,GAClB,MAAMC,QAAaC,EAAAA,KAAKC,IAAItB,KAAKuB,SAASL,GAAOC,GAAU,IAE3D,OAAQC,QAAsB,IAAdA,EAAKI,KAAsBJ,EAAKI,KAAOJ,CACzD,CAOA,cAAMK,CAASP,EAAMQ,GACnB,MAAMN,QAAaC,OAAKM,KAAK3B,KAAKuB,SAASL,GAAOQ,GAAQ,GAAI,CAAA,EAAI,CAAA,GAElE,OAAQN,QAAsB,IAAdA,EAAKI,KAAsBJ,EAAKI,KAAOJ,CACzD,CAEA,QAAAG,CAASL,GACP,MAAO,GAAGlB,KAAKF,WAAWoB,GAC5B,CAEA,eAAMU,CAAUC,GACd,IACE,aAAaA,EAAIC,MACnB,CAAA,MACE,OAAO,IACT,CACF,CAiBA,eAAAC,CAAgBC,GACd,OAAOhC,KAAKyB,SAASzB,KAAKD,UAAUI,SAAU6B,EAChD,CASA,kBAAM5B,CAAa6B,EAAOC,EAAO,IAC/B,IAAKD,GAAyC,IAAhChC,OAAOgC,GAAOE,OAAOC,OACjC,MAAO,CAAEC,SAAS,EAAMb,KAAM,GAAIc,KAAM,EAAGC,MAAO,GAE/CvC,KAAKE,eACRF,KAAKE,aAAeF,KAAKwC,uBAE3B,MAAMrB,EAAS,CAAEsB,MAAOR,EAAOS,cAAe1C,KAAKE,gBAAiBgC,GAC9DS,QAAe3C,KAAKiB,QAAQjB,KAAKD,UAAUK,aAAce,GAK/D,OAHIwB,GAAUA,EAAOD,gBACnB1C,KAAKE,aAAeyC,EAAOD,eAEtBC,CACT,CASA,YAAAC,EAAaC,SAAEA,EAAAH,cAAUA,KAAeI,GAAO,CAAA,GAC7C,MAAMC,EAAI,CAAA,EACJC,EAAMH,GAAYC,GAAM,KAG9B,OAFIE,MAAOH,SAAWG,GAClBN,MAAiBA,cAAgBA,GAC9B1C,KAAKiB,QAAQjB,KAAKD,UAAUM,QAAS0C,EAC9C,CAOA,OAAAzC,CAAQ0B,GACN,OAAOhC,KAAKyB,SAASzB,KAAKD,UAAUO,QAAS,CAAE0B,WACjD,CASA,cAAAiB,EAAeC,IAAEA,EAAAC,IAAKA,IACpB,OAAOnD,KAAKiB,QAAQjB,KAAKD,UAAUQ,QAAS,CAAE2C,MAAKC,OACrD,CASA,QAAA3C,EAAS0C,IAAEA,EAAAC,IAAKA,IACd,OAAOnD,KAAKiB,QAAQjB,KAAKD,UAAUS,SAAU,CAAE0C,MAAKC,OACtD,CAKA,iBAAAC,GACEpD,KAAKE,aAAe,IACtB,CAMA,mBAAAmD,CAAoBC,GAClB,MAAO,CACLR,GAAIQ,GAAYR,IAAMQ,GAAYT,UAAY,KAC9CA,SAAUS,GAAYT,UAAYS,GAAYR,IAAM,KACpDS,YAAaD,GAAYC,aAAe,GACxCC,UAAWF,GAAYE,WAAa,GACpCC,eAAgBH,GAAYG,gBAAkB,GAC9CC,MAAOJ,GAAYI,OAAS,GAEhC,CAEA,mBAAAlB,GAEE,MAAsB,oBAAXmB,QAAuD,mBAAtBA,OAAOC,WAC1CD,OAAOC,aAET,GAAGC,KAAKC,SAASC,KAAKC,SAASC,SAAS,IAAIC,MAAM,IAC3D,EClMK,SAASC,EAAwBC,GAAUC,OAChDA,EAAAC,MACAA,EAAQ,WAAAC,cACRA,EAAgB,cAAAC,SAChBA,EAAW,EAAAC,WACXA,EAAa,IAAAC,QACbA,EAAU,CACRC,SAAU,WACVC,KAAM,OACNC,WAAY,QACZC,YAAa,cACbC,aAAc,UACdC,SAAU,WACVC,UAAW,YACXC,kBAAmB,oBACnBrC,SAAU,YACdsC,SACEA,GACE,IACF,IAAKf,IAAaA,EAASgB,QAEzB,OADAC,QAAQC,KAAK,kEACN,OAET,IAAKjB,GAAyC,mBAAxBA,EAAOjE,cAA8D,mBAAxBiE,EAAOzB,aAExE,OADAyC,QAAQC,KAAK,kHACN,OAIT,MAAMC,EAA4B,iBAAVjB,EACnBF,EAASgB,QAAQI,cAAc,eAAelB,SAAaA,MAAY,KACvEA,aAAiBmB,YAAcnB,EAAQ,KAE5C,IAAKiB,EAEH,MAAO,OAIT,MAAMG,EAAKC,SAASC,cAAc,OAClCF,EAAGG,UAAYtB,GAAiB,cAChCmB,EAAGI,MAAMC,SAAW,WACpBL,EAAGI,MAAME,OAAS,QAClBN,EAAGI,MAAMG,QAAU,OACnBP,EAAGI,MAAMI,WAAa,OACtBR,EAAGI,MAAMK,OAAS,oBAClBT,EAAGI,MAAMM,aAAe,MACxBV,EAAGI,MAAMO,UAAY,6BACrBX,EAAGI,MAAMQ,QAAU,QACnBZ,EAAGI,MAAMS,UAAY,QACrBb,EAAGI,MAAMU,UAAY,OACrBd,EAAGI,MAAMW,SAAW,QACpBf,EAAGgB,aAAa,OAAQ,WACxBhB,EAAGgB,aAAa,aAAc,uBAE9B,IAAIC,GAAO,EACPC,EAAQ,KACRC,GAAW,EAEf,SAASC,IACP,IAAKH,EAAM,OACX,MAAMI,EAAIxB,EAAQyB,wBAClBtB,EAAGI,MAAMW,SAAW,GAAGM,EAAEE,UACzBvB,EAAGI,MAAMoB,KAAO,GAAGH,EAAEG,KAAOC,OAAOC,YACnC1B,EAAGI,MAAMuB,IAAM,GAAGN,EAAEO,OAASH,OAAOI,QAAU,KAChD,CAWA,SAASC,IACPb,GAAO,EACPjB,EAAGI,MAAMG,QAAU,OACnBP,EAAG+B,UAAY,GACX/B,EAAGgC,YACLhC,EAAGgC,WAAWC,YAAYjC,EAE9B,CA+GAkC,eAAeC,IACb,MAAM9E,EAAIwC,EAAQuC,MAAM3F,OACxB,GAAIY,EAAEX,OAASoC,EACbgD,SAIF,IACE,MAAM3F,QAAYwC,EAAOjE,aAAa2C,GAEhCgF,GADOC,MAAMC,QAAQpG,GAAKL,MAAQK,EAAIL,KAAO,IAChC0G,IAAIC,IAAA,CACrBrF,GAAIqF,EAAErF,GACND,SAAUsF,EAAEtF,SACZU,YAAa4E,EAAE5E,YACfC,UAAW2E,EAAE3E,UACbC,eAAgB0E,EAAE1E,eAClBC,MAAOyE,EAAEzE,SA9IRiD,IACHA,GAAO,EACPjB,EAAGI,MAAMG,QAAU,QACnBN,SAASjE,KAAK0G,YAAY1C,GAC1BoB,KA6IAA,UA7FJc,eAAiCS,GAE/B,GADA3C,EAAG+B,UAAY,IACVY,GAAwB,IAAhBA,EAAKjG,OAAc,CAC9B,MAAMkG,EAAQ3C,SAASC,cAAc,OAKrC,OAJA0C,EAAMxC,MAAMQ,QAAU,WACtBgC,EAAMxC,MAAMyC,MAAQ,UACpBD,EAAME,YAAc,kBACpB9C,EAAG0C,YAAYE,EAEjB,CACAD,EAAKI,QAAQ,CAACC,EAAMC,IAAQjD,EAAG0C,YA7CjC,SAAmBM,EAAME,GACvB,MAAMC,EAAMlD,SAASC,cAAc,OACnCiD,EAAInC,aAAa,OAAQ,UACzBmC,EAAInC,aAAa,WAAY,MAC7BmC,EAAI/C,MAAMQ,QAAU,WACpBuC,EAAI/C,MAAMgD,OAAS,UACnBD,EAAI/C,MAAMG,QAAU,OACpB4C,EAAI/C,MAAMiD,cAAgB,SAC1BF,EAAIG,QAAQJ,MAAQ3I,OAAO2I,GAE3B,MAAMK,EAAOtD,SAASC,cAAc,OACpCqD,EAAKnD,MAAMoD,WAAa,MACxBD,EAAKnD,MAAMyC,MAAQ,UACnBU,EAAKT,YAAcE,EAAKlF,WAAakF,EAAKnF,aAAe,GAEzD,MAAM4F,EAAMxD,SAASC,cAAc,OAiBnC,OAhBAuD,EAAIrD,MAAMsD,SAAW,OACrBD,EAAIrD,MAAMyC,MAAQ,UAClBY,EAAIX,YAAcE,EAAKjF,gBAAkB,GAEzCoF,EAAIT,YAAYa,GACZE,EAAIX,aAAaK,EAAIT,YAAYe,GAErCN,EAAIQ,iBAAiB,aAAc,KAAQR,EAAI/C,MAAMI,WAAa,YAClE2C,EAAIQ,iBAAiB,aAAc,KAAQR,EAAI/C,MAAMI,WAAa,gBAElE2C,EAAIQ,iBAAiB,YAAcC,IAEjCA,EAAEC,iBAoBN3B,eAAgCc,GAC9B,IACE,MAAM5F,EAAK4F,EAAK7F,UAAY6F,EAAK5F,GACjC,IAAIzC,EAAU,KACd,GAAIyC,EAAI,CACN,MAAMjB,QAAYwC,EAAOzB,aAAa,CAAEC,SAAUC,EAAIA,KAAIJ,cAAe2B,EAAOnE,eAChFG,EAAUwB,GAAKG,SAAW,IAC5B,CAIA6E,GAAW,EACX2C,aAAa5C,GACTvG,GAAS6E,mBACXK,EAAQuC,MAAQzH,EAAQ6E,kBAExBK,EAAQkE,cAAc,IAAIC,MAAM,QAAS,CAAEC,SAAS,MAC3CjB,EAAKnF,cACdgC,EAAQuC,MAAQY,EAAKnF,YACrBgC,EAAQkE,cAAc,IAAIC,MAAM,QAAS,CAAEC,SAAS,MAGtD,IAAMpE,EAAQqE,MAAQ,CAAA,MAAS,CAC/BpC,IAEAqC,WAAW,KAAQhD,GAAW,GAAUpC,EAAa,IAGjDpE,GAAWqE,GAA8B,iBAAZA,GAC/BoF,OAAOC,QAAQrF,GAAS+D,QAAQ,EAAEuB,EAAQC,MACxC,IAAKA,EAAW,OAChB,MAAMC,EAAM7J,EAAQ2J,GACpB,GAAIE,QAAmC,CAErC,IACwC,mBAA3B9F,EAAS+F,eAClB/F,EAAS+F,cAAcF,EAAWhK,OAAOiK,GAE7C,OAASE,GAET,CACA,MAAMC,EAAWjG,EAASgB,QAAQI,cAAc,eAAeyE,SAAiBA,qBAA6BA,qBAA6BA,OACtII,IACFA,EAASvC,MAAQ7H,OAAOiK,GACxBG,EAASZ,cAAc,IAAIC,MAAM,QAAS,CAAEC,SAAS,KACrDU,EAASZ,cAAc,IAAIC,MAAM,SAAU,CAAEC,SAAS,KAE1D,IAIoB,mBAAbxE,GACTA,EAAS9E,GAAW,KAExB,OAAS+J,GACP/E,QAAQC,KAAK,gDAAiD8E,EAChE,CAAA,QACE5C,GACF,CACF,CA9EI8C,CAAiB5B,KAGZG,CACT,CAY6C0B,CAAU7B,EAAMC,IAC7D,CAmFU6B,CAAkBzC,EAC1B,OAASqC,GACP/E,QAAQC,KAAK,gDAAiD8E,GAC9D5C,GACF,CACF,CAEA,SAASiD,IACH5D,IACJ2C,aAAa5C,GACbA,EAAQiD,WAAWhC,EAAapD,GAClC,CAEA,SAASiG,IACH7D,GACAtB,EAAQuC,MAAM3F,OAAOC,QAAUoC,GACjCiG,GAEJ,CAEA,SAASE,IAEPd,WAAW,KACJnE,EAAGkF,SAASjF,SAASkF,gBACxBrD,KAED,IACL,CAEA,SAASsD,IACHnE,GAAMG,GACZ,CAEA,SAASiE,EAAgBzB,GAClB5D,EAAGkF,SAAStB,EAAE0B,SAAW1B,EAAE0B,SAAWzF,GACzCiC,GAEJ,CAWA,OARAjC,EAAQ8D,iBAAiB,QAASoB,GAClClF,EAAQ8D,iBAAiB,QAASqB,GAClCnF,EAAQ8D,iBAAiB,OAAQsB,GACjCxD,OAAOkC,iBAAiB,SAAUyB,GAClC3D,OAAOkC,iBAAiB,SAAUyB,GAAc,GAChDnF,SAAS0D,iBAAiB,QAAS0B,GAG5B,WACLvB,aAAa5C,GACb,IAAMrB,EAAQ0F,oBAAoB,QAASR,EAAU,OAASnB,GAAkB,CAChF,IAAM/D,EAAQ0F,oBAAoB,QAASP,EAAU,OAASpB,GAAkB,CAChF,IAAM/D,EAAQ0F,oBAAoB,OAAQN,EAAS,OAASrB,GAAkB,CAC9E,IAAMnC,OAAO8D,oBAAoB,SAAUH,EAAe,OAASxB,GAAkB,CACrF,IAAMnC,OAAO8D,oBAAoB,SAAUH,GAAc,EAAO,OAASxB,GAAkB,CAC3F,IAAM3D,SAASsF,oBAAoB,QAASF,EAAkB,OAASzB,GAAkB,CACzF,IAAM9B,GAAiB,OAAS8B,GAAkB,CACpD,CACF,CCpSe,MAAM4B,4BAA4BC,EAAAA,KAO/C,WAAAtL,EAAYQ,QAAEA,EAAU,UAAI+K,EAAS,IAAAC,UAAKA,EAAY,OAAU,IAC9DC,MAAM,CACJzF,UAAW,0BAGb7F,KAAKK,QAAUA,GAAW,CAAA,EAC1BL,KAAKoL,OAASG,OAAOC,SAASJ,GAAUA,EAAS,IACjDpL,KAAKqL,UAAYA,GAAa,MAG9BrL,KAAKkF,kBAAoBlF,KAAKK,QAAQ6E,mBAAqB,GAC3DlF,KAAK6C,SAAW7C,KAAKK,QAAQwC,UAAY,GACzC7C,KAAKgF,SACHhF,KAAKyL,UAAUzL,KAAKK,QAAQ2E,UAAYhF,KAAKK,QAAQ6C,KAAO,MAC9DlD,KAAKiF,UACHjF,KAAKyL,UAAUzL,KAAKK,QAAQ4E,WAAajF,KAAKK,QAAQ8C,KAAO,MAE/DnD,KAAK0L,UAAYH,OAAOC,SAASxL,KAAKgF,WAAauG,OAAOC,SAASxL,KAAKiF,WAGxEjF,KAAK2L,SAAW,KAGhB3L,KAAK4L,SAAW,80BA2BlB,CAEA,YAAMC,GAEJ,GAAI7L,KAAK0L,UAAW,CAClB,MAAMI,EAAU,CAAC,CACf5I,IAAKlD,KAAKgF,SACV7B,IAAKnD,KAAKiF,UACV8G,MAAO/L,KAAKkF,mBAAqB,KAGnClF,KAAK2L,SAAW,IAAIK,UAAQ,CAC1BF,UACAG,OAAQ,CAACjM,KAAKgF,SAAUhF,KAAKiF,WAC7BiH,KAAM,GACNd,OAAQpL,KAAKoL,OACbC,UAAWrL,KAAKqL,UAChBc,kBAAkB,EAClBC,YAAa,QAGfpM,KAAKqM,SAASrM,KAAK2L,SACrB,CACF,CAEA,SAAAF,CAAUa,GACR,GAAIA,SAAuC,KAANA,EAAU,OAAO,KACtD,MAAMC,EAAIhB,OAAOe,GACjB,OAAOf,OAAOC,SAASe,GAAKA,EAAI,IAClC,EClCF,MAAMC,2BAA2BrB,EAAAA,KAC/B,WAAAtL,EAAYwE,OACVA,EAAAG,SACAA,EAAW,EAAAC,WACXA,EAAa,IAAAgI,YACbA,EAAc,iBAAArB,OACdA,EAAS,IAAAC,UACTA,EAAY,OACV,IACFC,MAAM,CACJzF,UAAW,uBACX+F,SAAU,kDAEZ5L,KAAKqE,OAASA,EACdrE,KAAKwE,SAAWA,EAChBxE,KAAKyE,WAAaA,EAClBzE,KAAKyM,YAAcA,EACnBzM,KAAKoL,OAASA,EACdpL,KAAKqL,UAAYA,EAEjBrL,KAAK0M,UAAY,KACjB1M,KAAK2M,OAAS,KACd3M,KAAK4M,aAAe,GACpB5M,KAAK6M,UAAY,GACjB7M,KAAK8M,aAAe,IACtB,CAEA,WAAAC,GACE,OAAO/M,KAAK0M,SACd,CAEA,mBAAMM,GAEJ,MAAMC,EAAOtH,SAASC,cAAc,OACpCqH,EAAKnH,MAAMG,QAAU,OACrBgH,EAAKnH,MAAMoH,IAAM,OAGjB,MAAMzK,EAAQkD,SAASC,cAAc,SACrCnD,EAAM0K,KAAO,OACb1K,EAAMoD,UAAY,eAClBpD,EAAMgK,YAAczM,KAAKyM,aAAe,iBACxChK,EAAMiE,aAAa,aAAc,kBACjCuG,EAAK7E,YAAY3F,GAGjB,MAAMiD,EAAKC,SAASC,cAAc,OAClCF,EAAGI,MAAMK,OAAS,oBAClBT,EAAGI,MAAMM,aAAe,MACxBV,EAAGI,MAAMI,WAAa,OACtBR,EAAGI,MAAMO,UAAY,6BACrBX,EAAGI,MAAMS,UAAY,QACrBb,EAAGI,MAAMU,UAAY,OACrBd,EAAGI,MAAMG,QAAU,OACnBgH,EAAK7E,YAAY1C,GAGjB,MAAM0H,EAAczH,SAASC,cAAc,OAC3CqH,EAAK7E,YAAYgF,GAGjBpN,KAAKoF,QAAQgD,YAAY6E,GAGzBjN,KAAK6M,UAAY,CAAEI,OAAMxK,QAAOiD,KAAI0H,eAGpC3K,EAAM4G,iBAAiB,QAAS,KAC9BG,aAAaxJ,KAAK2M,QAClB3M,KAAK2M,OAAS9C,WAAW,IAAM7J,KAAKqN,eAAgBrN,KAAKyE,cAG3DhC,EAAM4G,iBAAiB,QAAS,MACzB5G,EAAMqF,OAAS,IAAI3F,OAAOC,QAAUpC,KAAKwE,UAC5CxE,KAAKqN,uBAKHrN,KAAKsN,eAAe,KAC5B,CAEA,kBAAMD,GACJ,MAAMtK,GAAK/C,KAAK6M,UAAUpK,MAAMqF,OAAS,IAAI3F,OAC7C,GAAIY,EAAEX,OAASpC,KAAKwE,SAGlB,OAFAxE,KAAK6M,UAAUnH,GAAGI,MAAMG,QAAU,YAClCjG,KAAK6M,UAAUnH,GAAG+B,UAAY,IAIhC,IACE,MAAM5F,QAAY7B,KAAKqE,OAAOjE,aAAa2C,GACrCgF,EAAQC,MAAMC,QAAQpG,GAAKL,MAAQK,EAAIL,KAAO,GACpDxB,KAAK4M,aAAe7E,EAAMG,IAAIC,IAAA,CAC5BrF,GAAIqF,EAAErF,GACND,SAAUsF,EAAEtF,SACZU,YAAa4E,EAAE5E,YACfC,UAAW2E,EAAE3E,UACbC,eAAgB0E,EAAE1E,eAClBC,MAAOyE,EAAEzE,eAEL1D,KAAKuN,oBACb,OAASnD,GAEPpK,KAAK4M,aAAe,SACd5M,KAAKuN,oBACb,CACF,CAEA,wBAAMA,GACJ,MAAM7H,EAAK1F,KAAK6M,UAAUnH,GAG1B,GAFAA,EAAG+B,UAAY,IAEVzH,KAAK4M,aAAaxK,OAAQ,CAC7B,MAAMkG,EAAQ3C,SAASC,cAAc,OAMrC,OALA0C,EAAMxC,MAAMQ,QAAU,WACtBgC,EAAMxC,MAAMyC,MAAQ,UACpBD,EAAME,YAAc,aACpB9C,EAAG0C,YAAYE,QACf5C,EAAGI,MAAMG,QAAU,QAErB,CAEAjG,KAAK4M,aAAanE,QAAQ,CAACC,EAAMC,KAC/B,MAAME,EAAMlD,SAASC,cAAc,OACnCiD,EAAI/C,MAAMQ,QAAU,WACpBuC,EAAI/C,MAAMgD,OAAS,UACnBD,EAAI/C,MAAMG,QAAU,OACpB4C,EAAI/C,MAAMiD,cAAgB,SAC1BF,EAAIQ,iBAAiB,aAAc,KAAQR,EAAI/C,MAAMI,WAAa,YAClE2C,EAAIQ,iBAAiB,aAAc,KAAQR,EAAI/C,MAAMI,WAAa,gBAElE,MAAM+C,EAAOtD,SAASC,cAAc,OACpCqD,EAAKnD,MAAMoD,WAAa,MACxBD,EAAKnD,MAAMyC,MAAQ,UACnBU,EAAKT,YAAcE,EAAKlF,WAAakF,EAAKnF,aAAe,GAEzD,MAAM4F,EAAMxD,SAASC,cAAc,OACnCuD,EAAIrD,MAAMsD,SAAW,OACrBD,EAAIrD,MAAMyC,MAAQ,UAClBY,EAAIX,YAAcE,EAAKjF,gBAAkB,GAEzCoF,EAAIT,YAAYa,GACZE,EAAIX,aAAaK,EAAIT,YAAYe,GAErCN,EAAIQ,iBAAiB,YAAcC,IACjCA,EAAEC,iBACFvJ,KAAKwN,kBAAkB9E,KAGzBhD,EAAG0C,YAAYS,KAGjBnD,EAAGI,MAAMG,QAAU,OACrB,CAEA,uBAAMuH,CAAkB9E,GACtB,IACE,MAAM5F,EAAK4F,EAAK7F,UAAY6F,EAAK5F,GACjC,IAAKA,EAAI,OAET,MAAMjB,QAAY7B,KAAKqE,OAAOzB,aAAa,CAAEC,SAAUC,EAAIA,KAAIJ,cAAe1C,KAAKqE,OAAOnE,eACpFG,EAAUwB,GAAKG,SAAW,KAG5B3B,GAAS6E,kBACXlF,KAAK6M,UAAUpK,MAAMqF,MAAQzH,EAAQ6E,kBAC5BwD,EAAKnF,cACdvD,KAAK6M,UAAUpK,MAAMqF,MAAQY,EAAKnF,aAGpCvD,KAAK0M,UAAYrM,GAAW,KAG5BL,KAAK6M,UAAUnH,GAAGI,MAAMG,QAAU,OAClCjG,KAAK6M,UAAUnH,GAAG+B,UAAY,SAGxBzH,KAAKsN,eAAetN,KAAK0M,UACjC,OAAStC,GAET,CACF,CAEA,oBAAMkD,CAAejN,GAKnB,GAHAL,KAAK6M,UAAUO,YAAY3F,UAAY,IAGlCpH,EAAS,CACZ,MAAMoN,EAAK9H,SAASC,cAAc,OAOlC,OANA6H,EAAG3H,MAAMK,OAAS,qBAClBsH,EAAG3H,MAAMM,aAAe,MACxBqH,EAAG3H,MAAMQ,QAAU,OACnBmH,EAAG3H,MAAMyC,MAAQ,UACjBkF,EAAGjF,YAAc,4BACjBxI,KAAK6M,UAAUO,YAAYhF,YAAYqF,EAEzC,CAGA,IACEzN,KAAK8M,aAAe,IAAI5B,oBAAoB,CAC1C7K,UACA+K,OAAQpL,KAAKoL,OACbC,UAAWrL,KAAKqL,kBAEZrL,KAAK8M,aAAaY,QAAO,EAAM1N,KAAK6M,UAAUO,YACtD,CAAA,MACE,MAAMO,EAAWhI,SAASC,cAAc,OACxC+H,EAAS7H,MAAMK,OAAS,qBACxBwH,EAAS7H,MAAMM,aAAe,MAC9BuH,EAAS7H,MAAMQ,QAAU,OACzBqH,EAASnF,YAAcnI,GAAS6E,mBAAqB,oBACrDlF,KAAK6M,UAAUO,YAAYhF,YAAYuF,EACzC,CACF,CAEA,qBAAMC,GACJpE,aAAaxJ,KAAK2M,cACZrB,MAAMsC,iBACd,EC9PK,MAAMC,mBAWX,WAAAhO,EAAYC,SACVA,EAAW,OAAA4E,QACXA,EAAAoJ,kBACAA,GAAoB,EAAAC,cACpBA,EAAgB,UAAAC,kBAChBA,EAAoB,gBAAAxJ,SACpBA,EAAW,EAAAC,WACXA,EAAa,IAAAwJ,4BAKbA,GAA8B,EAAAC,kBAC9BA,EAAoB,gBAClB,IACFlO,KAAK8C,GAAK,WACV9C,KAAKqE,OAAS,IAAIzE,eAAe,CAAEE,aAEnCE,KAAK0E,QAAUA,GAAW,CACxBC,SAAU,WACVC,KAAM,OACNC,WAAY,QACZC,YAAa,cACbC,aAAc,UACdC,SAAU,WACVC,UAAW,YACXC,kBAAmB,oBACnBrC,SAAU,YAGZ7C,KAAK+N,cAAgBA,EACrB/N,KAAKgO,kBAAoBA,EACzBhO,KAAKwE,SAAWA,EAChBxE,KAAKyE,WAAaA,EAElBzE,KAAKiO,6BAA8D,IAAhCA,EACnCjO,KAAKkO,kBAAoBA,GAAqB,eAG1CJ,IACF9N,KAAKmO,WAAa,CAChB,CAACnO,KAAK+N,eAAgB,CAACK,EAAS9J,IAAUtE,KAAKqO,mBAAmBD,EAAS9J,IAGjF,CAMA,kBAAA+J,CAAmBD,EAAS9J,GAC1B,MAAMgK,EAAgBtO,KAAKiO,4BACvB,iBAAiBjO,KAAKkO,kGACtB,GAEEK,EAAI,IACLjK,EACH6I,KAAM,OACNV,YAAanI,EAAMmI,aAAe,0BAClC+B,MAAOxO,KAAKyO,WACVnK,EAAMkK,MACN,GAAGxO,KAAKgO,+BAA+BM,+CAI3C,GAAuC,mBAA5BF,EAAQM,gBACjB,OAAON,EAAQM,gBAAgBH,GAEjC,GAAwC,mBAA7BH,EAAQO,iBACjB,OAAOP,EAAQO,iBAAiBJ,EAAG,QAGrC,MAAMzL,EAAKsL,EAAQQ,aAAaL,EAAEM,OAAS,SAASN,EAAEM,OACtD,MAAO,oDAEDN,EAAEO,MAAQ,eAAehM,aAAcsL,EAAQW,SAASC,YAAc,iBAAiBT,EAAEO,gBAAkB,sCACpFhM,YAAayL,EAAEM,gBAAgBT,EAAQW,SAASE,YAAc,gDACjEV,EAAE9B,aAAe,OAAOzM,KAAKgO,+BAA+BM,0BAGxF,CAKA,UAAAG,CAAWS,EAAUC,GACnB,MAAMC,GAAQF,GAAY,IAAI/M,OACxBtB,GAASsO,GAAO,IAAIhN,OAC1B,OAAKiN,EACAvO,EACE,GAAGuO,KAAQvO,IADCuO,EADDvO,CAGpB,CAMA,cAAAwO,CAAeC,GAEf,CAMA,aAAAtC,CAAc5I,GACZ,GAAKA,GAAUgB,QAEf,IACE,MAAMmK,EAAW,SAASvP,KAAKgO,+BAChB5J,EAASgB,QAAQoK,iBAAiBD,GAC1C9G,QAASlD,IAEd,GAAIA,EAAQyD,SAA8C,MAAnCzD,EAAQyD,QAAQyG,eAAwB,OAG/D,GAAIzP,KAAKiO,4BACP,IACE1I,EAAQmB,aAAa,eAAgB1G,KAAKkO,mBAC1C3I,EAAQmB,aAAa,iBAAkB,OACvCnB,EAAQmB,aAAa,cAAe,OACpCnB,EAAQmB,aAAa,aAAc,SACnCnB,EAAQmB,aAAa,YAAa,SACpC,OAAS4C,GAET,CAGF,IAAIoG,EACJ,MAAMC,EAAS,KACbD,EAAUvL,EAAwBC,EAAU,CAC1CC,OAAQrE,KAAKqE,OACbC,MAAOiB,EACPb,QAAS1E,KAAK0E,QACdF,SAAUxE,KAAKwE,SACfC,WAAYzE,KAAKyE,WACjBU,SAAWyK,IAET,IAAMrK,EAAQqE,MAAQ,OAASN,GAAuB,CACtD,IAAMoG,GAAWA,GAAW,OAASpG,GAAuB,CAC5DO,WAAW,KACT8F,KACC3P,KAAKyE,WAAa,OAGzBc,EAAQyD,QAAQyG,eAAiB,IACjCzP,KAAK6P,eAAezL,EAAUsL,IAEhCC,KAEJ,OAASvF,GAET,CACF,CAMA,WAAA0F,CAAY1L,EAAU2L,EAASC,GAC7B,IACE,MAAMC,EAAgBD,GAAa7C,OAASnN,KAAK+N,cAC3CmC,EAA8D,YAApDH,GAASI,eAAenQ,KAAKgO,mBAE7C,GAAIiC,GAAiBC,EAAS,CAE5B,GAAIH,EAAQ/G,SAA8C,MAAnC+G,EAAQ/G,QAAQyG,eAAwB,OAG/D,GAAIzP,KAAKiO,4BACP,IACE8B,EAAQrJ,aAAa,eAAgB1G,KAAKkO,mBAC1C6B,EAAQrJ,aAAa,iBAAkB,OACvCqJ,EAAQrJ,aAAa,cAAe,OACpCqJ,EAAQrJ,aAAa,aAAc,SACnCqJ,EAAQrJ,aAAa,YAAa,SACpC,OAAS4C,GAET,CAGF,IAAIoG,EACJ,MAAMC,EAAS,KACbD,EAAUvL,EAAwBC,EAAU,CAC1CC,OAAQrE,KAAKqE,OACbC,MAAOyL,EACPrL,QAAS1E,KAAK0E,QACdF,SAAUxE,KAAKwE,SACfC,WAAYzE,KAAKyE,WACjBU,SAAWyK,IAET,IAAMG,EAAQnG,MAAQ,OAASN,GAAuB,CACtD,IAAMoG,GAAWA,GAAW,OAASpG,GAAuB,CAC5DO,WAAW,KACT8F,KACC3P,KAAKyE,WAAa,OAIzBsL,EAAQ/G,QAAQyG,eAAiB,IACjCzP,KAAK6P,eAAezL,EAAUsL,IAEhCC,GACF,CACF,OAASvF,GAET,CACF,CAKA,aAAAgG,CAAcd,EAAWe,EAAOC,GAEhC,CAKA,cAAAT,CAAezL,EAAUsL,GACvB,IAAKA,GAA8B,mBAAZA,EAAwB,OAC/C,IAAKtL,EAAU,OAEVA,EAASmM,oBACZzG,OAAO0G,eAAepM,EAAU,qBAAsB,CACpDqM,cAAc,EACdC,YAAY,EACZC,UAAU,EACV7I,MAAO,KAGX1D,EAASmM,mBAAmBK,KAAKlB,GAIjC,MAAMmB,EAAWC,IACf,GAA2B,mBAAhB1M,EAAS2M,GAClB,IAUE,OATA3M,EAAS2M,GAAGD,EAAW,KACrB,IACE1M,EAASmM,oBAAoB9H,QAAQuI,IACnC,IAAMA,GAAM,CAAA,MAAsC,GAEtD,CAAA,QACE5M,EAASmM,mBAAqB,EAChC,KAEK,CACT,CAAA,MAAuB,CAEzB,OAAO,GAIJM,EAAQ,mBACXA,EAAQ,UAEZ,mWAQK,SAAgC9B,EAAU,IAC/C,MAAMkC,EAAS,IAAIpD,mBAAmBkB,GACtC,OAAOmC,EAAAA,YAAYC,SAASF,EAC9B,oCDlSOrJ,gBAAyCvD,OAC9CA,EAAS,IAAIzE,eAAe,CAAEE,SAAU,SAAQO,QAChDA,EAAU,KAAAwC,SACVA,EAAW,KAAAC,GACXA,EAAK,KAAAsO,MACLA,EAAQ,mBAAAhG,OACRA,EAAS,IAAAC,UACTA,EAAY,OACV,IACF,IAAIgG,EAAWhR,EAEf,IAAKgR,IAAaxO,GAAYC,GAC5B,IACE,MAAMjB,QAAYwC,EAAOzB,aAAa,CAAEC,WAAUC,KAAIJ,cAAe2B,EAAOnE,eAC5EmR,EAAWxP,GAAKG,SAAW,IAC7B,OAASoI,GAEPiH,EAAW,CACTnM,kBAAmB,+BACnBoM,MAAOlH,GAAKmH,SAAW,gBAE3B,CAGF,MAAMC,EAAO,IAAItG,oBAAoB,CACnC7K,QAASgR,GAAY,CAAA,EACrBjG,SACAC,cAGF,OAAOoG,EAAAA,QAAOC,WAAW,CACvBN,QACA1P,KAAM8P,EACNlP,KAAM,KACNqP,QAAS,CAAC,CAAEC,KAAM,QAASC,MAAO,gBAAiBC,SAAS,EAAMhK,MAAO,WAE7E,mCAuPOF,gBAAwCvD,OAC7CA,EAAS,IAAIzE,eAAe,CAAEE,SAAU,SAAQsR,MAChDA,EAAQ,kBAAA5M,SACRA,EAAW,EAAAC,WACXA,EAAa,IAAAgI,YACbA,EAAc,iBAAAsF,YACdA,EAAc,SAAA3G,OACdA,EAAS,IAAAC,UACTA,EAAY,OACV,IACF,MAAMmG,EAAO,IAAIhF,mBAAmB,CAClCnI,SACAG,WACAC,aACAgI,cACArB,SACAC,cAcF,MAAe,aAXMoG,EAAAA,QAAOC,WAAW,CACrCN,QACA1P,KAAM8P,EACNlP,KAAM,KACNqP,QAAS,CACL,CAAEC,KAAM,SAAUC,MAAO,gBAAiBC,SAAS,EAAMhK,MAAO,UAChE,CAAE8J,KAAMG,EAAaF,MAAO,cAAe/J,MAAO,UAM/C0J,EAAKzE,eAEP,IACT"}
1
+ {"version":3,"file":"map.cjs.js","sources":["../src/extensions/map/location/LocationClient.js","../src/extensions/map/location/useLocationAutocomplete.js","../src/extensions/map/location/LocationDetailsView.js","../src/extensions/map/location/LocationDialogs.js","../src/extensions/map/location/LocationPlugin.js"],"sourcesContent":["/**\n * LocationClient - minimal client for Location & Address API\n *\n * Endpoints (default paths):\n * - POST /location/address/validate\n * - GET /location/address/suggestions ?input=...&session_token=...\n * - GET /location/address/place-details ?place_id=...&session_token=...\n * - POST /location/address/geocode\n * - GET /location/address/reverse-geocode ?lat=...&lng=...\n * - GET /location/timezone ?lat=...&lng=...\n *\n * This client is framework-friendly and can be used anywhere in a MOJO app.\n *\n * Examples:\n *\n * // Zero-config defaults (uses basePath '/api')\n * import LocationClient from '@ext/map/location/LocationClient.js';\n * const loc = new LocationClient();\n * const res = await loc.autocomplete('1600 Amphitheatre');\n * const details = await loc.placeDetails({ place_id: res?.data?.[0]?.place_id });\n *\n * // Custom base path (still works with core Rest base URL configuration)\n * const loc2 = new LocationClient({ basePath: '/api' });\n *\n * // Optional auth header (if your API requires auth)\n * loc2.setAuthHeader(() => `Bearer ${token}`);\n */\nimport rest from '@core/Rest.js';\nexport default class LocationClient {\n /**\n * @param {Object} options\n * @param {string} [options.basePath='/api'] - API base path prefix (e.g., '/api')\n * @param {string|(() => string|null)} [options.authHeader] - Authorization header string or function returning one\n * @param {Object} [options.endpoints] - Override endpoint paths\n * @param {Function} [options.fetchImpl] - Custom fetch implementation, defaults to global fetch\n */\n constructor({\n basePath = '/api',\n endpoints = {}\n } = {}) {\n this.basePath = String(basePath || '');\n\n this.sessionToken = null; // used for autocomplete session cost optimization\n\n // Default endpoint paths (relative to base path)\n this.endpoints = {\n validate: '/location/address/validate',\n autocomplete: '/location/address/suggestions',\n details: '/location/address/place-details',\n geocode: '/location/address/geocode',\n reverse: '/location/address/reverse-geocode',\n timezone: '/location/timezone',\n ...endpoints\n };\n\n\n }\n\n /**\n * Optional helper: supply or change auth header at runtime\n * @param {string|(() => string|null)} header\n */\n setAuthHeader(header) {\n this._authHeader = header;\n }\n\n /**\n * Compute headers for a request.\n * @param {Object} [extra]\n * @returns {Record<string,string>}\n */\n headers(extra) {\n let auth = null;\n if (typeof this._authHeader === 'function') {\n try { auth = this._authHeader(); } catch { auth = null; }\n } else {\n auth = this._authHeader;\n }\n const h = { 'Content-Type': 'application/json', ...(extra || {}) };\n if (auth) h.Authorization = auth;\n return h;\n }\n\n /**\n * Perform JSON GET with query params\n * @param {string} path - relative path starting with '/'\n * @param {Record<string, any>} [params]\n */\n async jsonGet(path, params) {\n const resp = await rest.GET(this.fullPath(path), params || {});\n // Unwrap Rest wrapper to return server JSON body (spec shape)\n return (resp && resp.data !== undefined) ? resp.data : resp;\n }\n\n /**\n * Perform JSON POST\n * @param {string} path - relative path starting with '/'\n * @param {any} body\n */\n async jsonPost(path, body) {\n const resp = await rest.POST(this.fullPath(path), body ?? {}, {}, {});\n // Unwrap Rest wrapper to return server JSON body (spec shape)\n return (resp && resp.data !== undefined) ? resp.data : resp;\n }\n\n fullPath(path) {\n return `${this.basePath}${path}`;\n }\n\n async _safeJson(res) {\n try {\n return await res.json();\n } catch {\n return null;\n }\n }\n\n // ----------------------------\n // API Methods\n // ----------------------------\n\n /**\n * Address Validation\n * @param {Object} address\n * @param {string} address.address1\n * @param {string} [address.address2]\n * @param {string} address.city\n * @param {string} address.state\n * @param {string} address.postal_code\n * @param {string} [address.provider] - e.g., 'usps' (if API supports provider selection)\n * @returns {Promise<any>} API response\n */\n validateAddress(address) {\n return this.jsonPost(this.endpoints.validate, address);\n }\n\n /**\n * Address Autocomplete (GET)\n * Maintains a session token for repeated queries to optimize provider cost.\n * @param {string} query\n * @param {Object} [opts] - e.g., { country, state, ... }\n * @returns {Promise<any>} { success, session_token, data: [{ id, place_id, description, ... }], ... }\n */\n async autocomplete(query, opts = {}) {\n if (!query || String(query).trim().length === 0) {\n return { success: true, data: [], size: 0, count: 0 };\n }\n if (!this.sessionToken) {\n this.sessionToken = this._createSessionToken();\n }\n const params = { input: query, session_token: this.sessionToken, ...opts };\n const result = await this.jsonGet(this.endpoints.autocomplete, params);\n // Persist returned session_token for subsequent calls if provided by API\n if (result && result.session_token) {\n this.sessionToken = result.session_token;\n }\n return result;\n }\n\n /**\n * Place Details (GET)\n * @param {Object} params\n * @param {string} [params.place_id]\n * @param {string} [params.id]\n * @returns {Promise<any>} { success, address: { formatted_address, latitude, longitude, ... } }\n */\n placeDetails({ place_id, session_token, id } = {}) {\n const q = {};\n const pid = place_id || id || null;\n if (pid) q.place_id = pid;\n if (session_token) q.session_token = session_token;\n return this.jsonGet(this.endpoints.details, q);\n }\n\n /**\n * Geocode (POST)\n * @param {string|Object} address - string or object { address1, city, state, postal_code }\n * @returns {Promise<any>} { success, latitude, longitude, formatted_address, place_id, address_components... }\n */\n geocode(address) {\n return this.jsonPost(this.endpoints.geocode, { address });\n }\n\n /**\n * Reverse Geocoding (GET)\n * @param {Object} coords\n * @param {number|string} coords.lat\n * @param {number|string} coords.lng\n * @returns {Promise<any>} { success, formatted_address, place_id, address_components... }\n */\n reverseGeocode({ lat, lng }) {\n return this.jsonGet(this.endpoints.reverse, { lat, lng });\n }\n\n /**\n * Timezone Lookup (GET)\n * @param {Object} coords\n * @param {number|string} coords.lat\n * @param {number|string} coords.lng\n * @returns {Promise<any>} { success, timezone_id, timezone_name, raw_offset, dst_offset, total_offset }\n */\n timezone({ lat, lng }) {\n return this.jsonGet(this.endpoints.timezone, { lat, lng });\n }\n\n /**\n * Reset session token (useful when user starts a new autocomplete flow).\n */\n resetSessionToken() {\n this.sessionToken = null;\n }\n\n /**\n * Basic parser for suggestion item to extract display info.\n * @param {Object} suggestion - item from autocomplete response\n */\n normalizeSuggestion(suggestion) {\n return {\n id: suggestion?.id || suggestion?.place_id || null,\n place_id: suggestion?.place_id || suggestion?.id || null,\n description: suggestion?.description || '',\n main_text: suggestion?.main_text || '',\n secondary_text: suggestion?.secondary_text || '',\n types: suggestion?.types || []\n };\n }\n\n _createSessionToken() {\n // Prefer crypto.randomUUID if available\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`;\n }\n}\n\nexport { LocationClient };","/**\n * useLocationAutocomplete - attach address autocomplete and details population to a FormView field.\n *\n * Minimal, framework-agnostic. Expects a LocationClient-like object with:\n * - autocomplete(input, opts?) -> Promise<{ success: boolean, session_token?: string, data: Array<{ id, place_id, description, main_text, secondary_text, types }> }>\n * - placeDetails({ place_id, session_token? }) -> Promise<{ success: boolean, address: { formatted_address, latitude, longitude, ... } }>\n *\n * Usage:\n * import { useLocationAutocomplete } from '@ext/map/location/useLocationAutocomplete.js';\n * import LocationClient from '@ext/map/location/LocationClient.js';\n *\n * const client = new LocationClient({ basePath: '/api' });\n * const dispose = useLocationAutocomplete(this, {\n * client,\n * field: 'address1',\n * mapping: {\n * address1: 'address1',\n * city: 'city',\n * state_code: 'state',\n * postal_code: 'postal_code',\n * country_code: 'country',\n * latitude: 'lat',\n * longitude: 'lng',\n * formatted_address: 'formatted_address',\n * place_id: 'place_id'\n * }\n * });\n *\n * @param {Object} formView - Instance of FormView (must expose element and optionally setFieldValue(name, value))\n * @param {Object} options\n * @param {Object} options.client - Location API client with autocomplete and placeDetails methods\n * @param {string|HTMLElement} options.field - Field name or input element to attach to (default: 'address1')\n * @param {string} [options.dropdownClass='loc-suggest'] - CSS class for the suggestions dropdown\n * @param {number} [options.minChars=3] - Minimum characters before triggering suggestions\n * @param {number} [options.debounceMs=200] - Debounce in ms for typing\n * @param {Object} [options.mapping] - Map of sourceKey(from API address) -> formFieldName\n * @param {Function} [options.onSelect] - Callback(details) when a suggestion is selected\n * @returns {Function} dispose() - Cleanup handler\n */\nexport function useLocationAutocomplete(formView, {\n client,\n field = 'address1',\n dropdownClass = 'loc-suggest',\n minChars = 3,\n debounceMs = 200,\n mapping = {\n address1: 'address1',\n city: 'city',\n state_code: 'state',\n postal_code: 'postal_code',\n country_code: 'country',\n latitude: 'latitude',\n longitude: 'longitude',\n formatted_address: 'formatted_address',\n place_id: 'place_id'\n },\n onSelect\n} = {}) {\n if (!formView || !formView.element) {\n console.warn('[useLocationAutocomplete] Missing formView or formView.element');\n return () => {};\n }\n if (!client || typeof client.autocomplete !== 'function' || typeof client.placeDetails !== 'function') {\n console.warn('[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails().');\n return () => {};\n }\n\n // Resolve target input\n const inputEl = (typeof field === 'string')\n ? (formView.element.querySelector(`input[name=\"${field}\"], #${field}`) || null)\n : (field instanceof HTMLElement ? field : null);\n\n if (!inputEl) {\n // Quietly no-op if field isn't present\n return () => {};\n }\n\n // Create dropdown\n const dd = document.createElement('div');\n dd.className = dropdownClass || 'loc-suggest';\n dd.style.position = 'absolute';\n dd.style.zIndex = '10000';\n dd.style.display = 'none';\n dd.style.background = '#fff';\n dd.style.border = '1px solid #e5e7eb';\n dd.style.borderRadius = '8px';\n dd.style.boxShadow = '0 8px 24px rgba(0,0,0,.08)';\n dd.style.padding = '4px 0';\n dd.style.maxHeight = '280px';\n dd.style.overflowY = 'auto';\n dd.style.minWidth = '240px';\n dd.setAttribute('role', 'listbox');\n dd.setAttribute('aria-label', 'Address suggestions');\n\n let open = false;\n let timer = null;\n let suppress = false;\n\n function placeDropdown() {\n if (!open) return;\n const r = inputEl.getBoundingClientRect();\n dd.style.minWidth = `${r.width}px`;\n dd.style.left = `${r.left + window.scrollX}px`;\n dd.style.top = `${r.bottom + window.scrollY + 4}px`;\n }\n\n function openDropdown() {\n if (!open) {\n open = true;\n dd.style.display = 'block';\n document.body.appendChild(dd);\n placeDropdown();\n }\n }\n\n function closeDropdown() {\n open = false;\n dd.style.display = 'none';\n dd.innerHTML = '';\n if (dd.parentNode) {\n dd.parentNode.removeChild(dd);\n }\n }\n\n function createRow(item, index) {\n const row = document.createElement('div');\n row.setAttribute('role', 'option');\n row.setAttribute('tabindex', '-1');\n row.style.padding = '8px 12px';\n row.style.cursor = 'pointer';\n row.style.display = 'flex';\n row.style.flexDirection = 'column';\n row.dataset.index = String(index);\n\n const main = document.createElement('div');\n main.style.fontWeight = '600';\n main.style.color = '#111827';\n main.textContent = item.main_text || item.description || '';\n\n const sub = document.createElement('div');\n sub.style.fontSize = '12px';\n sub.style.color = '#6b7280';\n sub.textContent = item.secondary_text || '';\n\n row.appendChild(main);\n if (sub.textContent) row.appendChild(sub);\n\n row.addEventListener('mouseenter', () => { row.style.background = '#f3f4f6'; });\n row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; });\n\n row.addEventListener('mousedown', (e) => {\n // Use mousedown to select without losing focus before click\n e.preventDefault();\n selectSuggestion(item);\n });\n\n return row;\n }\n\n async function renderSuggestions(list) {\n dd.innerHTML = '';\n if (!list || list.length === 0) {\n const empty = document.createElement('div');\n empty.style.padding = '8px 12px';\n empty.style.color = '#6b7280';\n empty.textContent = 'No results';\n dd.appendChild(empty);\n return;\n }\n list.forEach((item, idx) => dd.appendChild(createRow(item, idx)));\n }\n\n async function selectSuggestion(item) {\n try {\n const id = item.place_id || item.id;\n let details = null;\n if (id) {\n const res = await client.placeDetails({ place_id: id, id, session_token: client.sessionToken });\n details = res?.address || null;\n }\n\n // Set input to readable formatted address\n // Suppress immediate re-query from dispatched input/focus\n suppress = true;\n clearTimeout(timer);\n if (details?.formatted_address) {\n inputEl.value = details.formatted_address;\n // Trigger a native input event if consumers rely on it\n inputEl.dispatchEvent(new Event('input', { bubbles: true }));\n } else if (item.description) {\n inputEl.value = item.description;\n inputEl.dispatchEvent(new Event('input', { bubbles: true }));\n }\n // Hide dropdown and blur to avoid onFocus reopen\n try { inputEl.blur(); } catch {}\n closeDropdown();\n // Lift suppression after debounce window\n setTimeout(() => { suppress = false; }, debounceMs + 50);\n\n // Apply mapping to populate other fields\n if (details && mapping && typeof mapping === 'object') {\n Object.entries(mapping).forEach(([srcKey, formField]) => {\n if (!formField) return;\n const val = details[srcKey];\n if (val !== undefined && val !== null) {\n // Update FormView model and DOM field\n try {\n if (typeof formView.setFieldValue === 'function') {\n formView.setFieldValue(formField, String(val));\n }\n } catch (err) {\n // setFieldValue is optional; ignore errors\n }\n const targetEl = formView.element.querySelector(`input[name=\"${formField}\"], #${formField}, textarea[name=\"${formField}\"], select[name=\"${formField}\"]`);\n if (targetEl) {\n targetEl.value = String(val);\n targetEl.dispatchEvent(new Event('input', { bubbles: true }));\n targetEl.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n });\n }\n\n if (typeof onSelect === 'function') {\n onSelect(details || null);\n }\n } catch (err) {\n console.warn('[useLocationAutocomplete] placeDetails error:', err);\n } finally {\n closeDropdown();\n }\n }\n\n async function handleInput() {\n const q = inputEl.value.trim();\n if (q.length < minChars) {\n closeDropdown();\n return;\n }\n\n try {\n const res = await client.autocomplete(q);\n const list = Array.isArray(res?.data) ? res.data : [];\n const items = list.map(x => ({\n id: x.id,\n place_id: x.place_id,\n description: x.description,\n main_text: x.main_text,\n secondary_text: x.secondary_text,\n types: x.types\n }));\n openDropdown();\n placeDropdown();\n await renderSuggestions(items);\n } catch (err) {\n console.warn('[useLocationAutocomplete] autocomplete error:', err);\n closeDropdown();\n }\n }\n\n function onInput() {\n if (suppress) return;\n clearTimeout(timer);\n timer = setTimeout(handleInput, debounceMs);\n }\n\n function onFocus() {\n if (suppress) return;\n if (inputEl.value.trim().length >= minChars) {\n onInput();\n }\n }\n\n function onBlur() {\n // defer close to allow mousedown on dropdown items to register\n setTimeout(() => {\n if (!dd.contains(document.activeElement)) {\n closeDropdown();\n }\n }, 120);\n }\n\n function onWindowMove() {\n if (open) placeDropdown();\n }\n\n function onDocumentClick(e) {\n if (!dd.contains(e.target) && e.target !== inputEl) {\n closeDropdown();\n }\n }\n\n // Attach listeners\n inputEl.addEventListener('input', onInput);\n inputEl.addEventListener('focus', onFocus);\n inputEl.addEventListener('blur', onBlur);\n window.addEventListener('resize', onWindowMove);\n window.addEventListener('scroll', onWindowMove, true);\n document.addEventListener('click', onDocumentClick);\n\n // Return disposer for cleanup\n return function dispose() {\n clearTimeout(timer);\n try { inputEl.removeEventListener('input', onInput); } catch (e) { /* ignore */ }\n try { inputEl.removeEventListener('focus', onFocus); } catch (e) { /* ignore */ }\n try { inputEl.removeEventListener('blur', onBlur); } catch (e) { /* ignore */ }\n try { window.removeEventListener('resize', onWindowMove); } catch (e) { /* ignore */ }\n try { window.removeEventListener('scroll', onWindowMove, true); } catch (e) { /* ignore */ }\n try { document.removeEventListener('click', onDocumentClick); } catch (e) { /* ignore */ }\n try { closeDropdown(); } catch (e) { /* ignore */ }\n };\n}\n\nexport default useLocationAutocomplete;","/**\n * LocationDetailsView - Displays a formatted address and an optional map preview.\n *\n * Usage:\n * new LocationDetailsView({\n * details: {\n * formatted_address: '1600 Amphitheatre Pkwy...',\n * latitude: 37.422,\n * longitude: -122.084,\n * place_id: '...'\n * },\n * height: 260,\n * tileLayer: 'osm'\n * });\n */\n\nimport View from '@core/View.js';\nimport MapView from '../MapView.js';\n\nexport default class LocationDetailsView extends View {\n /**\n * @param {Object} options\n * @param {Object} [options.details] - Address details (formatted_address, latitude, longitude, place_id, etc.)\n * @param {number} [options.height=260] - Map height (px)\n * @param {string} [options.tileLayer='osm'] - Tile layer key for MapView\n */\n constructor({ details = {}, height = 260, tileLayer = 'osm' } = {}) {\n super({\n className: 'location-details-view'\n });\n\n this.details = details || {};\n this.height = Number.isFinite(height) ? height : 260;\n this.tileLayer = tileLayer || 'osm';\n\n // Expose flattened fields for Mustache (view is the template context)\n this.formatted_address = this.details.formatted_address || '';\n this.place_id = this.details.place_id || '';\n this.latitude =\n this._toNumber(this.details.latitude ?? this.details.lat ?? null);\n this.longitude =\n this._toNumber(this.details.longitude ?? this.details.lng ?? null);\n\n this.hasCoords = Number.isFinite(this.latitude) && Number.isFinite(this.longitude);\n\n // Keep a reference to child map view (if created)\n this._mapView = null;\n\n // Simple, framework-friendly template with a child container for the map\n this.template = `\n <div class=\"loc-details\">\n {{#formatted_address}}\n <div class=\"mb-2 fw-semibold\" style=\"word-break: break-word;\">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class=\"text-muted small mb-2\">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class=\"text-muted small mb-2\">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container=\"map\"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class=\"text-muted small\" style=\"border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;\">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n `;\n }\n\n async onInit() {\n // Create child map view only if we have coordinates\n if (this.hasCoords) {\n const markers = [{\n lat: this.latitude,\n lng: this.longitude,\n popup: this.formatted_address || ''\n }];\n\n this._mapView = new MapView({\n markers,\n center: [this.latitude, this.longitude],\n zoom: 13,\n height: this.height,\n tileLayer: this.tileLayer,\n showLayerControl: true,\n containerId: 'map'\n });\n\n this.addChild(this._mapView);\n }\n }\n\n _toNumber(v) {\n if (v === null || v === undefined || v === '') return null;\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n }\n}","/**\n * LocationDialogs - helpers for showing location details and a picker dialog.\n *\n * Exports:\n * - showLocationDetailsDialog({ client?, details?, place_id?, id?, title?, height?, tileLayer? })\n * -> Shows a dialog with a formatted address + map. If details not provided, fetches via place_id/id.\n * - showLocationPickerDialog({ client?, title?, minChars?, debounceMs?, placeholder?, confirmText?, height?, tileLayer? })\n * -> Opens a dialog with an address search box, suggestion list, and live preview map. Returns the chosen details or null.\n */\n\nimport Dialog from '@core/views/feedback/Dialog.js';\nimport View from '@core/View.js';\nimport LocationClient from './LocationClient.js';\nimport LocationDetailsView from './LocationDetailsView.js';\n\n/**\n * Show a dialog with location details + map\n * @param {Object} options\n * @param {LocationClient} [options.client] - Optional LocationClient instance\n * @param {Object} [options.details] - If provided, dialog renders immediately without fetching\n * @param {string} [options.place_id] - Place id to fetch details\n * @param {string} [options.id] - Alternative id to fetch details\n * @param {string} [options.title='Location Details'] - Dialog title\n * @param {number} [options.height=260] - Map height\n * @param {string} [options.tileLayer='osm'] - Tile layer\n * @returns {Promise<any>} Dialog result (from Dialog.showView), primarily useful for awaiting close\n */\nexport async function showLocationDetailsDialog({\n client = new LocationClient({ basePath: '/api' }),\n details = null,\n place_id = null,\n id = null,\n title = 'Location Details',\n height = 260,\n tileLayer = 'osm'\n} = {}) {\n let resolved = details;\n\n if (!resolved && (place_id || id)) {\n try {\n const res = await client.placeDetails({ place_id, id, session_token: client.sessionToken });\n resolved = res?.address || null;\n } catch (err) {\n // Fallback to an error placeholder\n resolved = {\n formatted_address: 'Unable to load place details',\n error: err?.message || 'Unknown error'\n };\n }\n }\n\n const view = new LocationDetailsView({\n details: resolved || {},\n height,\n tileLayer\n });\n\n return Dialog.showDialog({\n title,\n body: view,\n size: 'md',\n buttons: [{ text: 'Close', class: 'btn-secondary', dismiss: true, value: 'close' }]\n });\n}\n\n/**\n * Internal view class to implement a picker UI:\n * - Search input with debounced autocomplete\n * - Suggestions dropdown\n * - Live details preview with map\n */\nclass LocationPickerView extends View {\n constructor({\n client,\n minChars = 3,\n debounceMs = 200,\n placeholder = 'Search address',\n height = 240,\n tileLayer = 'osm'\n } = {}) {\n super({\n className: 'location-picker-view',\n template: '<div class=\"location-picker-view-root\"></div>'\n });\n this.client = client;\n this.minChars = minChars;\n this.debounceMs = debounceMs;\n this.placeholder = placeholder;\n this.height = height;\n this.tileLayer = tileLayer;\n\n this._selected = null; // Selected details object\n this._timer = null; // Debounce timer\n this._suggestions = []; // Cached suggestions\n this._elements = {}; // refs to elements\n this._previewView = null; // LocationDetailsView instance for live preview\n }\n\n getSelected() {\n return this._selected;\n }\n\n async onAfterRender() {\n // Build UI\n const root = document.createElement('div');\n root.style.display = 'grid';\n root.style.gap = '10px';\n\n // Search input\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'form-control';\n input.placeholder = this.placeholder || 'Search address';\n input.setAttribute('aria-label', 'Address search');\n root.appendChild(input);\n\n // Suggestions container\n const dd = document.createElement('div');\n dd.style.border = '1px solid #e5e7eb';\n dd.style.borderRadius = '8px';\n dd.style.background = '#fff';\n dd.style.boxShadow = '0 8px 24px rgba(0,0,0,.08)';\n dd.style.maxHeight = '260px';\n dd.style.overflowY = 'auto';\n dd.style.display = 'none';\n root.appendChild(dd);\n\n // Preview container for details + map\n const previewHost = document.createElement('div');\n root.appendChild(previewHost);\n\n // Mount tree\n this.element.appendChild(root);\n\n // Store refs\n this._elements = { root, input, dd, previewHost };\n\n // Wire events\n input.addEventListener('input', () => {\n clearTimeout(this._timer);\n this._timer = setTimeout(() => this._handleInput(), this.debounceMs);\n });\n\n input.addEventListener('focus', () => {\n if ((input.value || '').trim().length >= this.minChars) {\n this._handleInput();\n }\n });\n\n // Render empty preview on init\n await this._renderPreview(null);\n }\n\n async _handleInput() {\n const q = (this._elements.input.value || '').trim();\n if (q.length < this.minChars) {\n this._elements.dd.style.display = 'none';\n this._elements.dd.innerHTML = '';\n return;\n }\n\n try {\n const res = await this.client.autocomplete(q);\n const items = Array.isArray(res?.data) ? res.data : [];\n this._suggestions = items.map(x => ({\n id: x.id,\n place_id: x.place_id,\n description: x.description,\n main_text: x.main_text,\n secondary_text: x.secondary_text,\n types: x.types\n }));\n await this._renderSuggestions();\n } catch (err) {\n // show empty state\n this._suggestions = [];\n await this._renderSuggestions();\n }\n }\n\n async _renderSuggestions() {\n const dd = this._elements.dd;\n dd.innerHTML = '';\n\n if (!this._suggestions.length) {\n const empty = document.createElement('div');\n empty.style.padding = '8px 12px';\n empty.style.color = '#6b7280';\n empty.textContent = 'No results';\n dd.appendChild(empty);\n dd.style.display = 'block';\n return;\n }\n\n this._suggestions.forEach((item, idx) => {\n const row = document.createElement('div');\n row.style.padding = '8px 12px';\n row.style.cursor = 'pointer';\n row.style.display = 'flex';\n row.style.flexDirection = 'column';\n row.addEventListener('mouseenter', () => { row.style.background = '#f3f4f6'; });\n row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; });\n\n const main = document.createElement('div');\n main.style.fontWeight = '600';\n main.style.color = '#111827';\n main.textContent = item.main_text || item.description || '';\n\n const sub = document.createElement('div');\n sub.style.fontSize = '12px';\n sub.style.color = '#6b7280';\n sub.textContent = item.secondary_text || '';\n\n row.appendChild(main);\n if (sub.textContent) row.appendChild(sub);\n\n row.addEventListener('mousedown', (e) => {\n e.preventDefault(); // select without losing focus\n this._selectSuggestion(item);\n });\n\n dd.appendChild(row);\n });\n\n dd.style.display = 'block';\n }\n\n async _selectSuggestion(item) {\n try {\n const id = item.place_id || item.id;\n if (!id) return;\n\n const res = await this.client.placeDetails({ place_id: id, id, session_token: this.client.sessionToken });\n const details = res?.address || null;\n\n // Update input to readable value and keep selected\n if (details?.formatted_address) {\n this._elements.input.value = details.formatted_address;\n } else if (item.description) {\n this._elements.input.value = item.description;\n }\n\n this._selected = details || null;\n\n // Hide suggestions after selection\n this._elements.dd.style.display = 'none';\n this._elements.dd.innerHTML = '';\n\n // Update preview\n await this._renderPreview(this._selected);\n } catch (err) {\n // keep selection unchanged on error\n }\n }\n\n async _renderPreview(details) {\n // Clear previous preview\n this._elements.previewHost.innerHTML = '';\n\n // If nothing selected, render a small placeholder\n if (!details) {\n const ph = document.createElement('div');\n ph.style.border = '1px dashed #e5e7eb';\n ph.style.borderRadius = '8px';\n ph.style.padding = '12px';\n ph.style.color = '#6b7280';\n ph.textContent = 'No location selected';\n this._elements.previewHost.appendChild(ph);\n return;\n }\n\n // Render a LocationDetailsView\n try {\n this._previewView = new LocationDetailsView({\n details,\n height: this.height,\n tileLayer: this.tileLayer\n });\n await this._previewView.render(true, this._elements.previewHost);\n } catch {\n const fallback = document.createElement('div');\n fallback.style.border = '1px dashed #e5e7eb';\n fallback.style.borderRadius = '8px';\n fallback.style.padding = '12px';\n fallback.textContent = details?.formatted_address || 'Selected location';\n this._elements.previewHost.appendChild(fallback);\n }\n }\n\n async onBeforeDestroy() {\n clearTimeout(this._timer);\n await super.onBeforeDestroy();\n }\n}\n\n/**\n * Show a picker dialog for choosing a location.\n * Returns the chosen location details or null if canceled.\n *\n * @param {Object} options\n * @param {LocationClient} [options.client] - Optional client instance\n * @param {string} [options.title='Pick a Location'] - Dialog title\n * @param {number} [options.minChars=3] - Minimum characters before suggestions\n * @param {number} [options.debounceMs=200] - Typing debounce\n * @param {string} [options.placeholder='Search address'] - Input placeholder\n * @param {string} [options.confirmText='Select'] - Confirm button text\n * @param {number} [options.height=240] - Preview map height\n * @param {string} [options.tileLayer='osm'] - Tile layer\n * @returns {Promise<Object|null>} Selected details or null\n */\nexport async function showLocationPickerDialog({\n client = new LocationClient({ basePath: '/api' }),\n title = 'Pick a Location',\n minChars = 3,\n debounceMs = 200,\n placeholder = 'Search address',\n confirmText = 'Select',\n height = 240,\n tileLayer = 'osm'\n} = {}) {\n const view = new LocationPickerView({\n client,\n minChars,\n debounceMs,\n placeholder,\n height,\n tileLayer\n });\n\n const result = await Dialog.showDialog({\n title,\n body: view,\n size: 'md',\n buttons: [\n { text: 'Cancel', class: 'btn-secondary', dismiss: true, value: 'cancel' },\n { text: confirmText, class: 'btn-primary', value: 'ok' }\n ]\n });\n\n // If user confirmed, return the selected details; otherwise null\n if (result === 'ok') {\n return view.getSelected() || null;\n }\n return null;\n}\n\nexport default {\n showLocationDetailsDialog,\n showLocationPickerDialog\n};","/**\n * LocationPlugin - Form extension for address autocomplete and place details\n *\n * This plugin integrates with the core FormPlugins registry to:\n * - Optionally add a new field type \"address\" (rendered as a text input)\n * - Auto-wire autocomplete and details population for address fields\n *\n * Nothing is active unless you explicitly register this plugin.\n *\n * Usage:\n * import { registerLocationPlugin } from '@ext/map/location/LocationPlugin.js';\n *\n * // Register once at app startup\n * const unregister = registerLocationPlugin({\n * basePath: '/api', // optional prefix for location endpoints (works with Rest baseURL)\n * fieldTypeName: 'address',\n * registerFieldType: true,\n * mapping: {\n * address1: 'address1',\n * city: 'city',\n * state_code: 'state',\n * postal_code: 'postal_code',\n * country_code: 'country',\n * latitude: 'lat',\n * longitude: 'lng',\n * formatted_address: 'formatted_address',\n * place_id: 'place_id'\n * }\n * });\n *\n * // Later (optional):\n * // unregister();\n */\n\nimport { FormPlugins } from '@core/forms/FormPlugins.js';\nimport LocationClient from './LocationClient.js';\nimport { useLocationAutocomplete } from './useLocationAutocomplete.js';\n\nexport class LocationFormPlugin {\n /**\n * @param {Object} options\n * @param {string} [options.basePath] - API base path prefix (e.g., '/api') used with core Rest\n * @param {Object} [options.mapping] - Mapping from API address keys -> form field names\n * @param {boolean} [options.registerFieldType=true] - Register custom field type (address)\n * @param {string} [options.fieldTypeName='address'] - Field type name to register\n * @param {string} [options.attributeSelector='data-location'] - Attribute to opt-in on any text input (e.g., data-location=\"address\")\n * @param {number} [options.minChars=3] - Minimum characters before triggering autocomplete\n * @param {number} [options.debounceMs=200] - Typing debounce for autocomplete\n */\n constructor({\n basePath = '/api',\n mapping,\n registerFieldType = true,\n fieldTypeName = 'address',\n attributeSelector = 'data-location',\n minChars = 3,\n debounceMs = 200,\n\n // Browser autofill/autocomplete suppression (important for suggestion inputs)\n // Chrome often ignores autocomplete=\"off\" for address-like fields, so we default\n // to a more reliable value.\n suppressBrowserAutocomplete = true,\n autocompleteValue = 'new-password'\n } = {}) {\n this.id = 'location';\n this.client = new LocationClient({ basePath });\n\n this.mapping = mapping || {\n address1: 'address1',\n city: 'city',\n state_code: 'state',\n postal_code: 'postal_code',\n country_code: 'country',\n latitude: 'latitude',\n longitude: 'longitude',\n formatted_address: 'formatted_address',\n place_id: 'place_id'\n };\n\n this.fieldTypeName = fieldTypeName;\n this.attributeSelector = attributeSelector;\n this.minChars = minChars;\n this.debounceMs = debounceMs;\n\n this.suppressBrowserAutocomplete = suppressBrowserAutocomplete !== false;\n this.autocompleteValue = autocompleteValue || 'new-password';\n\n // Optional field type registration\n if (registerFieldType) {\n this.fieldTypes = {\n [this.fieldTypeName]: (builder, field) => this.renderAddressField(builder, field)\n };\n }\n }\n\n /**\n * Custom renderer for the \"address\" field type.\n * Leverages existing FormBuilder input rendering for consistency (text input).\n */\n renderAddressField(builder, field) {\n const suppressAttrs = this.suppressBrowserAutocomplete\n ? `autocomplete=\"${this.autocompleteValue}\" autocapitalize=\"off\" autocorrect=\"off\" spellcheck=\"false\" inputmode=\"search\"`\n : '';\n\n const f = {\n ...field,\n type: 'text',\n placeholder: field.placeholder || 'Start typing an address',\n attrs: this.mergeAttrs(\n field.attrs,\n `${this.attributeSelector}=\"address\" ${suppressAttrs} aria-autocomplete=\"list\" role=\"combobox\"`\n )\n };\n // Prefer builder.renderTextField if available, otherwise fallback to generic input\n if (typeof builder.renderTextField === 'function') {\n return builder.renderTextField(f);\n }\n if (typeof builder.renderInputField === 'function') {\n return builder.renderInputField(f, 'text');\n }\n // Minimal fallback if neither method exists (should not happen)\n const id = builder.getFieldId?.(f.name) || `field_${f.name}`;\n return `\n <div class=\"mojo-form-control\">\n ${f.label ? `<label for=\"${id}\" class=\"${builder.options?.labelClass || 'form-label'}\">${f.label}</label>` : ''}\n <input type=\"text\" id=\"${id}\" name=\"${f.name}\" class=\"${builder.options?.inputClass || 'form-control'}\"\n placeholder=\"${f.placeholder || ''}\" ${this.attributeSelector}=\"address\" ${suppressAttrs} />\n </div>\n `;\n }\n\n /**\n * Helper to merge existing attrs with an additional attribute string\n */\n mergeAttrs(existing, add) {\n const base = (existing || '').trim();\n const extra = (add || '').trim();\n if (!base) return extra;\n if (!extra) return base;\n return `${base} ${extra}`;\n }\n\n /**\n * Hook: called when FormView is initialized\n * You can read application config here if needed.\n */\n onFormViewInit(_formView) {\n // no-op by default\n }\n\n /**\n * Hook: called after FormView finished rendering and initializing components\n * This is a good spot to opt-in based on attributes (e.g., data-location=\"address\") on any text input.\n */\n onAfterRender(formView) {\n if (!formView?.element) return;\n\n try {\n const selector = `input[${this.attributeSelector}=\"address\"]`;\n const inputs = formView.element.querySelectorAll(selector);\n inputs.forEach((inputEl) => {\n // Ensure we don't double-bind if a field-level init already handled it\n if (inputEl.dataset && inputEl.dataset._locationBound === '1') return;\n\n // Enforce suppression on any opt-in input (not just our field type renderer)\n if (this.suppressBrowserAutocomplete) {\n try {\n inputEl.setAttribute('autocomplete', this.autocompleteValue);\n inputEl.setAttribute('autocapitalize', 'off');\n inputEl.setAttribute('autocorrect', 'off');\n inputEl.setAttribute('spellcheck', 'false');\n inputEl.setAttribute('inputmode', 'search');\n } catch (e) {\n // best-effort: some environments may block setting attributes\n }\n }\n\n let dispose;\n const rebind = () => {\n dispose = useLocationAutocomplete(formView, {\n client: this.client,\n field: inputEl,\n mapping: this.mapping,\n minChars: this.minChars,\n debounceMs: this.debounceMs,\n onSelect: (_details) => {\n // Prevent immediate re-open by removing listeners during debounce window\n try { inputEl.blur(); } catch (e) { /* best-effort */ }\n try { dispose && dispose(); } catch (e) { /* best-effort */ }\n setTimeout(() => {\n rebind();\n }, this.debounceMs + 50);\n }\n });\n inputEl.dataset._locationBound = '1';\n this._trackDisposer(formView, dispose);\n };\n rebind();\n });\n } catch (err) {\n // best-effort\n }\n }\n\n /**\n * Hook: called for each field element with its config after FormView initialization\n * If the field is our \"address\" type (or opt-in via attribute), attach autocomplete.\n */\n onFieldInit(formView, fieldEl, fieldConfig) {\n try {\n const isAddressType = fieldConfig?.type === this.fieldTypeName;\n const hasAttr = fieldEl?.getAttribute?.(this.attributeSelector) === 'address';\n\n if (isAddressType || hasAttr) {\n // Avoid double-binding\n if (fieldEl.dataset && fieldEl.dataset._locationBound === '1') return;\n\n // Enforce suppression on field-type and attribute-bound inputs\n if (this.suppressBrowserAutocomplete) {\n try {\n fieldEl.setAttribute('autocomplete', this.autocompleteValue);\n fieldEl.setAttribute('autocapitalize', 'off');\n fieldEl.setAttribute('autocorrect', 'off');\n fieldEl.setAttribute('spellcheck', 'false');\n fieldEl.setAttribute('inputmode', 'search');\n } catch (e) {\n // best-effort: some environments may block setting attributes\n }\n }\n\n let dispose;\n const rebind = () => {\n dispose = useLocationAutocomplete(formView, {\n client: this.client,\n field: fieldEl,\n mapping: this.mapping,\n minChars: this.minChars,\n debounceMs: this.debounceMs,\n onSelect: (_details) => {\n // Prevent immediate re-open by removing listeners during debounce window\n try { fieldEl.blur(); } catch (e) { /* best-effort */ }\n try { dispose && dispose(); } catch (e) { /* best-effort */ }\n setTimeout(() => {\n rebind();\n }, this.debounceMs + 50);\n }\n });\n\n fieldEl.dataset._locationBound = '1';\n this._trackDisposer(formView, dispose);\n };\n rebind();\n }\n } catch (err) {\n // best-effort\n }\n }\n\n /**\n * Hook: field change notification (no-op for now)\n */\n onFieldChange(_formView, _name, _value) {\n // Could add logic to validate or re-resolve coordinates based on user edits\n }\n\n /**\n * Track disposers for cleanup. If the FormView provides an event API, attach to destroy/before-destroy.\n */\n _trackDisposer(formView, dispose) {\n if (!dispose || typeof dispose !== 'function') return;\n if (!formView) return;\n\n if (!formView._locationDisposers) {\n Object.defineProperty(formView, '_locationDisposers', {\n configurable: true,\n enumerable: false,\n writable: true,\n value: []\n });\n }\n formView._locationDisposers.push(dispose);\n\n // Attempt to hook into a lifecycle if available\n // Many MOJO View classes expose an event emitter; if not, this is still fine (garbage-collected on page change).\n const tryBind = (eventName) => {\n if (typeof formView.on === 'function') {\n try {\n formView.on(eventName, () => {\n try {\n formView._locationDisposers?.forEach(fn => {\n try { fn(); } catch { /* ignore dispose errors */ }\n });\n } finally {\n formView._locationDisposers = [];\n }\n });\n return true;\n } catch { /* ignore */ }\n }\n return false;\n };\n\n // Bind to a best-effort lifecycle event if present\n if (!tryBind('before:destroy')) {\n tryBind('destroy');\n }\n }\n}\n\n/**\n * Register the location plugin with the core FormPlugins registry.\n * @param {ConstructorParameters<typeof LocationFormPlugin>[0]} options\n * @returns {Function} unregister function\n */\nexport function registerLocationPlugin(options = {}) {\n const plugin = new LocationFormPlugin(options);\n return FormPlugins.register(plugin);\n}\n\nexport default LocationFormPlugin;"],"names":["LocationClient","constructor","basePath","endpoints","this","String","sessionToken","validate","autocomplete","details","geocode","reverse","timezone","setAuthHeader","header","_authHeader","headers","extra","auth","h","Authorization","jsonGet","path","params","resp","rest","GET","fullPath","data","jsonPost","body","POST","_safeJson","res","json","validateAddress","address","query","opts","trim","length","success","size","count","_createSessionToken","input","session_token","result","placeDetails","place_id","id","q","pid","reverseGeocode","lat","lng","resetSessionToken","normalizeSuggestion","suggestion","description","main_text","secondary_text","types","crypto","randomUUID","Date","now","Math","random","toString","slice","useLocationAutocomplete","formView","client","field","dropdownClass","minChars","debounceMs","mapping","address1","city","state_code","postal_code","country_code","latitude","longitude","formatted_address","onSelect","element","console","warn","inputEl","querySelector","HTMLElement","dd","document","createElement","className","style","position","zIndex","display","background","border","borderRadius","boxShadow","padding","maxHeight","overflowY","minWidth","setAttribute","open","timer","suppress","placeDropdown","r","getBoundingClientRect","width","left","window","scrollX","top","bottom","scrollY","closeDropdown","innerHTML","parentNode","removeChild","async","handleInput","value","items","Array","isArray","map","x","appendChild","list","empty","color","textContent","forEach","item","idx","index","row","cursor","flexDirection","dataset","main","fontWeight","sub","fontSize","addEventListener","e","preventDefault","clearTimeout","dispatchEvent","Event","bubbles","blur","setTimeout","Object","entries","srcKey","formField","val","setFieldValue","err","targetEl","selectSuggestion","createRow","renderSuggestions","onInput","onFocus","onBlur","contains","activeElement","onWindowMove","onDocumentClick","target","removeEventListener","LocationDetailsView","View","height","tileLayer","super","Number","isFinite","_toNumber","hasCoords","_mapView","template","onInit","markers","popup","MapView","center","zoom","showLayerControl","containerId","addChild","v","n","LocationPickerView","placeholder","_selected","_timer","_suggestions","_elements","_previewView","getSelected","onAfterRender","root","gap","type","previewHost","_handleInput","_renderPreview","_renderSuggestions","_selectSuggestion","ph","render","fallback","onBeforeDestroy","LocationFormPlugin","registerFieldType","fieldTypeName","attributeSelector","suppressBrowserAutocomplete","autocompleteValue","fieldTypes","builder","renderAddressField","suppressAttrs","f","attrs","mergeAttrs","renderTextField","renderInputField","getFieldId","name","label","options","labelClass","inputClass","existing","add","base","onFormViewInit","_formView","selector","querySelectorAll","_locationBound","dispose","rebind","_details","_trackDisposer","onFieldInit","fieldEl","fieldConfig","isAddressType","hasAttr","getAttribute","onFieldChange","_name","_value","_locationDisposers","defineProperty","configurable","enumerable","writable","push","tryBind","eventName","on","fn","plugin","FormPlugins","register","title","resolved","error","message","view","Dialog","showDialog","buttons","text","class","dismiss","confirmText"],"mappings":"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"}
package/dist/map.es.js CHANGED
@@ -1,2 +1,2 @@
1
- import{M as e}from"./chunks/MetricsCountryMapView-DuBKO7gz.js";import{a as t,b as s}from"./chunks/MetricsCountryMapView-DuBKO7gz.js";import{r as i,V as n}from"./chunks/Rest-BJ3Mvx1L.js";import o from"./chunks/Dialog-t_9l2Mou.js";import{F as a}from"./chunks/FormPlugins-DvQ-G5J5.js";import{C as r,M as l}from"./chunks/Collection-BWKmydl5.js";class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,t){const s=await i.GET(this.fullPath(e),t||{});return s&&void 0!==s.data?s.data:s}async jsonPost(e,t){const s=await i.POST(this.fullPath(e),t??{},{},{});return s&&void 0!==s.data?s.data:s}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function d(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function _(){p||l.value.trim().length>=n&&f()}function b(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function g(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",_),l.addEventListener("blur",b),window.addEventListener("resize",g),window.addEventListener("scroll",g,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",_)}catch(e){}try{l.removeEventListener("blur",b)}catch(e){}try{window.removeEventListener("resize",g)}catch(e){}try{window.removeEventListener("scroll",g,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends n{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}async function c({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:s=null,id:i=null,title:n="Location Details",height:a=260,tileLayer:r="osm"}={}){let l=t;if(!l&&(s||i))try{const t=await e.placeDetails({place_id:s,id:i,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:a,tileLayer:r});return o.showDialog({title:n,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})}class LocationPickerView extends n{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}async function u({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:s=3,debounceMs:i=200,placeholder:n="Search address",confirmText:a="Select",height:r=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:s,debounceMs:i,placeholder:n,height:r,tileLayer:l});return"ok"===await o.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:a,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(n){}let s;const i=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(n){}try{s&&s()}catch(n){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const n=s?.type===this.fieldTypeName,o="address"===t?.getAttribute?.(this.attributeSelector);if(n||o){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const n=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{n()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};n()}}catch(n){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}function p(e={}){const t=new LocationFormPlugin(e);return a.register(t)}export{r as Collection,LocationClient,LocationDetailsView,LocationFormPlugin,t as MapLibreView,e as MapView,s as MetricsCountryMapView,l as Model,n as View,p as registerLocationPlugin,c as showLocationDetailsDialog,u as showLocationPickerDialog,d as useLocationAutocomplete};
1
+ import{M as e}from"./chunks/MetricsCountryMapView-DIlezla0.js";import{a as t,b as s}from"./chunks/MetricsCountryMapView-DIlezla0.js";import{r as i,V as n}from"./chunks/Collection-r1ACzUeh.js";import{C as o,M as a}from"./chunks/Collection-r1ACzUeh.js";import{D as r}from"./chunks/Dialog-BVCCpLPw.js";import{F as l}from"./chunks/FormPlugins-DvQ-G5J5.js";class LocationClient{constructor({basePath:e="/api",endpoints:t={}}={}){this.basePath=String(e||""),this.sessionToken=null,this.endpoints={validate:"/location/address/validate",autocomplete:"/location/address/suggestions",details:"/location/address/place-details",geocode:"/location/address/geocode",reverse:"/location/address/reverse-geocode",timezone:"/location/timezone",...t}}setAuthHeader(e){this._authHeader=e}headers(e){let t=null;if("function"==typeof this._authHeader)try{t=this._authHeader()}catch{t=null}else t=this._authHeader;const s={"Content-Type":"application/json",...e||{}};return t&&(s.Authorization=t),s}async jsonGet(e,t){const s=await i.GET(this.fullPath(e),t||{});return s&&void 0!==s.data?s.data:s}async jsonPost(e,t){const s=await i.POST(this.fullPath(e),t??{},{},{});return s&&void 0!==s.data?s.data:s}fullPath(e){return`${this.basePath}${e}`}async _safeJson(e){try{return await e.json()}catch{return null}}validateAddress(e){return this.jsonPost(this.endpoints.validate,e)}async autocomplete(e,t={}){if(!e||0===String(e).trim().length)return{success:!0,data:[],size:0,count:0};this.sessionToken||(this.sessionToken=this._createSessionToken());const s={input:e,session_token:this.sessionToken,...t},i=await this.jsonGet(this.endpoints.autocomplete,s);return i&&i.session_token&&(this.sessionToken=i.session_token),i}placeDetails({place_id:e,session_token:t,id:s}={}){const i={},n=e||s||null;return n&&(i.place_id=n),t&&(i.session_token=t),this.jsonGet(this.endpoints.details,i)}geocode(e){return this.jsonPost(this.endpoints.geocode,{address:e})}reverseGeocode({lat:e,lng:t}){return this.jsonGet(this.endpoints.reverse,{lat:e,lng:t})}timezone({lat:e,lng:t}){return this.jsonGet(this.endpoints.timezone,{lat:e,lng:t})}resetSessionToken(){this.sessionToken=null}normalizeSuggestion(e){return{id:e?.id||e?.place_id||null,place_id:e?.place_id||e?.id||null,description:e?.description||"",main_text:e?.main_text||"",secondary_text:e?.secondary_text||"",types:e?.types||[]}}_createSessionToken(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}}function d(e,{client:t,field:s="address1",dropdownClass:i="loc-suggest",minChars:n=3,debounceMs:o=200,mapping:a={address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},onSelect:r}={}){if(!e||!e.element)return console.warn("[useLocationAutocomplete] Missing formView or formView.element"),()=>{};if(!t||"function"!=typeof t.autocomplete||"function"!=typeof t.placeDetails)return console.warn("[useLocationAutocomplete] Missing or invalid client. Provide an object with autocomplete() and placeDetails()."),()=>{};const l="string"==typeof s?e.element.querySelector(`input[name="${s}"], #${s}`)||null:s instanceof HTMLElement?s:null;if(!l)return()=>{};const d=document.createElement("div");d.className=i||"loc-suggest",d.style.position="absolute",d.style.zIndex="10000",d.style.display="none",d.style.background="#fff",d.style.border="1px solid #e5e7eb",d.style.borderRadius="8px",d.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",d.style.padding="4px 0",d.style.maxHeight="280px",d.style.overflowY="auto",d.style.minWidth="240px",d.setAttribute("role","listbox"),d.setAttribute("aria-label","Address suggestions");let c=!1,u=null,p=!1;function h(){if(!c)return;const e=l.getBoundingClientRect();d.style.minWidth=`${e.width}px`,d.style.left=`${e.left+window.scrollX}px`,d.style.top=`${e.bottom+window.scrollY+4}px`}function m(){c=!1,d.style.display="none",d.innerHTML="",d.parentNode&&d.parentNode.removeChild(d)}async function y(){const s=l.value.trim();if(s.length<n)m();else try{const i=await t.autocomplete(s),n=(Array.isArray(i?.data)?i.data:[]).map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types}));c||(c=!0,d.style.display="block",document.body.appendChild(d),h()),h(),await async function(s){if(d.innerHTML="",!s||0===s.length){const e=document.createElement("div");return e.style.padding="8px 12px",e.style.color="#6b7280",e.textContent="No results",void d.appendChild(e)}s.forEach((s,i)=>d.appendChild(function(s,i){const n=document.createElement("div");n.setAttribute("role","option"),n.setAttribute("tabindex","-1"),n.style.padding="8px 12px",n.style.cursor="pointer",n.style.display="flex",n.style.flexDirection="column",n.dataset.index=String(i);const d=document.createElement("div");d.style.fontWeight="600",d.style.color="#111827",d.textContent=s.main_text||s.description||"";const c=document.createElement("div");return c.style.fontSize="12px",c.style.color="#6b7280",c.textContent=s.secondary_text||"",n.appendChild(d),c.textContent&&n.appendChild(c),n.addEventListener("mouseenter",()=>{n.style.background="#f3f4f6"}),n.addEventListener("mouseleave",()=>{n.style.background="transparent"}),n.addEventListener("mousedown",i=>{i.preventDefault(),async function(s){try{const i=s.place_id||s.id;let n=null;if(i){const e=await t.placeDetails({place_id:i,id:i,session_token:t.sessionToken});n=e?.address||null}p=!0,clearTimeout(u),n?.formatted_address?(l.value=n.formatted_address,l.dispatchEvent(new Event("input",{bubbles:!0}))):s.description&&(l.value=s.description,l.dispatchEvent(new Event("input",{bubbles:!0})));try{l.blur()}catch{}m(),setTimeout(()=>{p=!1},o+50),n&&a&&"object"==typeof a&&Object.entries(a).forEach(([t,s])=>{if(!s)return;const i=n[t];if(null!=i){try{"function"==typeof e.setFieldValue&&e.setFieldValue(s,String(i))}catch(o){}const t=e.element.querySelector(`input[name="${s}"], #${s}, textarea[name="${s}"], select[name="${s}"]`);t&&(t.value=String(i),t.dispatchEvent(new Event("input",{bubbles:!0})),t.dispatchEvent(new Event("change",{bubbles:!0})))}}),"function"==typeof r&&r(n||null)}catch(i){console.warn("[useLocationAutocomplete] placeDetails error:",i)}finally{m()}}(s)}),n}(s,i)))}(n)}catch(i){console.warn("[useLocationAutocomplete] autocomplete error:",i),m()}}function f(){p||(clearTimeout(u),u=setTimeout(y,o))}function _(){p||l.value.trim().length>=n&&f()}function b(){setTimeout(()=>{d.contains(document.activeElement)||m()},120)}function g(){c&&h()}function v(e){d.contains(e.target)||e.target===l||m()}return l.addEventListener("input",f),l.addEventListener("focus",_),l.addEventListener("blur",b),window.addEventListener("resize",g),window.addEventListener("scroll",g,!0),document.addEventListener("click",v),function(){clearTimeout(u);try{l.removeEventListener("input",f)}catch(e){}try{l.removeEventListener("focus",_)}catch(e){}try{l.removeEventListener("blur",b)}catch(e){}try{window.removeEventListener("resize",g)}catch(e){}try{window.removeEventListener("scroll",g,!0)}catch(e){}try{document.removeEventListener("click",v)}catch(e){}try{m()}catch(e){}}}class LocationDetailsView extends n{constructor({details:e={},height:t=260,tileLayer:s="osm"}={}){super({className:"location-details-view"}),this.details=e||{},this.height=Number.isFinite(t)?t:260,this.tileLayer=s||"osm",this.formatted_address=this.details.formatted_address||"",this.place_id=this.details.place_id||"",this.latitude=this._toNumber(this.details.latitude??this.details.lat??null),this.longitude=this._toNumber(this.details.longitude??this.details.lng??null),this.hasCoords=Number.isFinite(this.latitude)&&Number.isFinite(this.longitude),this._mapView=null,this.template='\n <div class="loc-details">\n {{#formatted_address}}\n <div class="mb-2 fw-semibold" style="word-break: break-word;">\n {{formatted_address}}\n </div>\n {{/formatted_address}}\n\n {{#place_id}}\n <div class="text-muted small mb-2">Place ID: {{place_id}}</div>\n {{/place_id}}\n\n {{^formatted_address}}\n <div class="text-muted small mb-2">No formatted address provided</div>\n {{/formatted_address}}\n\n {{#hasCoords}}\n <div data-container="map"></div>\n {{/hasCoords}}\n\n {{^hasCoords}}\n <div class="text-muted small" style="border: 1px dashed #e5e7eb; border-radius: 8px; padding: 12px;">\n No coordinates available for map preview\n </div>\n {{/hasCoords}}\n </div>\n '}async onInit(){if(this.hasCoords){const t=[{lat:this.latitude,lng:this.longitude,popup:this.formatted_address||""}];this._mapView=new e({markers:t,center:[this.latitude,this.longitude],zoom:13,height:this.height,tileLayer:this.tileLayer,showLayerControl:!0,containerId:"map"}),this.addChild(this._mapView)}}_toNumber(e){if(null==e||""===e)return null;const t=Number(e);return Number.isFinite(t)?t:null}}async function c({client:e=new LocationClient({basePath:"/api"}),details:t=null,place_id:s=null,id:i=null,title:n="Location Details",height:o=260,tileLayer:a="osm"}={}){let l=t;if(!l&&(s||i))try{const t=await e.placeDetails({place_id:s,id:i,session_token:e.sessionToken});l=t?.address||null}catch(c){l={formatted_address:"Unable to load place details",error:c?.message||"Unknown error"}}const d=new LocationDetailsView({details:l||{},height:o,tileLayer:a});return r.showDialog({title:n,body:d,size:"md",buttons:[{text:"Close",class:"btn-secondary",dismiss:!0,value:"close"}]})}class LocationPickerView extends n{constructor({client:e,minChars:t=3,debounceMs:s=200,placeholder:i="Search address",height:n=240,tileLayer:o="osm"}={}){super({className:"location-picker-view",template:'<div class="location-picker-view-root"></div>'}),this.client=e,this.minChars=t,this.debounceMs=s,this.placeholder=i,this.height=n,this.tileLayer=o,this._selected=null,this._timer=null,this._suggestions=[],this._elements={},this._previewView=null}getSelected(){return this._selected}async onAfterRender(){const e=document.createElement("div");e.style.display="grid",e.style.gap="10px";const t=document.createElement("input");t.type="text",t.className="form-control",t.placeholder=this.placeholder||"Search address",t.setAttribute("aria-label","Address search"),e.appendChild(t);const s=document.createElement("div");s.style.border="1px solid #e5e7eb",s.style.borderRadius="8px",s.style.background="#fff",s.style.boxShadow="0 8px 24px rgba(0,0,0,.08)",s.style.maxHeight="260px",s.style.overflowY="auto",s.style.display="none",e.appendChild(s);const i=document.createElement("div");e.appendChild(i),this.element.appendChild(e),this._elements={root:e,input:t,dd:s,previewHost:i},t.addEventListener("input",()=>{clearTimeout(this._timer),this._timer=setTimeout(()=>this._handleInput(),this.debounceMs)}),t.addEventListener("focus",()=>{(t.value||"").trim().length>=this.minChars&&this._handleInput()}),await this._renderPreview(null)}async _handleInput(){const e=(this._elements.input.value||"").trim();if(e.length<this.minChars)return this._elements.dd.style.display="none",void(this._elements.dd.innerHTML="");try{const t=await this.client.autocomplete(e),s=Array.isArray(t?.data)?t.data:[];this._suggestions=s.map(e=>({id:e.id,place_id:e.place_id,description:e.description,main_text:e.main_text,secondary_text:e.secondary_text,types:e.types})),await this._renderSuggestions()}catch(t){this._suggestions=[],await this._renderSuggestions()}}async _renderSuggestions(){const e=this._elements.dd;if(e.innerHTML="",!this._suggestions.length){const t=document.createElement("div");return t.style.padding="8px 12px",t.style.color="#6b7280",t.textContent="No results",e.appendChild(t),void(e.style.display="block")}this._suggestions.forEach((t,s)=>{const i=document.createElement("div");i.style.padding="8px 12px",i.style.cursor="pointer",i.style.display="flex",i.style.flexDirection="column",i.addEventListener("mouseenter",()=>{i.style.background="#f3f4f6"}),i.addEventListener("mouseleave",()=>{i.style.background="transparent"});const n=document.createElement("div");n.style.fontWeight="600",n.style.color="#111827",n.textContent=t.main_text||t.description||"";const o=document.createElement("div");o.style.fontSize="12px",o.style.color="#6b7280",o.textContent=t.secondary_text||"",i.appendChild(n),o.textContent&&i.appendChild(o),i.addEventListener("mousedown",e=>{e.preventDefault(),this._selectSuggestion(t)}),e.appendChild(i)}),e.style.display="block"}async _selectSuggestion(e){try{const t=e.place_id||e.id;if(!t)return;const s=await this.client.placeDetails({place_id:t,id:t,session_token:this.client.sessionToken}),i=s?.address||null;i?.formatted_address?this._elements.input.value=i.formatted_address:e.description&&(this._elements.input.value=e.description),this._selected=i||null,this._elements.dd.style.display="none",this._elements.dd.innerHTML="",await this._renderPreview(this._selected)}catch(t){}}async _renderPreview(e){if(this._elements.previewHost.innerHTML="",!e){const e=document.createElement("div");return e.style.border="1px dashed #e5e7eb",e.style.borderRadius="8px",e.style.padding="12px",e.style.color="#6b7280",e.textContent="No location selected",void this._elements.previewHost.appendChild(e)}try{this._previewView=new LocationDetailsView({details:e,height:this.height,tileLayer:this.tileLayer}),await this._previewView.render(!0,this._elements.previewHost)}catch{const t=document.createElement("div");t.style.border="1px dashed #e5e7eb",t.style.borderRadius="8px",t.style.padding="12px",t.textContent=e?.formatted_address||"Selected location",this._elements.previewHost.appendChild(t)}}async onBeforeDestroy(){clearTimeout(this._timer),await super.onBeforeDestroy()}}async function u({client:e=new LocationClient({basePath:"/api"}),title:t="Pick a Location",minChars:s=3,debounceMs:i=200,placeholder:n="Search address",confirmText:o="Select",height:a=240,tileLayer:l="osm"}={}){const d=new LocationPickerView({client:e,minChars:s,debounceMs:i,placeholder:n,height:a,tileLayer:l});return"ok"===await r.showDialog({title:t,body:d,size:"md",buttons:[{text:"Cancel",class:"btn-secondary",dismiss:!0,value:"cancel"},{text:o,class:"btn-primary",value:"ok"}]})&&d.getSelected()||null}class LocationFormPlugin{constructor({basePath:e="/api",mapping:t,registerFieldType:s=!0,fieldTypeName:i="address",attributeSelector:n="data-location",minChars:o=3,debounceMs:a=200,suppressBrowserAutocomplete:r=!0,autocompleteValue:l="new-password"}={}){this.id="location",this.client=new LocationClient({basePath:e}),this.mapping=t||{address1:"address1",city:"city",state_code:"state",postal_code:"postal_code",country_code:"country",latitude:"latitude",longitude:"longitude",formatted_address:"formatted_address",place_id:"place_id"},this.fieldTypeName=i,this.attributeSelector=n,this.minChars=o,this.debounceMs=a,this.suppressBrowserAutocomplete=!1!==r,this.autocompleteValue=l||"new-password",s&&(this.fieldTypes={[this.fieldTypeName]:(e,t)=>this.renderAddressField(e,t)})}renderAddressField(e,t){const s=this.suppressBrowserAutocomplete?`autocomplete="${this.autocompleteValue}" autocapitalize="off" autocorrect="off" spellcheck="false" inputmode="search"`:"",i={...t,type:"text",placeholder:t.placeholder||"Start typing an address",attrs:this.mergeAttrs(t.attrs,`${this.attributeSelector}="address" ${s} aria-autocomplete="list" role="combobox"`)};if("function"==typeof e.renderTextField)return e.renderTextField(i);if("function"==typeof e.renderInputField)return e.renderInputField(i,"text");const n=e.getFieldId?.(i.name)||`field_${i.name}`;return`\n <div class="mojo-form-control">\n ${i.label?`<label for="${n}" class="${e.options?.labelClass||"form-label"}">${i.label}</label>`:""}\n <input type="text" id="${n}" name="${i.name}" class="${e.options?.inputClass||"form-control"}"\n placeholder="${i.placeholder||""}" ${this.attributeSelector}="address" ${s} />\n </div>\n `}mergeAttrs(e,t){const s=(e||"").trim(),i=(t||"").trim();return s?i?`${s} ${i}`:s:i}onFormViewInit(e){}onAfterRender(e){if(e?.element)try{const t=`input[${this.attributeSelector}="address"]`;e.element.querySelectorAll(t).forEach(t=>{if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(n){}let s;const i=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(n){}try{s&&s()}catch(n){}setTimeout(()=>{i()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};i()})}catch(t){}}onFieldInit(e,t,s){try{const n=s?.type===this.fieldTypeName,o="address"===t?.getAttribute?.(this.attributeSelector);if(n||o){if(t.dataset&&"1"===t.dataset._locationBound)return;if(this.suppressBrowserAutocomplete)try{t.setAttribute("autocomplete",this.autocompleteValue),t.setAttribute("autocapitalize","off"),t.setAttribute("autocorrect","off"),t.setAttribute("spellcheck","false"),t.setAttribute("inputmode","search")}catch(i){}let s;const n=()=>{s=d(e,{client:this.client,field:t,mapping:this.mapping,minChars:this.minChars,debounceMs:this.debounceMs,onSelect:e=>{try{t.blur()}catch(i){}try{s&&s()}catch(i){}setTimeout(()=>{n()},this.debounceMs+50)}}),t.dataset._locationBound="1",this._trackDisposer(e,s)};n()}}catch(n){}}onFieldChange(e,t,s){}_trackDisposer(e,t){if(!t||"function"!=typeof t)return;if(!e)return;e._locationDisposers||Object.defineProperty(e,"_locationDisposers",{configurable:!0,enumerable:!1,writable:!0,value:[]}),e._locationDisposers.push(t);const s=t=>{if("function"==typeof e.on)try{return e.on(t,()=>{try{e._locationDisposers?.forEach(e=>{try{e()}catch{}})}finally{e._locationDisposers=[]}}),!0}catch{}return!1};s("before:destroy")||s("destroy")}}function p(e={}){const t=new LocationFormPlugin(e);return l.register(t)}export{o as Collection,LocationClient,LocationDetailsView,LocationFormPlugin,t as MapLibreView,e as MapView,s as MetricsCountryMapView,a as Model,n as View,p as registerLocationPlugin,c as showLocationDetailsDialog,u as showLocationPickerDialog,d as useLocationAutocomplete};
2
2
  //# sourceMappingURL=map.es.js.map