Prayer Progress: "What can this map do?" is the top of its own control column

- Prayer Progress's column becomes the engine's square grouped card (controls="square"). Its first
  control is MapInfoButton, which opens the gallery's MapInfo for the tab. The gallery's tabInfo
  'prayer-map' entry now uses trigger 'control', so the floating button hides while that control is
  on screen, and shows again (fail-open) whenever it is not.
- MapInfo, control mode: the first-visit pointer now points at the control. Its place is measured
  against the pane (which scrolls) and goes on the control's physical inner side, so an Arabic page,
  whose column mirrors to the left, gets the pointer on the right with the arrow reversed. It shows at
  every width: the under-1200px rule was measured for the floating pointer crossing each tab's top
  strip, and does not apply here.
- MapInfo, pointerKeep (prayer-map only): the popup offers "Keep tooltip activated" instead of the
  every-map "don't point again" box. It is off by default, and opening the popup retires the pointer.
  Checked, the pointer keeps showing on every load. It is stored per map.
- mapInfo.keepPointer in all 11 locales.
- Rebuilt doxa-simple-map and doxa-maps-spa, then tree and fix:sri.
This commit is contained in:
Doxa Maps 2026-09-30 03:31:15 +00:00
parent 915a317ebe
commit 48a1a747ba
24 changed files with 174 additions and 77 deletions

View file

@ -26,7 +26,9 @@
* 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. Both live in
* could be a mis-tap). The checkbox in the popup turns the pointer off on EVERY map. A map given
* `pointer-keep` offers "Keep tooltip activated" instead: opening alone retires its pointer, and the box
* keeps it showing on every load (see the prop). All of it 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.
*
@ -75,6 +77,12 @@ const props = defineProps({
* actually on screen (see "control mode" below).
*/
trigger: { type: String, default: 'floating', validator: (v) => ['floating', 'control'].includes(v) },
/**
* Offer "Keep tooltip activated" in the popup, IN PLACE OF the every-map "don't point again" box. Off by
* default: opening the popup once retires this map's pointer, as before. Checked: the pointer keeps
* showing on every load. Per map (its storageKey), so it answers only for the map that offers it.
*/
pointerKeep: { type: Boolean, default: false },
})
// ── locale and text ──────────────────────────────────────────────────────────────────────────────
@ -93,19 +101,24 @@ 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 */ } }
const pointing = ref(false)
const pointerOff = ref(false)
const keep = ref(false)
// A kept pointer outranks both "seen" and the every-map off switch: it is this map's own, later answer.
const wantPointer = () => keep.value || (!pointerOff.value && readStore(SEEN + props.storageKey) !== '1')
function refreshPointer() {
pointerOff.value = readStore(OFF) === 'off'
pointing.value = !pointerOff.value && readStore(SEEN + props.storageKey) !== '1'
keep.value = props.pointerKeep && readStore(KEEP + props.storageKey) === '1'
pointing.value = wantPointer()
}
// Another map on the page (same document) or another tab (storage event) turned the pointer off.
function onPointerOff() { refreshPointer() }
function onStorage(e) { if (!e || e.key === OFF || e.key === SEEN + props.storageKey) refreshPointer() }
function onStorage(e) { if (!e || e.key === OFF || e.key === SEEN + props.storageKey || e.key === KEEP + props.storageKey) refreshPointer() }
onMounted(() => {
refreshPointer()
window.addEventListener(OFF_EVENT, onPointerOff)
@ -118,9 +131,13 @@ onBeforeUnmount(() => {
function setPointerOff(off) {
pointerOff.value = off
writeStore(OFF, off ? 'off' : 'on')
pointing.value = !off && readStore(SEEN + props.storageKey) !== '1'
pointing.value = wantPointer()
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)
@ -131,13 +148,16 @@ const failed = reactive({})
async function show() {
opens.value++
open.value = true
// With the keep box on offer, OPENING the popup is what retires the pointer (the box is right there
// to say otherwise), so a reader who opens it and reloads without closing is not pointed at again.
if (props.pointerKeep) writeStore(SEEN + props.storageKey, '1')
await nextTick()
closeEl.value?.focus()
}
async function hide() {
open.value = false
writeStore(SEEN + props.storageKey, '1')
pointing.value = false
pointing.value = keep.value
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
@ -169,9 +189,35 @@ function measureControl() {
if (c && c.isConnected && !inert(c)) {
const r = c.getBoundingClientRect()
shown = r.width > 0 && r.height > 0 && r.bottom > 0 && r.right > 0 && r.top < window.innerHeight && r.left < window.innerWidth
if (shown) {
const n = placeBeside(r), o = ctlAt.value
if (!n || !o || n.side !== o.side || n.top !== o.top || n.left !== o.left || n.right !== o.right) ctlAt.value = n
}
}
controlShown.value = shown
}
// ★ THE POINTER FOLLOWS THE CONTROL, WHEREVER THE MAP PUT IT. The floating pointer is anchored to our
// own button by CSS; the control lives in the map's shadow root, so its place is MEASURED, against the
// box our absolutely-placed pointer is laid out in (our offsetParent, the pane, which scrolls: hence
// scrollTop). It goes on the control's physical inner side: a right-to-left map mirrors its column to
// the left edge, and that is read from where the control IS, never assumed from the page's direction.
const GAP = 10
function placeBeside(r) {
const cb = rootEl.value && rootEl.value.offsetParent
if (!cb) return null
const b = cb.getBoundingClientRect()
const x0 = b.left + cb.clientLeft, y0 = b.top + cb.clientTop
const top = Math.round(r.top - y0 + cb.scrollTop + r.height / 2)
return (r.left + r.width / 2) > (x0 + cb.clientWidth / 2)
? { side: 'right', top, right: Math.round(x0 + cb.clientWidth - r.left + GAP) }
: { side: 'left', top, left: Math.round(r.right - x0 + GAP) }
}
const ctlAt = ref(null)
const ctlStyle = () => {
const p = ctlAt.value
return p.side === 'right' ? { top: p.top + 'px', right: p.right + 'px' } : { top: p.top + 'px', left: p.left + 'px' }
}
const onResize = () => measureControl()
function adopt(el) {
if (!el || el === control.value) { measureControl(); return }
try { io?.disconnect(); ro?.disconnect() } catch { /* already gone */ }
@ -202,6 +248,7 @@ onMounted(() => {
pane.addEventListener('dxmaps:mapinfo-control', onControl)
pane.addEventListener('dxmaps:mapinfo-open', onOpenRequest)
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.
try { window.dispatchEvent(new CustomEvent('dxmaps:mapinfo-host')) } catch { /* no CustomEvent */ }
})
@ -212,6 +259,7 @@ onBeforeUnmount(() => {
}
try { io?.disconnect(); ro?.disconnect() } catch { /* already gone */ }
if (timer) window.clearInterval(timer)
window.removeEventListener('resize', onResize)
})
watch(open, (v) => { control.value?.setAttribute('aria-expanded', String(v)) })
const floatingShown = () => props.trigger !== 'control' || !controlShown.value
@ -281,6 +329,16 @@ useShadowStyles(`
.mi-pointer svg{ width:16px; height:16px; flex:none; }
@keyframes mi-nudge{ 0%,100%{ transform:translateX(0) } 50%{ transform:translateX(6px) } }
@keyframes mi-nudge-left{ 0%,100%{ transform:translateX(0) } 50%{ transform:translateX(-6px) } }
/* Control mode: top/left/right come from placeBeside (inline), centred on the control by translate,
which composes with the nudge's transform. Laid out left-to-right on purpose: the arrow's side is the
control's PHYSICAL side, measured, so the page direction must not flip the row, the glyph or the
nudge (the span keeps the text's own direction). (0,3,0) so the 1200px rule below does not hide it:
that rule measured the floating pointer crossing each tab's top strip, and this one is placed and
measured beside one map's own control instead. */
.mi .mi-pointer.mi-ctl{ display:flex; direction:ltr; translate:0 -50%; }
.mi .mi-pointer.mi-ctl-left{ flex-direction:row-reverse; animation-name:mi-nudge-left; }
.mi .mi-pointer.mi-ctl-right{ animation-name:mi-nudge; }
.mi[dir="rtl"] .mi-pointer.mi-ctl svg{ transform:none; }
/* z-index above every map's own top layer: PinHint's first-visit pill sits at 2147482000, so at 1000 it
floated bright and clickable over the dimmed backdrop (gospel). The dialog is a child of this fixed,
@ -382,7 +440,7 @@ useShadowStyles(`
/* This component animates (the pointer's nudge), so it carries its own reduced-motion rule where its
styles are injected: a page-level rule cannot reach into a shadow root. */
@media (prefers-reduced-motion: reduce){
.mi-pointer{ animation:none; }
.mi-pointer, .mi .mi-pointer.mi-ctl{ animation:none; }
}
`, 'map-info')
</script>
@ -406,6 +464,14 @@ useShadowStyles(`
<path v-else d="M2 8h11M9 3.5 13.5 8 9 12.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
<!-- Control mode: the same pointer, beside the map's own control (placed by measurement, placeBeside). -->
<div v-if="pointing && !open && !floatingShown() && ctlAt" class="mi-pointer mi-ctl" :class="'mi-ctl-' + ctlAt.side" :style="ctlStyle()" aria-hidden="true">
<span :dir="isRtl() ? 'rtl' : 'ltr'">{{ t('mapInfo.pointer') }}</span>
<svg viewBox="0 0 16 16" focusable="false">
<path v-if="ctlAt.side === 'left'" d="M14 8H3M7 3.5 2.5 8 7 12.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<path v-else d="M2 8h11M9 3.5 13.5 8 9 12.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
<div v-if="open" class="mi-scrim" @click.self="hide" @keydown="onKey">
<div ref="dialogEl" class="mi-dialog" role="dialog" aria-modal="true" :aria-labelledby="'mi-title-' + storageKey">
@ -487,7 +553,11 @@ useShadowStyles(`
</div>
<div class="mi-foot">
<label>
<label v-if="pointerKeep">
<input type="checkbox" :checked="keep" @change="setKeep($event.target.checked)" />
<span>{{ t('mapInfo.keepPointer') }}</span>
</label>
<label v-else>
<input type="checkbox" :checked="pointerOff" @change="setPointerOff($event.target.checked)" />
<span>{{ t('mapInfo.pointerOff') }}</span>
</label>

View file

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

View file

@ -16,6 +16,7 @@
"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 aktiviert 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,6 +16,7 @@
"moreRows": "More rows in the full spreadsheet: {n}",
"openFull": "Open full size",
"pointerOff": "Don't point at this button again",
"keepPointer": "Keep tooltip activated",
"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,6 +16,7 @@
"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,6 +16,7 @@
"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,6 +16,7 @@
"moreRows": "पूरी स्प्रेडशीट में और पंक्तियाँ: {n}",
"openFull": "पूरे आकार में खोलें",
"pointerOff": "इस बटन की ओर फिर से इशारा न करें",
"keepPointer": "टूलटिप चालू रखें",
"tab": {
"upg-100": {
"lead": "UPG-100 सूची एक ही नक्शे पर: अपहुँचे जन-समूह, क्षेत्रों में बाँटे गए, जिन्हें आप प्रार्थना में अपना सकते हैं।",

View file

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

View file

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

View file

@ -106,6 +106,10 @@ export const TAB_INFO = {
},
'prayer-map': {
storageKey: 'prayer-map',
// 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

@ -51,6 +51,9 @@ import HelpButton from '@map/components/map-controls/HelpButton.vue'
import HamburgerButton from '@map/components/map-controls/HamburgerButton.vue'
import GeocoderComponent from '@map/components/map-controls/GeocoderComponent.vue'
import ShareButton from '@map/components/map-controls/ShareButton.vue'
// By path, not through map-controls/index.js: the barrel would pull the 'mapInfo' catalogue area into
// every bundle that imports a control.
import MapInfoButton from '@map/components/map-controls/MapInfoButton.vue'
// ─── Shadow DOM style injection ──────────────────────────────────────────────
useShadowStyles(`
@ -1526,7 +1529,12 @@ onBeforeUnmount(() => {
collapses its mobile top offset when no search pill is rendered.
When the flag is flipped back on, the toolbar reserves space
below the geocoder again with no further changes here. -->
<MapToolbar v-if="appReady" :has-geocoder="FEATURES.geocoder">
<!-- controls="square": this map's column is the engine's grouped card, not separate circles.
The TOP control is "What can this map do?": it opens the gallery's MapInfo for this tab,
which then hides its own floating button. It renders only where a MapInfo answers it, so a
standalone page (DX Life's home, the leaf) has no dead button. -->
<MapToolbar v-if="appReady" :has-geocoder="FEATURES.geocoder" controls="square">
<MapInfoButton :is-dark="isDark" />
<ZoomInButton :map="map" :is-dark="isDark" />
<ZoomOutButton :map="map" :is-dark="isDark" />
<LocationButton :map="map" :is-dark="isDark" />

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-/WBGcZg4flWOec4ecIIdNIwzt2WolZLtelRsoEAnebRR7QMZPSrUpkfe/V22q5Xd" crossorigin="anonymous"></script>
<script src="../../doxa-maps-spa.js" integrity="sha384-953GDTereBaH6E44k1JIYWzMZtiaiwRVosEG2MqYARwl9loRarojvZ0h3pCuFULQ" 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-/WBGcZg4flWOec4ecIIdNIwzt2WolZLtelRsoEAnebRR7QMZPSrUpkfe/V22q5Xd" crossorigin="anonymous"></script>
<script src="../doxa-maps-spa.js" integrity="sha384-953GDTereBaH6E44k1JIYWzMZtiaiwRVosEG2MqYARwl9loRarojvZ0h3pCuFULQ" 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-/WBGcZg4flWOec4ecIIdNIwzt2WolZLtelRsoEAnebRR7QMZPSrUpkfe/V22q5Xd",
"integrity": "sha384-953GDTereBaH6E44k1JIYWzMZtiaiwRVosEG2MqYARwl9loRarojvZ0h3pCuFULQ",
"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-/WBGcZg4flWOec4ecIIdNIwzt2WolZLtelRsoEAnebRR7QMZPSrUpkfe/V22q5Xd\" 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-953GDTereBaH6E44k1JIYWzMZtiaiwRVosEG2MqYARwl9loRarojvZ0h3pCuFULQ\" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz",
"integrity": "sha384-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt",
"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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz\" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz\" 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-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt\" 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-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt\" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz" crossorigin="anonymous"></script>
<script src="../doxa-simple-map.js" integrity="sha384-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt" 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-/WBGcZg4flWOec4ecIIdNIwzt2WolZLtelRsoEAnebRR7QMZPSrUpkfe/V22q5Xd",
"integrity": "sha384-953GDTereBaH6E44k1JIYWzMZtiaiwRVosEG2MqYARwl9loRarojvZ0h3pCuFULQ",
"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-/WBGcZg4flWOec4ecIIdNIwzt2WolZLtelRsoEAnebRR7QMZPSrUpkfe/V22q5Xd\" 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-953GDTereBaH6E44k1JIYWzMZtiaiwRVosEG2MqYARwl9loRarojvZ0h3pCuFULQ\" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz",
"integrity": "sha384-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt",
"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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz\" 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-zpRoXQ8RfdoNCpMyKx3EyiqWvnXCQ8NVOyGPfqIun+MiFhR3a+SS+ZzvJBT+3qZz\" 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-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt\" 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-5pUXPKkzBznEBqIoa5soXOBvZIXIYhCIdKxDmWG5P7q7E0dT3yUDPxMVVtGRfKRt\" 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": [
{