Merge remote-tracking branch 'origin/development' into studio/phone-place

# Conflicts:
#	public/dx-maps/doxa-maps-spa/doxa-maps-spa.js
#	public/dx-maps/doxa-maps-spa/doxa-maps-spa/app/index.html
#	public/dx-maps/doxa-maps-spa/doxa-maps-spa/index.html
#	public/dx-maps/doxa-maps-spa/manifest.json
#	public/dx-maps/doxa-simple-map/doxa-simple-map.js
#	public/dx-maps/doxa-simple-map/doxa-simple-map/adoption/index.html
#	public/dx-maps/doxa-simple-map/doxa-simple-map/engagement/index.html
#	public/dx-maps/doxa-simple-map/doxa-simple-map/index.html
#	public/dx-maps/doxa-simple-map/doxa-simple-map/prayer/index.html
#	public/dx-maps/manifest.json
This commit is contained in:
Doxa Maps 2026-09-30 05:03:17 +00:00
commit 17ce1bf9a9
24 changed files with 141 additions and 138 deletions

View file

@ -30,9 +30,9 @@ const pointerDoneThisLoad = new Set()
* Relative srcs resolve against `assetsBase`, so the same list works in any tree.
*
* ★ THE POINTER GOES FOR GOOD, per map, once its popup has been opened AND closed (opening alone
* could be a mis-tap). The checkbox in the popup turns the pointer off on EVERY map. A map given
* `pointer-keep` points ONCE PER PAGE LOAD instead, and offers "Keep tooltip turned on" in place of that
* box (see the prop). What is stored lives in
* could be a mis-tap). The checkbox in the popup turns the pointer off on EVERY map. A map whose ⓘ is
* its own control (trigger 'control') points ONCE PER PAGE LOAD instead, with no checkbox: see "the
* page-load pointer" below. What is stored lives in
* localStorage, read through a guard: private browsing and sandboxed frames throw on access, and a
* hint must never be the thing that breaks a page. The button itself always stays.
*
@ -81,15 +81,13 @@ const props = defineProps({
* actually on screen (see "control mode" below).
*/
trigger: { type: String, default: 'floating', validator: (v) => ['floating', 'control'].includes(v) },
/**
* The pointer model of a map that offers "Keep tooltip turned on" (IN PLACE OF the every-map "don't
* point again" box). Its pointer shows ONCE PER PAGE LOAD: the first time the map is on screen in that
* load. Opening the popup hides it for the rest of that load only, and the next full load shows it
* again: page-load memory (pointerDoneThisLoad, below), never storage. The box, off by default, keeps it
* showing after the popup closes and on every appearance; that preference IS stored, per map.
*/
pointerKeep: { type: Boolean, default: false },
})
// ★ THE PAGE-LOAD POINTER (control mode). The pointer beside the map's own ⓘ shows ONCE PER PAGE LOAD:
// the first time that control is on screen in the load. It goes for the rest of the load once the reader
// opens the popup OR leaves the tab (the pane is hidden or inert), and a full reload shows it again. That
// is page-load memory (pointerDoneThisLoad), never storage, so the popup offers no checkbox: the
// every-map "don't point again" box belongs to the floating model, which stores its answer.
const pageLoad = () => props.trigger === 'control'
// ── locale and text ──────────────────────────────────────────────────────────────────────────────
// `opens` is read by every lookup, so opening the popup re-reads the page-global locale on hosts
@ -107,7 +105,6 @@ const isRtl = () => lang() === 'ar'
// ── storage, guarded ─────────────────────────────────────────────────────────────────────────────
const SEEN = 'dxmaps.mapInfo.seen.'
const OFF = 'dxmaps.mapInfo.pointer'
const KEEP = 'dxmaps.mapInfo.keep.'
const OFF_EVENT = 'dxmaps:mapinfo-pointer-off'
function readStore(k) { try { return window.localStorage.getItem(k) } catch { return null } }
function writeStore(k, v) { try { window.localStorage.setItem(k, v) } catch { /* private mode: in-memory only */ } }
@ -115,33 +112,22 @@ function writeStore(k, v) { try { window.localStorage.setItem(k, v) } catch { /*
// ★ PAGE-LOAD MEMORY: pointerDoneThisLoad, in the plain <script> block at the top of this file, is shared
// by every MapInfo on the page, so it must live in module scope: the gallery REMOUNTS a heavy tab's MapInfo
// on each switch back, and component state dies with the instance. It holds the storageKeys whose
// page-load pointer (pointerKeep) is done for this load; a full reload starts empty. (It sits ABOVE
// page-load pointer is done for this load; a full reload starts empty. (It sits ABOVE
// <script setup> so an ordering check reads it as declared before use; it carries no comment there
// because the library index takes this component's description from the file's first comment.)
const pointing = ref(false)
const pointerOff = ref(false)
const keep = ref(false)
function refreshPointer() {
pointerOff.value = readStore(OFF) === 'off'
if (props.pointerKeep) {
keep.value = readStore(KEEP + props.storageKey) === '1'
pointing.value = keep.value || !pointerDoneThisLoad.has(props.storageKey)
return
}
if (pageLoad()) { pointing.value = !pointerDoneThisLoad.has(props.storageKey); return }
pointing.value = !pointerOff.value && readStore(SEEN + props.storageKey) !== '1'
}
// Another map on the page (same document) or another tab (storage event) turned the pointer off. A
// page-load pointer answers only its own keep box: the every-map switch is not offered beside it, and a
// switch the reader cannot see on this map must not silently take its pointer away.
function onPointerOff() { if (!props.pointerKeep) refreshPointer() }
// page-load pointer ignores both: the every-map switch is not offered beside it, and a switch the reader
// cannot see on this map must not silently take its pointer away.
function onPointerOff() { if (!pageLoad()) refreshPointer() }
function onStorage(e) {
if (props.pointerKeep) {
if (!e || e.key === KEEP + props.storageKey) {
keep.value = readStore(KEEP + props.storageKey) === '1'
if (keep.value) pointing.value = true
}
return
}
if (pageLoad()) return
if (!e || e.key === OFF || e.key === SEEN + props.storageKey) refreshPointer()
}
onMounted(() => {
@ -159,10 +145,6 @@ function setPointerOff(off) {
pointing.value = !off && readStore(SEEN + props.storageKey) !== '1'
try { window.dispatchEvent(new CustomEvent(OFF_EVENT)) } catch { /* no window */ }
}
function setKeep(on) {
keep.value = on
writeStore(KEEP + props.storageKey, on ? '1' : '0')
}
// ── open / close ─────────────────────────────────────────────────────────────────────────────────
const open = ref(false)
@ -174,14 +156,14 @@ async function show() {
opens.value++
open.value = true
// A page-load pointer is done for this load once the popup has been opened (in memory only).
if (props.pointerKeep) pointerDoneThisLoad.add(props.storageKey)
if (pageLoad()) pointerDoneThisLoad.add(props.storageKey)
await nextTick()
closeEl.value?.focus()
}
async function hide() {
open.value = false
if (!props.pointerKeep) writeStore(SEEN + props.storageKey, '1')
pointing.value = props.pointerKeep && keep.value
if (!pageLoad()) writeStore(SEEN + props.storageKey, '1')
pointing.value = false
await nextTick()
// Back to whichever button the reader can see: the map's own control when it is on screen, else ours.
const back = props.trigger === 'control' && controlShown.value && control.value ? control.value : btnEl.value
@ -213,20 +195,29 @@ function inert(el) {
// tab that mounts hidden, or waits in an inert pane, keeps its full grace for when it is opened): if no
// control arrives within CONTROL_GRACE_MS of visible time, our button shows (fail-open). Once a control
// has announced itself, the measurement below decides alone, so a hidden column still gives our button.
// A map that knows its control is on its way says so ('dxmaps:mapinfo-pending', { pending: true }), and
// then the clock is only a BACKSTOP (CONTROL_BACKSTOP_MS, for a failure nobody reported); a map that
// reports its failure ({ pending: false }) ends the wait at once.
const CONTROL_GRACE_MS = 30000
const CONTROL_BACKSTOP_MS = 90000
let controlPending = false
const awaitingControl = ref(props.trigger === 'control')
let graceFrom = 0
function tickGrace() {
if (!awaitingControl.value) return
function paneVisible() {
const p = rootEl.value && rootEl.value.parentElement
const r = p && p.getBoundingClientRect()
const visible = !!p && !inert(p) && r.width > 0 && r.height > 0
if (!visible) { graceFrom = 0; return }
return !!p && !inert(p) && r.width > 0 && r.height > 0
}
function tickGrace() {
if (!awaitingControl.value) return
if (!paneVisible()) { graceFrom = 0; return }
if (!graceFrom) graceFrom = Date.now()
else if (Date.now() - graceFrom >= CONTROL_GRACE_MS) awaitingControl.value = false
else if (Date.now() - graceFrom >= (controlPending ? CONTROL_BACKSTOP_MS : CONTROL_GRACE_MS)) awaitingControl.value = false
}
function measureControl() {
tickGrace()
// Leaving the tab ends a page-load pointer that has already been on screen (see the page-load pointer).
if (pageLoad() && pointing.value && pointerDoneThisLoad.has(props.storageKey) && !paneVisible()) pointing.value = false
const c = control.value
let shown = false
if (c && c.isConnected && !inert(c)) {
@ -279,6 +270,12 @@ function onControl(e) {
if (e.detail) e.detail.ack = true
adopt(e.detail && e.detail.el)
}
function onPending(e) {
if (!ours(e)) return
e.stopPropagation()
controlPending = !!(e.detail && e.detail.pending)
if (!controlPending && !control.value) awaitingControl.value = false
}
function onOpenRequest(e) {
if (!ours(e)) return
e.stopPropagation()
@ -291,6 +288,7 @@ onMounted(() => {
if (!pane) return
pane.addEventListener('dxmaps:mapinfo-control', onControl)
pane.addEventListener('dxmaps:mapinfo-open', onOpenRequest)
pane.addEventListener('dxmaps:mapinfo-pending', onPending)
timer = window.setInterval(measureControl, 1500)
window.addEventListener('resize', onResize)
// A control that mounted before we did asked into the void; tell it to ask again.
@ -300,6 +298,7 @@ onBeforeUnmount(() => {
if (pane) {
pane.removeEventListener('dxmaps:mapinfo-control', onControl)
pane.removeEventListener('dxmaps:mapinfo-open', onOpenRequest)
pane.removeEventListener('dxmaps:mapinfo-pending', onPending)
}
try { io?.disconnect(); ro?.disconnect() } catch { /* already gone */ }
if (timer) window.clearInterval(timer)
@ -310,7 +309,7 @@ const floatingShown = () => props.trigger !== 'control' || (!awaitingControl.val
// The pointer beside the control has been on screen: this page load has pointed once, so a remount (the
// gallery rebuilds a heavy tab on every switch back) does not point again. Marked on SCREEN, not on mount:
// a tab's MapInfo can mount inside a hidden pane.
watch(() => props.pointerKeep && pointing.value && !open.value && !floatingShown() && !!ctlAt.value,
watch(() => pageLoad() && pointing.value && !open.value && !floatingShown() && !!ctlAt.value,
(on) => { if (on) pointerDoneThisLoad.add(props.storageKey) }, { immediate: true })
// ★ aria-modal PROMISES THE READER THAT TAB STAYS INSIDE, so keep it there: Tab from the last control
// wraps to the first, Shift+Tab from the first to the last. The active element is read from the shadow
@ -601,12 +600,8 @@ useShadowStyles(`
<p v-if="noteKey || note" class="mi-note">{{ text(noteKey, note) }}</p>
</div>
<div class="mi-foot">
<label v-if="pointerKeep">
<input type="checkbox" :checked="keep" @change="setKeep($event.target.checked)" />
<span>{{ t('mapInfo.keepPointer') }}</span>
</label>
<label v-else>
<div v-if="!pageLoad()" class="mi-foot">
<label>
<input type="checkbox" :checked="pointerOff" @change="setPointerOff($event.target.checked)" />
<span>{{ t('mapInfo.pointerOff') }}</span>
</label>

View file

@ -16,7 +16,6 @@
"moreRows": "صفوف إضافية في جدول البيانات الكامل: {n}",
"openFull": "فتح بالحجم الكامل",
"pointerOff": "لا تُشِر إلى هذا الزر مرة أخرى",
"keepPointer": "إبقاء التلميح مفعّلًا",
"tab": {
"upg-100": {
"lead": "قائمة UPG-100 على خريطة واحدة: الشعوب غير المُبشَّرة، مجمّعة في مناطق يمكنك أن تتبنّاها بالصلاة.",

View file

@ -16,7 +16,6 @@
"moreRows": "Weitere Zeilen in der vollständigen Tabelle: {n}",
"openFull": "In voller Größe öffnen",
"pointerOff": "Nicht mehr auf diese Schaltfläche hinweisen",
"keepPointer": "Tooltip eingeschaltet lassen",
"tab": {
"upg-100": {
"lead": "Die UPG-100-Liste auf einer Karte: unerreichte Volksgruppen, zusammengefasst in Zonen, die Sie im Gebet übernehmen können.",

View file

@ -16,7 +16,6 @@
"moreRows": "More rows in the full spreadsheet: {n}",
"openFull": "Open full size",
"pointerOff": "Don't point at this button again",
"keepPointer": "Keep tooltip turned on",
"tab": {
"upg-100": {
"lead": "The UPG-100 list on one map: unreached people groups, gathered into zones you can adopt in prayer.",

View file

@ -16,7 +16,6 @@
"moreRows": "Más filas en la hoja de cálculo completa: {n}",
"openFull": "Abrir a tamaño completo",
"pointerOff": "No volver a señalar este botón",
"keepPointer": "Mantener activada la sugerencia",
"tab": {
"upg-100": {
"lead": "La lista UPG-100 en un solo mapa: pueblos no alcanzados, agrupados en zonas que usted puede adoptar en oración.",

View file

@ -16,7 +16,6 @@
"moreRows": "Lignes supplémentaires dans le tableur complet : {n}",
"openFull": "Ouvrir en taille réelle",
"pointerOff": "Ne plus signaler ce bouton",
"keepPointer": "Garder l'infobulle activée",
"tab": {
"upg-100": {
"lead": "La liste UPG-100 sur une seule carte : les peuples non atteints, regroupés en zones que vous pouvez adopter dans la prière.",

View file

@ -16,7 +16,6 @@
"moreRows": "पूरी स्प्रेडशीट में और पंक्तियाँ: {n}",
"openFull": "पूरे आकार में खोलें",
"pointerOff": "इस बटन की ओर फिर से इशारा न करें",
"keepPointer": "टूलटिप चालू रखें",
"tab": {
"upg-100": {
"lead": "UPG-100 सूची एक ही नक्शे पर: अपहुँचे जन-समूह, क्षेत्रों में बाँटे गए, जिन्हें आप प्रार्थना में अपना सकते हैं।",

View file

@ -16,7 +16,6 @@
"moreRows": "Altre righe nel foglio di calcolo completo: {n}",
"openFull": "Apri a grandezza intera",
"pointerOff": "Non indicare più questo pulsante",
"keepPointer": "Mantieni attivo il suggerimento",
"tab": {
"upg-100": {
"lead": "La lista UPG-100 su un'unica mappa: popoli non raggiunti, raggruppati in zone che puoi adottare nella preghiera.",

View file

@ -16,7 +16,6 @@
"moreRows": "Mais linhas na planilha completa: {n}",
"openFull": "Abrir em tamanho real",
"pointerOff": "Não apontar mais para este botão",
"keepPointer": "Manter a dica ativada",
"tab": {
"upg-100": {
"lead": "A lista UPG-100 em um só mapa: povos não alcançados, agrupados em zonas que você pode adotar em oração.",

View file

@ -16,7 +16,6 @@
"moreRows": "Rânduri suplimentare în foaia de calcul completă: {n}",
"openFull": "Deschide la dimensiune completă",
"pointerOff": "Nu mai indica acest buton",
"keepPointer": "Păstrează indicația activă",
"tab": {
"upg-100": {
"lead": "Lista UPG-100 pe o singură hartă: popoare neatinse, grupate în zone pe care le poți adopta în rugăciune.",

View file

@ -16,7 +16,6 @@
"moreRows": "Ещё строк в полной таблице: {n}",
"openFull": "Открыть в полном размере",
"pointerOff": "Больше не указывать на эту кнопку",
"keepPointer": "Оставить подсказку включённой",
"tab": {
"upg-100": {
"lead": "Список UPG-100 на одной карте: недостигнутые народы, объединённые в зоны, которые вы можете взять на молитвенное попечение.",

View file

@ -16,7 +16,6 @@
"moreRows": "完整电子表格中的更多行:{n}",
"openFull": "以完整尺寸打开",
"pointerOff": "不再指向这个按钮",
"keepPointer": "保持提示开启",
"tab": {
"upg-100": {
"lead": "UPG-100 名单尽在一张地图上:未得之民按区域分组,你可以在祷告中认领一个区域。",

View file

@ -109,7 +109,6 @@ export const TAB_INFO = {
// The map draws the button as the top of its own control column (MapInfoButton); pos still places
// this component's fallback button, shown whenever that control is not on screen.
trigger: 'control',
pointerKeep: true,
pos: { side: '56px' },
leadKey: 'mapInfo.tab.prayer-map.lead',
takeawayKey: 'mapInfo.tab.prayer-map.takeaway',

View file

@ -1346,8 +1346,26 @@ async function onMapReady(normalizedPeopleGroups) {
// (page usually reloads on Polylang switch, but this keeps things robust).
watch(locale, (loc) => applyBasemapLocale(loc))
// ─── "What can this map do?" is on its way ────────────────────────────────────
// The control column (with its ⓘ on top) renders only at appReady, after the style AND the people-group
// data, which can take tens of seconds on a slow phone. So at mount this map tells the gallery's MapInfo
// that a control is COMING, and the MapInfo waits for the map rather than a clock, instead of showing its
// floating ⓘ and swapping it out later. A known failure (mapError) or an unmount withdraws the promise, so
// the floating ⓘ shows at once: there is always a way to open the popup. With no MapInfo (a standalone
// page) nothing answers and nothing changes.
function announceInfoPending(pending) {
const el = dsmRoot.value
if (!el) return
try { el.dispatchEvent(new CustomEvent('dxmaps:mapinfo-pending', { bubbles: true, composed: true, detail: { pending } })) } catch { /* no CustomEvent */ }
}
// A MapInfo that mounted after this map asked into the void; it announces itself, and we answer again.
const reannounceInfoPending = () => { if (!appReady.value && !mapError.value) announceInfoPending(true) }
watch(mapError, (e) => { if (e) announceInfoPending(false) })
// ─── Lifecycle — cloned from MapComponent.mounted ─────────────────────────────
onMounted(async () => {
announceInfoPending(true)
window.addEventListener('dxmaps:mapinfo-host', reannounceInfoPending)
activeTabId.value = tabs.value[0]?.id ?? 'prayer'
// Initial breakpoint — will be refined immediately by ResizeObserver below
uiStore.updateBreakpoint(window.innerWidth)
@ -1403,6 +1421,8 @@ onMounted(async () => {
})
onBeforeUnmount(() => {
window.removeEventListener('dxmaps:mapinfo-host', reannounceInfoPending)
if (!appReady.value) announceInfoPending(false)
mapLayers.stopPrayerGlow()
clearActiveLayer()
mapStore.unregisterMap(mapId)

File diff suppressed because one or more lines are too long

View file

@ -61,7 +61,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-maps-spa> custom element. -->
<script src="../../doxa-maps-spa.js" integrity="sha384-89Pp72vZrHYxf1y2J90nwHpQAKKEcMhA9AZY7uNgnhwIjJJA+kOrHA31QgnWw2Z8" crossorigin="anonymous"></script>
<script src="../../doxa-maps-spa.js" integrity="sha384-xn3zFdqVF7Pley9nROpDgBqCgqnfjy6nbxgi1K/Fm2HmM54socQUviO0tafUdTqb" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -61,7 +61,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-maps-spa> custom element. -->
<script src="../doxa-maps-spa.js" integrity="sha384-89Pp72vZrHYxf1y2J90nwHpQAKKEcMhA9AZY7uNgnhwIjJJA+kOrHA31QgnWw2Z8" crossorigin="anonymous"></script>
<script src="../doxa-maps-spa.js" integrity="sha384-xn3zFdqVF7Pley9nROpDgBqCgqnfjy6nbxgi1K/Fm2HmM54socQUviO0tafUdTqb" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -361,7 +361,7 @@
"bundle": "doxa-maps-spa",
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"output": "dx-maps/doxa-maps-spa/doxa-maps-spa.js",
"integrity": "sha384-89Pp72vZrHYxf1y2J90nwHpQAKKEcMhA9AZY7uNgnhwIjJJA+kOrHA31QgnWw2Z8",
"integrity": "sha384-xn3zFdqVF7Pley9nROpDgBqCgqnfjy6nbxgi1K/Fm2HmM54socQUviO0tafUdTqb",
"elements": [
"doxa-maps-spa"
],
@ -389,7 +389,7 @@
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"embed": {
"iframe": "<iframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-89Pp72vZrHYxf1y2J90nwHpQAKKEcMhA9AZY7uNgnhwIjJJA+kOrHA31QgnWw2Z8\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-xn3zFdqVF7Pley9nROpDgBqCgqnfjy6nbxgi1K/Fm2HmM54socQUviO0tafUdTqb\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
},
"instances": [
{
@ -462,7 +462,7 @@
"bundle": "doxa-simple-map",
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"output": "dx-maps/doxa-simple-map/doxa-simple-map.js",
"integrity": "sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH",
"integrity": "sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM",
"elements": [
"doxa-map",
"doxa-simple-map"
@ -482,8 +482,8 @@
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -70,7 +70,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../../doxa-simple-map.js" integrity="sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -70,7 +70,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../../doxa-simple-map.js" integrity="sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -70,7 +70,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../doxa-simple-map.js" integrity="sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH" crossorigin="anonymous"></script>
<script src="../doxa-simple-map.js" integrity="sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -70,7 +70,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-map> custom element. -->
<script src="../../doxa-simple-map.js" integrity="sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -361,7 +361,7 @@
"bundle": "doxa-maps-spa",
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"output": "dx-maps/doxa-maps-spa/doxa-maps-spa.js",
"integrity": "sha384-89Pp72vZrHYxf1y2J90nwHpQAKKEcMhA9AZY7uNgnhwIjJJA+kOrHA31QgnWw2Z8",
"integrity": "sha384-xn3zFdqVF7Pley9nROpDgBqCgqnfjy6nbxgi1K/Fm2HmM54socQUviO0tafUdTqb",
"elements": [
"doxa-maps-spa"
],
@ -389,7 +389,7 @@
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"embed": {
"iframe": "<iframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-89Pp72vZrHYxf1y2J90nwHpQAKKEcMhA9AZY7uNgnhwIjJJA+kOrHA31QgnWw2Z8\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-xn3zFdqVF7Pley9nROpDgBqCgqnfjy6nbxgi1K/Fm2HmM54socQUviO0tafUdTqb\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
},
"instances": [
{
@ -462,7 +462,7 @@
"bundle": "doxa-simple-map",
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"output": "dx-maps/doxa-simple-map/doxa-simple-map.js",
"integrity": "sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH",
"integrity": "sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM",
"elements": [
"doxa-map",
"doxa-simple-map"
@ -482,8 +482,8 @@
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-A1s+I+d0avERLG0kEo0+tFt1T2IpA8LsScPb1Rc0rECo8nt3e7dNxVYB5J+yzFzH\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-SaQaogmmi81KkwjSQCe5zFN5RJH88Y9kpOum8xeJkJLBRIAD34k2cl/sPBfE/hhM\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
},
"instances": [
{