Take main
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions

This commit is contained in:
doxa-maps-88655c70 2026-09-25 10:19:42 +00:00
commit c73b775b6d
95 changed files with 1016 additions and 714 deletions

View file

@ -48,16 +48,19 @@ the Research Gallery.
<table>
<tr>
<td width="20%" valign="top"><img src="documentation/readme-images/prod-regions-phone.png" alt="Regions on a phone"><br><b>Regions</b></td>
<td width="20%" valign="top"><img src="documentation/readme-images/deploy2-mobile-upg100.png" alt="UPG-100 List on a phone"><br><b>UPG-100 List</b></td>
<td width="20%" valign="top"><img src="documentation/readme-images/prod-prayer-phone.png" alt="Prayer Progress on a phone"><br><b>Prayer Progress</b></td>
<td width="20%" valign="top"><img src="documentation/readme-images/prod-cities-internet-phone.png" alt="Cities with Internet on a phone"><br><b>Cities with Internet</b></td>
<td width="20%" valign="top"><img src="documentation/readme-images/prod-gospel-resources-phone.png" alt="Gospel Resources on a phone"><br><b>Gospel Resources</b></td>
<td width="25%" valign="top"><img src="documentation/readme-images/prod-simple-research-phone.png" alt="Simple Research on a phone"><br><b>Simple Research</b></td>
<td width="25%" valign="top"><img src="documentation/readme-images/prod-regions-phone.png" alt="Regions on a phone"><br><b>Regions</b></td>
<td width="25%" valign="top"><img src="documentation/readme-images/deploy2-mobile-upg100.png" alt="UPG-100 List on a phone"><br><b>UPG-100 List</b></td>
<td width="25%" valign="top"><img src="documentation/readme-images/prod-prayer-phone.png" alt="Prayer Progress on a phone"><br><b>Prayer Progress</b></td>
</tr>
<tr>
<td width="25%" valign="top"><img src="documentation/readme-images/prod-cities-internet-phone.png" alt="Cities with Internet on a phone"><br><b>Cities with Internet</b></td>
<td width="25%" valign="top"><img src="documentation/readme-images/prod-gospel-resources-phone.png" alt="Gospel Resources on a phone"><br><b>Gospel Resources</b></td>
<td width="25%" valign="top"><img src="documentation/readme-images/prod-research-gallery-phone.png" alt="Research Gallery on a phone"><br><b>Research Gallery</b></td>
<td width="25%" valign="top"><b>Big Picture</b><br><sub>a desktop dashboard by design — on a phone, Simple Research is the dashboard, and the Research Gallery opens on it.</sub></td>
</tr>
</table>
<sub>Big Picture is a desktop dashboard by design — on phones, Simple Research is the dashboard. Simple Research's phone layout is still being finished.</sub>
### Every capability ships every way
| Capability | Example | iframe | Mobile app | JavaScript embed | WordPress / Disciple Tools | Desktop |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 KiB

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 KiB

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 749 KiB

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

View file

@ -1,35 +1,35 @@
{
"cap1-single-element": {
"bundle": "all-people-groups",
"sha256": "33b9b1e760d930f5a92c0b2c194c2ab0542ffe6e0d84ac6a0896587e551b18fc"
"sha256": "0ede35b98a92ea2a56fa5b6302eb3dd827d9de4c636f82f663f9c0f4d8b24890"
},
"cap2-adoption": {
"bundle": "doxa-simple-map",
"sha256": "c46bc416c687dc4e41d5bae5a387995aec65d4928cde3aa55e0188c295c50546"
"sha256": "6aa091deb995128b21dbeaa49bd4988c60fe61092d1859b1bdb83dba49d7ce20"
},
"cap2-engagement": {
"bundle": "doxa-simple-map",
"sha256": "c46bc416c687dc4e41d5bae5a387995aec65d4928cde3aa55e0188c295c50546"
"sha256": "6aa091deb995128b21dbeaa49bd4988c60fe61092d1859b1bdb83dba49d7ce20"
},
"cap2-prayer": {
"bundle": "doxa-simple-map",
"sha256": "c46bc416c687dc4e41d5bae5a387995aec65d4928cde3aa55e0188c295c50546"
"sha256": "6aa091deb995128b21dbeaa49bd4988c60fe61092d1859b1bdb83dba49d7ce20"
},
"cap3-monolithic-research": {
"bundle": "doxa-research-map",
"sha256": "9cbe76cb74f992dbbc1dc61c50270f98f787cb983d402b5ee6cc6a26bc6878a4"
"sha256": "3391075b32dd710b7f810e3097074a4d453c7720a6963e101ddec99ad4186d29"
},
"cap4-upg100-list": {
"bundle": "my-upg-100-list",
"sha256": "95ee299d03809f7cc9bb9c84b7f68b6706ffaa482873900289028fede38aa9c6"
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
},
"cap4-upg100-mfe": {
"bundle": "my-upg-100-list",
"sha256": "95ee299d03809f7cc9bb9c84b7f68b6706ffaa482873900289028fede38aa9c6"
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
},
"cap4-upg100-poster-wall": {
"bundle": "my-upg-100-list",
"sha256": "95ee299d03809f7cc9bb9c84b7f68b6706ffaa482873900289028fede38aa9c6"
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
},
"cap5-doxa-maps-spa": {
"bundle": "doxa-maps-spa",
@ -41,11 +41,11 @@
},
"deploy2-mobile-upg100": {
"bundle": "my-upg-100-list",
"sha256": "95ee299d03809f7cc9bb9c84b7f68b6706ffaa482873900289028fede38aa9c6"
"sha256": "fab621d68bdad964da2be67135d0385d40417c6180f008f36cbc0f50076f5e4a"
},
"prod-big-picture": {
"bundle": "dashboard-plus-grid",
"sha256": "3c1fc9c4af52752e545365676d630e9dfee3a5cd67018ca5181d499304742f61"
"sha256": "d3eb3468e6f42c3346885a665102dd10965a98a5e045f50b0d15c14f52a05e56"
},
"prod-cities-internet": {
"bundle": "cities-internet",
@ -57,26 +57,34 @@
},
"prod-gospel-resources": {
"bundle": "gospel-resources",
"sha256": "abf213dca851c7ef0d3dd94cb9456f4dd01b1b8e9dea4acf65452f56fd0c5879"
"sha256": "943fa394060c009d88a3a8e17651980e9c40077be315fb69c98fed92aa17dce0"
},
"prod-gospel-resources-phone": {
"bundle": "gospel-resources",
"sha256": "abf213dca851c7ef0d3dd94cb9456f4dd01b1b8e9dea4acf65452f56fd0c5879"
"sha256": "943fa394060c009d88a3a8e17651980e9c40077be315fb69c98fed92aa17dce0"
},
"prod-prayer-phone": {
"bundle": "doxa-simple-map",
"sha256": "c46bc416c687dc4e41d5bae5a387995aec65d4928cde3aa55e0188c295c50546"
"sha256": "6aa091deb995128b21dbeaa49bd4988c60fe61092d1859b1bdb83dba49d7ce20"
},
"prod-regions": {
"bundle": "regions-dashboard",
"sha256": "95ea2a4720a0c2081f3f3c757b658f9a5b4358b1943d11aefb2b3566df6e88b6"
"sha256": "a9b91cd0428834cdbdc711f5be2921c08fc2f2b057081f26c21812e56cfa114e"
},
"prod-regions-phone": {
"bundle": "regions-dashboard",
"sha256": "95ea2a4720a0c2081f3f3c757b658f9a5b4358b1943d11aefb2b3566df6e88b6"
"sha256": "a9b91cd0428834cdbdc711f5be2921c08fc2f2b057081f26c21812e56cfa114e"
},
"prod-research-gallery-phone": {
"bundle": "doxa-maps-spa",
"sha256": "3d5467fd47ada47979fb878a5a355329ddc3ad88071ec947bf6c4a61d53df143"
},
"prod-simple-research": {
"bundle": "research-map-plus-grid",
"sha256": "458139bb4b1da0e79fefeb3c671b78d81da4ef832e411e54789915ca07007ee7"
},
"prod-simple-research-phone": {
"bundle": "research-map-plus-grid",
"sha256": "458139bb4b1da0e79fefeb3c671b78d81da4ef832e411e54789915ca07007ee7"
}
}

View file

@ -19,9 +19,9 @@
* Deliberately NOT fatal on its own: the caller decides whether to start its own server
* instead, or to skip. What it must never do is quietly continue.
*/
import { readFileSync } from 'node:fs'
import { readFileSync, existsSync, realpathSync } from 'node:fs'
import { createHash } from 'node:crypto'
import { resolve } from 'node:path'
import { resolve, dirname, join } from 'node:path'
const MANIFEST = 'doxa-maps/manifest.json'
@ -123,3 +123,48 @@ export async function requireThisTree(base, repo, label) {
}
return verdict
}
/**
* The same guard for gates that READ A CHECKOUT instead of talking to a server.
*
* check-tabs-localized and check-poster-prints read library/ source from DOXA_REPO, which exists
* so a gate can be pointed at ANOTHER repo on purpose. That is also exactly how a leaked env var
* from someone else's shell makes a gate grade their tree while reporting as ours. So pointing
* elsewhere is an explicit opt-in (DOXA_REPO_MEASURE_OTHER=1); without it the gate exits 75.
*
* @param {string} repoPath the checkout the gate is about to read
* @param {string} repo this gate's own repo root
* @param {string} label the gate's name, for the message
*/
export function requireThisRepo(repoPath, repo, label) {
const real = (p) => { try { return realpathSync(p) } catch { return String(p) } }
if (real(repoPath) === real(repo)) return { ok: true, why: 'this checkout' }
if (process.env.DOXA_REPO_MEASURE_OTHER === '1') {
console.log(`${label}: measuring ANOTHER checkout on purpose (DOXA_REPO_MEASURE_OTHER=1): ${repoPath}`)
return { ok: true, why: 'explicit opt-in' }
}
console.log(`${label}: NOT MEASURED — DOXA_REPO points at ${repoPath}, not this checkout (${repo}).`)
console.log(' Unset DOXA_REPO, or set DOXA_REPO_MEASURE_OTHER=1 if measuring that checkout is the intent.')
process.exit(75)
}
/**
* For a gate handed a FILE to render: refuse a file that lives inside a DIFFERENT git checkout.
* A file outside any checkout (a /tmp or _qa-cycle output) cannot be attributed and is allowed,
* with a note; one inside another worktree is that worktree's build, not ours.
*/
export function requirePathInThisCheckout(file, repo, label) {
const real = (p) => { try { return realpathSync(p) } catch { return String(p) } }
let dir = dirname(real(file))
while (dir && dir !== dirname(dir)) {
if (existsSync(join(dir, '.git'))) {
if (real(dir) === real(repo)) return { ok: true, why: 'inside this checkout' }
console.log(`${label}: NOT MEASURED — ${file} belongs to another checkout (${dir}), not this one (${repo}).`)
process.exit(75)
}
dir = dirname(dir)
}
console.log(`${label}: note — ${file} is outside any checkout, so which build produced it cannot be checked.`)
return { ok: true, why: 'outside any checkout' }
}

View file

@ -23,13 +23,16 @@ set -u
SLOTS="${BROWSER_SLOTS:-2}"
DIR="${BROWSER_SLOT_DIR:-/tmp}"
BASE="$DIR/doxa-maps-browser-gate.lock"
# SLOT_BASE, never BASE: a variable the CALLER exported stays exported in bash, so assigning BASE
# here rewrote the caller's BASE for every gate this runs. Seven gates read process.env.BASE, and
# check:upg-map-data-painted measured /tmp/doxa-maps-browser-gate.lock/... and SKIPPED with exit 0.
SLOT_BASE="$DIR/doxa-maps-browser-gate.lock"
FLOOR_MB="${BROWSER_MEM_FLOOR_MB:-2500}"
WAIT_S="${BROWSER_LOCK_WAIT:-900}"
MEM_WAIT_S="${BROWSER_MEM_WAIT:-300}"
BUSY=75
slot_path() { [ "$1" = "1" ] && echo "$BASE" || echo "$BASE$1"; }
slot_path() { [ "$1" = "1" ] && echo "$SLOT_BASE" || echo "$SLOT_BASE$1"; }
mem_available_mb() {
awk '/^MemAvailable:/ { printf "%d", $2 / 1024 }' /proc/meminfo 2>/dev/null || echo 999999
@ -58,10 +61,22 @@ while [ "$i" -le "$SLOTS" ]; do
i=$((i + 1))
done
# Everything busy: queue on slot 1, and report a timeout as UNMEASURED rather than as a verdict.
exec 9>"$(slot_path 1)"
if flock -w "$WAIT_S" 9; then
exec "$@"
fi
# Everything busy: keep POLLING EVERY SLOT until the deadline, and report a timeout as UNMEASURED
# rather than as a verdict. This used to queue on slot 1 alone (flock -w on slot 1), so a slot that
# freed up was never taken: measured 2026-09-25 (dx-shepherd), ten waiters were piled on slot 1 while
# slot 2 sat free, and with slot 1 held 12 s and slot 2 freed at ~3 s a caller waited 11.5 s — the
# two-slot semaphore had collapsed into ONE queue. Polling takes the first slot that frees (~4 s in
# the same test). Trade-off, accepted: polling is not first-come-first-served, so under heavy
# contention a waiter can lose a few rounds; an idle slot beside a queue is strictly worse.
deadline=$(( $(date +%s) + WAIT_S ))
while [ "$(date +%s)" -lt "$deadline" ]; do
i=1
while [ "$i" -le "$SLOTS" ]; do
exec 9>"$(slot_path "$i")" 2>/dev/null || true
if flock -n 9; then exec "$@"; fi
i=$((i + 1))
done
sleep 2
done
echo "browser-slot: no slot within ${WAIT_S}s (${SLOTS} slots, floor ${FLOOR_MB}MB) — not measuring" >&2
exit "$BUSY"

View file

@ -7,127 +7,100 @@
* live in a fresh context, and published. All true — and insufficient. `big-picture` (cap-4,
* tab-1 of the cap-5 SPA) and `dashboard-plus-grid` NEST that grid and COMPILE IT IN, so both
* kept shipping the PRE-FIX code. The surface that was proved is not the surface anyone opens.
*
* ★ A COMPOSITE IS A COPY TAKEN AT ITS OWN BUILD TIME, NOT A POINTER TO ITS CHILD.
* So a child's source newer than a parent's bundle means the parent is serving stale code.
*
* WHY NOT GREP FOR A MARKER CLASS
* That is how the bug was originally found, but it does not generalise: it needs a human to
* remember a string unique to each fix. Worse, the OBVIOUS choice misleads — grepping the
* pre-existing `filters__toggle` reported "present" on all three bundles and HID the staleness;
* only the fix-introduced `filters__toggle-count` exposed it. A gate that depends on choosing
* the right marker will eventually be handed the wrong one. MTIME needs no such choice.
* ★ WHY IT NOW REBUILDS AND COMPARES BYTES, NOT MTIMES (2026-09-25). The first version compared
* the newest child SOURCE mtime with the parent BUNDLE mtime. File times are not build facts:
* a git merge or checkout rewrites sources with checkout-time mtimes, so rega's publish of
* beb13da3 stopped on "doxa-maps-spa / plugin-studio / stack-demo STALE vs gospel-resources /
* research-dashboard" although a rebuild reproduced the committed bytes exactly — and two of
* those parents mount their children BY TAG at runtime, so they compile none of the child in
* and CANNOT be stale with respect to it. It was also gameable the other way: checking out the
* parents makes their mtimes new and the gate passes on stale code.
* The question is "does the committed bundle equal what its source builds TODAY?", so that is
* what is asked: each composite is rebuilt into a temp dir (vite.config.js DOXA_OUT_ROOT — never
* public/) and its sha256 compared with the committed bundle. Builds are deterministic (two runs
* → identical bytes, verified), so a difference is real: the committed parent predates a source
* change it compiles in — its own, or a child's. A child it only mounts by tag cannot move it.
*
* HOW IT WORKS
* 1. read every plugin-designs/<b>/<b>.meta.json `composes: []` — the AUTHORITATIVE declaration
* of nesting (the manifest has no `composes` field).
* 2. resolve each entry to a bundle: it is either a bundle DIRECTORY or an ELEMENT TAG owned by
* one (manifest `bundles[].elements`). A tag that resolves to nothing is REPORTED, never
* silently skipped — an unresolved edge is an unchecked edge.
* 3. for each parent→child edge compare the NEWEST child source mtime against the parent's
* built bundle mtime. Child newer ⇒ FAIL.
* 1. every plugin-designs/<b>/<b>.meta.json with `composes: [...]` is a composite parent (entries
* are still resolved and an unresolvable one is REPORTED — an unresolved edge is an unchecked
* edge);
* 2. BUNDLE=<parent> DOXA_OUT_ROOT=<tmp> vite build, then sha256(tmp) vs sha256(public/…/<b>.js);
* 3. differ ⇒ STALE (FAIL); no committed bundle ⇒ reported, not built ⇒ FAIL.
* Usage: node internal/check-composite-freshness.mjs [--only a,b] (~5-15 s per composite)
*
* SELF-EDGES ARE SKIPPED: big-picture composes `bp-research-header`, which resolves back to
* big-picture. A bundle cannot be stale with respect to itself.
*
* NEGATIVE-TESTED: `touch plugin-designs/people-groups-grid/people-groups-grid.vue` makes this
* FAIL naming big-picture + dashboard-plus-grid + doxa-maps-spa + plugin-studio + stack-demo;
* a full `bun run build` returns it to PASS. A gate without a negative test is not a gate.
* NEGATIVE-TESTED 2026-09-25: an edit to people-groups-grid.vue with no rebuild fails the
* composites that compile it in; `touch` on every child source (no content change) still PASSES on
* a fresh tree — the mtime false alarm is gone.
*/
import { readFileSync, existsSync, statSync, readdirSync } from 'node:fs'
import { join, dirname, resolve } from 'node:path'
import { readFileSync, existsSync, readdirSync, mkdtempSync, rmSync } from 'node:fs'
import { createHash } from 'node:crypto'
import { spawnSync } from 'node:child_process'
import { tmpdir } from 'node:os'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const DESIGNS = join(ROOT, 'plugin-designs')
const BUILT = join(ROOT, 'public', 'doxa-maps')
const MANIFEST = join(BUILT, 'manifest.json')
const TREE = join(ROOT, 'public', 'doxa-maps')
const argv = process.argv.slice(2)
const oi = argv.indexOf('--only')
const ONLY = oi >= 0 && argv[oi + 1] ? new Set(argv[oi + 1].split(',')) : null
if (!existsSync(MANIFEST)) {
console.log('COMPOSITE-FRESHNESS: SKIP — no manifest yet (run `bun run build` first).')
if (!existsSync(join(TREE, 'manifest.json'))) {
console.log('COMPOSITE-FRESHNESS: SKIP — no built tree (public/doxa-maps/manifest.json).')
process.exit(0)
}
const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8'))
const manifest = JSON.parse(readFileSync(join(TREE, 'manifest.json'), 'utf8'))
// tag -> owning bundle, so a composes entry naming an ELEMENT still resolves.
const tagToBundle = new Map()
for (const b of manifest.bundles || []) {
for (const e of b.elements || []) {
const tag = typeof e === 'string' ? e : e?.tag
if (tag) tagToBundle.set(tag, b.bundle)
}
}
const byTag = new Map()
for (const b of manifest.bundles || []) for (const t of b.elements || []) byTag.set(t, b.bundle)
const bundles = new Set(readdirSync(DESIGNS).filter((d) => existsSync(join(DESIGNS, d, 'index.js'))))
// A bare PROFILE name of the same bundle (gospel-resources composes "resources-map", "data-tables" —
// its own sibling profiles) resolves to that bundle, as the studio resolves it.
const profilesOf = new Map((manifest.bundles || []).map((b) => [b.bundle, new Set((b.profiles || []).map((p) => p.profile))]))
const resolveEntry = (e, parent) => { const s = String(e); const head = s.split('/')[0]
if (bundles.has(head)) return head
if (parent && profilesOf.get(parent) && profilesOf.get(parent).has(s)) return parent
return byTag.get(s) || null }
/** Newest mtime under a bundle's SOURCE dir (its .vue/.js/.json — not node_modules). */
function newestSource(bundle) {
const dir = join(DESIGNS, bundle)
if (!existsSync(dir)) return null
let newest = 0, file = null
const walk = (d) => {
for (const ent of readdirSync(d, { withFileTypes: true })) {
if (ent.name === 'node_modules' || ent.name.startsWith('.')) continue
const p = join(d, ent.name)
if (ent.isDirectory()) { walk(p); continue }
if (!/\.(vue|js|mjs|json|css)$/.test(ent.name)) continue
const m = statSync(p).mtimeMs
if (m > newest) { newest = m; file = p.slice(ROOT.length + 1) }
const sha = (p) => createHash('sha256').update(readFileSync(p)).digest('hex')
const tmp = mkdtempSync(join(tmpdir(), 'composite-freshness-'))
const failures = [], unresolved = [], fresh = []
let composites = 0
try {
for (const bundle of [...bundles].sort()) {
if (ONLY && !ONLY.has(bundle)) continue
let composes = null
for (const f of readdirSync(join(DESIGNS, bundle)).filter((f) => f.endsWith('.meta.json'))) {
try { const c = JSON.parse(readFileSync(join(DESIGNS, bundle, f), 'utf8')).composes; if (Array.isArray(c) && c.length) composes = (composes || []).concat(c) } catch { /* not JSON: not a declaration */ }
}
}
walk(dir)
return newest ? { mtime: newest, file } : null
}
function builtBundle(bundle) {
const p = join(BUILT, bundle, `${bundle}.js`)
return existsSync(p) ? { mtime: statSync(p).mtimeMs, file: p.slice(ROOT.length + 1) } : null
}
const failures = []
const unresolved = []
let edges = 0, composites = 0
for (const ent of readdirSync(DESIGNS, { withFileTypes: true })) {
if (!ent.isDirectory()) continue
const bundle = ent.name
const meta = join(DESIGNS, bundle, `${bundle}.meta.json`)
if (!existsSync(meta)) continue
let composes
try { composes = JSON.parse(readFileSync(meta, 'utf8')).composes } catch { continue }
if (!Array.isArray(composes) || !composes.length) continue
composites++
const parentBuilt = builtBundle(bundle)
if (!parentBuilt) continue // never built — not this gate's concern
for (const rawEntry of composes) {
// a composes entry may be "bundle", "bundle/profile", or an element tag
const head = String(rawEntry).split('/')[0]
const child = existsSync(join(DESIGNS, head)) ? head : tagToBundle.get(head)
if (!child) { unresolved.push(`${bundle} composes "${rawEntry}" — resolves to no bundle`); continue }
if (child === bundle) continue // self-edge (own element); cannot be stale vs itself
edges++
const src = newestSource(child)
if (!src) continue
if (src.mtime > parentBuilt.mtime) {
const lagS = Math.round((src.mtime - parentBuilt.mtime) / 1000)
failures.push(
`${bundle} is STALE vs its child ${child}\n` +
` child source : ${src.file}\n` +
` parent bundle: ${parentBuilt.file}\n` +
` child is ${lagS}s newer — the parent compiled an OLD copy. Run a FULL \`bun run build\`.`
)
if (!composes) continue
for (const e of composes) if (!resolveEntry(e, bundle)) unresolved.push(`${bundle} composes "${e}" — resolves to no bundle`)
composites++
const committed = join(TREE, bundle, `${bundle}.js`)
if (!existsSync(committed)) { failures.push(`${bundle}: no committed bundle at public/doxa-maps/${bundle}/${bundle}.js`); continue }
const out = join(tmp, bundle)
const r = spawnSync('bun', ['x', 'vite', 'build'], { cwd: ROOT, env: { ...process.env, BUNDLE: bundle, DOXA_OUT_ROOT: out }, encoding: 'utf8' })
const built = join(out, bundle, `${bundle}.js`)
if (r.status !== 0 || !existsSync(built)) {
failures.push(`${bundle}: the rebuild FAILED (exit ${r.status}) — ${String(r.stderr || r.stdout).trim().split('\n').slice(-2).join(' / ').slice(0, 200)}`)
continue
}
if (sha(built) !== sha(committed)) {
failures.push(`${bundle} is STALE — the committed bundle is not what its source builds today (committed ${sha(committed).slice(0, 12)}, rebuilt ${sha(built).slice(0, 12)}). Rebuild it: BUNDLE=${bundle} bun x vite build, then bun run fix:sri.`)
} else fresh.push(bundle)
}
}
} finally { rmSync(tmp, { recursive: true, force: true }) }
console.log(`COMPOSITE-FRESHNESS: ${composites} composite(s), ${edges} parent->child edge(s) checked.`)
console.log(`COMPOSITE-FRESHNESS: ${composites} composite(s) rebuilt into a temp dir and compared byte for byte with the committed bundle.`)
if (fresh.length) console.log(` ✓ ${fresh.join(', ')}`)
for (const u of unresolved) console.log(` ! UNRESOLVED ${u}`)
if (failures.length) {
console.log(`\nCOMPOSITE-FRESHNESS: FAIL — ${failures.length} composite(s) carry a stale child:`)
console.log(`\nCOMPOSITE-FRESHNESS: FAIL — ${failures.length} composite(s):`)
for (const f of failures) console.log(` ✗ ${f}`)
console.log('\n A composite is a COPY taken at its own build time, not a pointer to its child.')
process.exit(1)
}
console.log('COMPOSITE-FRESHNESS: PASS — every composite was built after the children it nests.')
console.log('COMPOSITE-FRESHNESS: PASS — every committed composite is exactly what its source builds today.')

View file

@ -28,6 +28,7 @@
*/
import { chromium } from 'playwright'
import { serveReaped } from './_serve-reaped.mjs'
import { servesThisTree } from './_serves-this-tree.mjs'
import { writeFileSync, mkdtempSync, readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { tmpdir } from 'node:os'
@ -38,7 +39,9 @@ const REPO = resolve(HERE, '..')
const PUBLIC = resolve(REPO, 'public')
const LEAF = 'doxa-maps/gospel-resources/resources-map/index.html'
const leafPort = 5810 + Math.floor(Math.random() * 60)
// EMBED_BASEMAP_PORT pins the port — used to prove the identity guard below (occupy it with another
// tree's server first; the gate must refuse rather than measure that tree).
const leafPort = Number(process.env.EMBED_BASEMAP_PORT) || (5810 + Math.floor(Math.random() * 60))
const hostPort = leafPort + 1
/** Mapbox endpoints that actually draw the map. Telemetry is not evidence of anything. */
@ -82,7 +85,15 @@ const stop = () => {
if (!(await waitFor(`http://127.0.0.1:${leafPort}/${LEAF}`)) || !(await waitFor(`http://127.0.0.1:${hostPort}/index.html`))) {
console.error('EMBED-BASEMAP: NOT MEASURED — a server did not come up.')
stop()
process.exit(1)
process.exit(75) // a precondition, not a product failure (was 1, which read as a red gate)
}
// ★ THE PORT IS RANDOM, NOT OS-ASSIGNED: if another agent's server already holds it, ours fails to
// start and waitFor() above succeeds against THEIRS. Prove the leaf server is this build.
// Measure THIS build, or measure nothing (exit 75 = NOT MEASURED; see a9550858 / _serves-this-tree.mjs).
{
// servesThisTree (not requireThisTree) so this gate stops ITS OWN two servers before exiting.
const t = await servesThisTree(`http://127.0.0.1:${leafPort}`, REPO)
if (/DIFFERENT build/.test(t.why)) { console.log('EMBED-BASEMAP: NOT MEASURED — ' + t.why); stop(); process.exit(75) }
}
const browser = await chromium.launch({

View file

@ -28,6 +28,9 @@
// Usage: node internal/check-legend-export-reachable.mjs [leafUrl]
// SKIPs when nothing is served — this needs the built tree.
import { chromium } from 'playwright'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { requireThisTree } from './_serves-this-tree.mjs'
const DEFAULT_LEAF = (process.env.BASE_URL || 'http://127.0.0.1:4173')
+ '/doxa-maps/my-upg-100-list/upg-100-list/index.html'
@ -43,6 +46,8 @@ if (!reachable) {
console.log(' Serve the built tree: python3 -m http.server 4173 --directory public (or set BASE_URL)')
process.exit(0)
}
// Measure THIS build, or measure nothing (exit 75 = NOT MEASURED; see a9550858 / _serves-this-tree.mjs).
await requireThisTree(LEAF, resolve(dirname(fileURLToPath(import.meta.url)), '..'), 'LEGEND-EXPORT-REACHABLE')
const WALK = `(root, out) => { out = out || []; const rec = (r) => { for (const e of r.querySelectorAll('*')) { out.push(e); if (e.shadowRoot) rec(e.shadowRoot) } }; rec(root); return out }`

View file

@ -29,7 +29,8 @@
import { chromium } from 'playwright'
import { existsSync, readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { pathToFileURL } from 'node:url'
import { pathToFileURL, fileURLToPath } from 'node:url'
import { requirePathInThisCheckout } from './_serves-this-tree.mjs'
const target = process.argv[2] || process.env.PACKET_HTML
if (!target) {
@ -43,6 +44,8 @@ if (!existsSync(file)) {
console.error(`PACKET-RENDER: FAIL — no such packet: ${file}`)
process.exit(1)
}
// Measure THIS build, or measure nothing (exit 75 = NOT MEASURED; see a9550858 / _serves-this-tree.mjs).
requirePathInThisCheckout(file, resolve(dirname(fileURLToPath(import.meta.url)), '..'), 'PACKET-RENDER')
const browser = await chromium.launch()
const page = await browser.newPage({ viewport: { width: 1500, height: 1941 } })

View file

@ -29,6 +29,8 @@ const REPO = process.env.DOXA_REPO
// other builder. DOXA_REPO still wins; only the default is made portable, using the idiom
// check-no-absolute-paths.mjs prints in its own failure text.
|| resolve(dirname(fileURLToPath(import.meta.url)), '..')
// Measure THIS build, or measure nothing (exit 75 = NOT MEASURED; see a9550858 / _serves-this-tree.mjs).
{ const { requireThisRepo } = await import('./_serves-this-tree.mjs'); requireThisRepo(REPO, resolve(dirname(fileURLToPath(import.meta.url)), '..'), 'POSTER-PRINTS') }
const src = readFileSync(resolve(REPO, 'library/composables/usePosterPrints.js'), 'utf8')
const frame = readFileSync(resolve(REPO, 'library/composables/printInPlace.js'), 'utf8')
// The composable dynamically imports its renderers by RELATIVE path. Under a blob

View file

@ -47,6 +47,8 @@ import { chromium } from 'playwright'
const REPO = process.env.DOXA_REPO
|| resolve(dirname(fileURLToPath(import.meta.url)), '..')
// Measure THIS build, or measure nothing (exit 75 = NOT MEASURED; see a9550858 / _serves-this-tree.mjs).
{ const { requireThisRepo } = await import('./_serves-this-tree.mjs'); requireThisRepo(REPO, resolve(dirname(fileURLToPath(import.meta.url)), '..'), 'TABS-LOCALIZED') }
const LOCALES = join(REPO, 'library/i18n/locales')
// The tab ids research-map renders, in order, with the KEY each resolves through.

View file

@ -58,9 +58,12 @@ const MIN_ZONE_RATIO = 0.9
let reachable = false
try { const r = await fetch(urls[0], { signal: AbortSignal.timeout(5000) }); reachable = r.ok } catch { /* precondition */ }
if (!reachable) {
console.log(`UPG-MAP-DATA: SKIP — nothing served at ${urls[0]}.`)
console.log(' Serve the built tree: python3 -m http.server 4173 --directory public (or set BASE)')
process.exit(0)
// NOT MEASURED, exit 75 — never exit 0. Through browser-slot.sh this line once read
// "SKIP — nothing served at /tmp/doxa-maps-browser-gate.lock/…" (the slot had overwritten BASE)
// and EXITED 0, so a wave counted a gate that measured nothing as green.
console.log(`UPG-MAP-DATA: NOT MEASURED — nothing served at ${urls[0]}.`)
console.log(' Serve THIS checkout (internal/serve-strict.mjs on your own port) and pass BASE=http://127.0.0.1:<port>/doxa-maps')
process.exit(75)
}
// ★ "ANSWERS 200" AND "IS SERVING THIS BUILD" ARE DIFFERENT QUESTIONS. This gate reads the counter
@ -70,15 +73,16 @@ if (!reachable) {
// See _serves-this-tree.mjs (bundler-2, BUG-BROWSER-GATES-CAN-MEASURE-ANOTHER-WORKTREES-TREE):
// :4173 on this box was once held by a different worktree and a gate reported three confident runs
// about code nobody had changed.
// ⚠ NOT MEASURED rather than SKIP, exit 2: "nothing to check here" and "I cannot attribute what I
// saw" are different facts, and only the second one should stop you trusting the numbers.
// ⚠ NOT MEASURED, exit 75 — the repo's EX_TEMPFAIL for "not measured" (a9550858), which the wave
// runner reports as ⊘. It was exit 2 here, a code nothing else in the repo reads that way.
// This check is also what makes the :4173 default safe: another agent's tree there is refused.
const TREE_BASE = String(urls[0]).split('/doxa-maps/')[0]
const treeCheck = await servesThisTree(TREE_BASE, ROOT)
if (!treeCheck.ok) {
console.error('UPG-MAP-DATA: NOT MEASURED — the server is not serving this checkout.')
console.error(` ${treeCheck.why}`)
console.error(' The polygon and pin counts below would describe someone else\'s build.')
process.exit(2)
process.exit(75)
}
const WALK = `(root) => { const o = []; const rec = (r) => { for (const e of r.querySelectorAll('*')) { o.push(e); if (e.shadowRoot) rec(e.shadowRoot) } }; rec(root); return o }`

View file

@ -47,6 +47,9 @@ for (const k of Object.keys(expect)) {
const i = process.argv.indexOf('--url')
if (i > -1) {
// Measure THIS build, or measure nothing (exit 75 = NOT MEASURED; see a9550858 / _serves-this-tree.mjs).
const { requireThisTree } = await import('./_serves-this-tree.mjs')
await requireThisTree(process.argv[i + 1], ROOT, 'VISION-CARD-NUMBERS')
const { chromium } = await import('playwright')
const b = await chromium.launch()
const p = await b.newPage({ viewport: { width: 900, height: 1000 } })

View file

@ -271,6 +271,47 @@ function ownsAMapDeeply(profileDir) {
return false
}
/**
* Does THIS PROFILE reach a map through its own import graph?
*
* ★ PER PROFILE, NOT PER BUNDLE (BUG-FALSE-ISMAP-BAKES-403KB-MAPBOX-INTO-MAPLESS-LEAVES, fixed
* 2026-09-25). ownsAMapDeeply() answers "is there a map ANYWHERE under this bundle's src/", and the
* single-leaf branch used it for every profile — so a map-less sibling inherited map-ness from the
* bundle: regions-dashboard/regions-list shows a list and no map, but src/RegionCountryMap.vue made
* it isMap=true. Its leaf then baked the ~400 KB Mapbox engine it never uses, and since 4a6886d2 its
* embed snippet also preloaded it. This follows the RELATIVE imports reachable from the profile's
* own .vue (bounded to the bundle folder, .vue/.js/.mjs), so research-dashboard — whose map sits
* several imports deep in src/components/dashboards/DashboardMapPanel.vue — still resolves true.
* An explicit boolean `isMap` in <profile>.meta.json wins over the scan.
*/
const RELATIVE_IMPORT_RE = /(?:import[^'"`;]*?from\s*|import\s*\(\s*|import\s+)['"`](\.{1,2}\/[^'"`]+)['"`]/g
function profileReachesAMap(dir, file) {
const seen = new Set()
const stack = [join(dir, file)]
while (stack.length) {
const f = stack.pop()
if (seen.has(f)) continue
seen.add(f)
let src
try { src = readFileSync(f, 'utf8') } catch { continue }
if (MAP_ENGINE_RE.test(src)) return true
for (const m of src.matchAll(RELATIVE_IMPORT_RE)) {
const base = resolve(dirname(f), m[1])
if (!base.startsWith(dir)) continue // stay inside this bundle
for (const cand of [base, base + '.vue', base + '.js', base + '.mjs', join(base, 'index.js')]) {
if (NESTED_SCAN_EXT.test(cand) && existsSync(cand) && statSync(cand).isFile()) { stack.push(cand); break }
}
}
}
return false
}
function metaIsMap(dir, profileName) {
try {
const meta = JSON.parse(readFileSync(join(dir, `${profileName}.meta.json`), 'utf8'))
return typeof meta?.isMap === 'boolean' ? meta.isMap : null
} catch { return null }
}
/**
* Does a cap-4 composite (`nested-elements-mfe`) compose a child that paints a map?
*
@ -1206,7 +1247,7 @@ function scanBundles() {
? false
: profileType === 'nested-elements-mfe'
? composesAMapLeaf(dir, profileName, file)
: (isMapLeaf(join(dir, file)) || ownsAMapDeeply(dir))
: (metaIsMap(dir, profileName) ?? profileReachesAMap(dir, file))
return {
profile: profileName,

View file

@ -377,8 +377,9 @@ await shot('prod-gospel-resources', '/doxa-maps/gospel-resources/resources-map/i
// ---- THE PRODUCTION MAPS ON A PHONE (390x844) — for the README's phone row ----
// Big Picture is desktop-only by ruling (on phones the Research Gallery disables it and Simple Research is
// the dashboard), so it has no phone shot. UPG-100's phone shot is deploy2-mobile-upg100. Simple Research
// (and the Research Gallery, which opens on it on phones) are left out until its phone layout is fixed.
// the dashboard), so it has no phone shot. UPG-100's phone shot is deploy2-mobile-upg100.
await shot('prod-simple-research-phone', '/doxa-maps/research-map-plus-grid/research-map-plus-grid/index.html', { vp: { width: 390, height: 844 }, dsf: 2, wait: 10000, minElements: 60 })
await shot('prod-research-gallery-phone', '/doxa-maps/doxa-maps-spa/doxa-maps-spa/index.html', { vp: { width: 390, height: 844 }, dsf: 2, wait: 10000, minElements: 60 })
await shot('prod-regions-phone', '/doxa-maps/regions-dashboard/regions-mfe/index.html', { vp: { width: 390, height: 844 }, dsf: 2, wait: 10000, minElements: 60 })
await shot('prod-prayer-phone', '/doxa-maps/doxa-simple-map/doxa-simple-map/prayer/index.html', { vp: { width: 390, height: 844 }, dsf: 2, wait: 10000, minElements: 60 })
await shot('prod-cities-internet-phone', '/doxa-maps/cities-internet/connectivity-map/index.html', { vp: { width: 390, height: 844 }, dsf: 2, wait: 10000, minElements: 60 })

View file

@ -23,6 +23,9 @@
* (defaults to http://localhost:8899; serve ./public)
*/
import { chromium } from 'playwright'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { requireThisTree } from './_serves-this-tree.mjs'
// Default matches every other browser gate in this repo: `bun run serve` on loopback 4173.
// It was `localhost:8899` — one of the ad-hoc ports a previous session happened to have running,
@ -37,6 +40,9 @@ const BASE = process.env.BASE_URL || process.argv[2] || 'http://127.0.0.1:4173'
// internal/serve-strict.mjs, which 404s a directory exactly as production does, and it caught these
// two the first time it ran. The manifest already ships 28/28 iframeUrl values in this form.
const SHELL = '/doxa-maps/plugin-studio/plugin-studio/index.html'
// Measure THIS build, or measure nothing: a default port on this machine can belong to another
// worktree, and a stale studio there would be graded as ours. Exit 75 = NOT MEASURED (see a9550858).
await requireThisTree(BASE, resolve(dirname(fileURLToPath(import.meta.url)), '..'), 'SHELL-SCROLL')
const LEAF = '/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/index.html'
// Shadow DOM DOES NOT PIERCE. A single-level querySelector lies about every composite, so every
@ -110,76 +116,62 @@ const run = async () => {
if (!before) { await browser.close(); fail('.pgg-root is NOT MOUNTED in the shell — different bug, not a scroll bug') }
if (before.h < 400) { await browser.close(); fail('.pgg-root mounted but collapsed (h=' + before.h + ') — not a scroll bug') }
// 2. A REAL WHEEL GESTURE over the MAP AREA must move the grid up the screen. This is the
// gesture the Driver makes, over the surface he makes it on. Mapbox scrollZoom eating the
// wheel would show up here as no movement.
await wheel(page, 850, 420, WHEEL_DY)
const afterMap = await page.evaluate(`(() => { ${WALK} return __gridTop() })()`)
const movedByMap = before.top - afterMap.top
// 3. ...and a gesture BELOW the map must too (in case the map swallows its own area).
await wheel(page, 850, 800, WHEEL_DY)
// 2. THE DRIVER'S RULING, asserted as stated (2026-09-23, recorded in
// plugin-designs/research-dashboard/src/components/dashboards/DashboardMapPanel.vue:~1188):
// "I can't zoom in and out on the map. The scrolling is causing that to change." →
// a wheel OVER the map ZOOMS and moves the page 0 px; a wheel BELOW the map SCROLLS the page.
// ★ THIS STEP USED TO ASSERT THE OPPOSITE — that a wheel over the map must scroll the page —
// written before the ruling, when the map row filled the whole pane. Once the ruling made the
// map a bounded region with zoom always on, that assertion failed on the correct behaviour and
// kept this gate red on a product that was right (re-measured 2026-09-25: over-map 0 px, below
// the map 2502 px, grid on screen). dx-shepherd ruled the gate stale; this is the ruling.
// Negative control: SHELL_SCROLL_NEGATIVE=1 disables scrollZoom on the map before the gestures
// (the pre-ruling behaviour), and the gate must FAIL.
const MAP = `(() => { ${WALK}
const c = __walk('canvas.mapboxgl-canvas, canvas.maplibregl-canvas').find(x => x.getBoundingClientRect().height > 50)
if (!c) return null
const host = __walk('*').find(e => e.__doxaMap && e.__doxaMap.getCanvas && e.__doxaMap.getCanvas() === c)
const r = c.getBoundingClientRect()
return { x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2), bottom: Math.round(r.bottom), top: Math.round(r.top),
zoom: host ? host.__doxaMap.getZoom() : null }
})()`
const mapBox = await page.evaluate(MAP)
if (!mapBox || mapBox.zoom == null) { await browser.close(); fail('no map (canvas + its map instance) on the dashboard tab — cannot assert the zoom-vs-scroll ruling') }
if (process.env.SHELL_SCROLL_NEGATIVE === '1') {
await page.evaluate(`(() => { ${WALK} for (const e of __walk('*')) if (e.__doxaMap && e.__doxaMap.scrollZoom) e.__doxaMap.scrollZoom.disable() })()`)
console.log(' NEGATIVE CONTROL: scrollZoom disabled on the map — this run must FAIL')
}
// (a) a scroll-DOWN wheel over the map: the page must not move.
await wheel(page, mapBox.x, mapBox.y, WHEEL_DY, 3)
const afterMapDown = await page.evaluate(`(() => { ${WALK} return __gridTop() })()`)
const movedByMap = before.top - afterMapDown.top
// (b) a zoom-IN wheel over the map: the zoom must go up (in, not out, so a map already at its
// minimum zoom cannot fake a failure).
const zBefore = (await page.evaluate(MAP)).zoom
await wheel(page, mapBox.x, mapBox.y, -WHEEL_DY, 3)
const zAfter = (await page.evaluate(MAP)).zoom
// (c) the same scroll-down wheel BELOW the map's box: this is how the page reaches the grid.
// Below the map's box, and no higher than the y=800 the original gate measured this gesture at: a
// point just under the canvas (y=636 on a 900px viewport) lands on the dashboard's own lower
// controls, which consume the wheel themselves, and read as "the page will not scroll".
const belowY = Math.min(Math.max(mapBox.bottom + 60, VIEWPORT_FOLD - 100), VIEWPORT_FOLD - 20)
const pageBefore = await page.evaluate(`(() => { ${WALK} return __gridTop() })()`)
await wheel(page, mapBox.x, belowY, WHEEL_DY)
const afterLow = await page.evaluate(`(() => { ${WALK} return __gridTop() })()`)
const movedTotal = before.top - afterLow.top
// 4. THE REAL QUESTION: is the grid actually ON SCREEN now? A grid that moved 50px but still
// sits below the fold is still unreachable. Require its top inside the viewport.
const movedBelow = pageBefore.top - afterLow.top
const onScreen = afterLow.top < VIEWPORT_FOLD
console.log('SHELL-SCROLL measurements (cap-5 shell, tab="' + tab + '"):')
console.log(' grid mounted : h=' + before.h + 'px, top=' + before.top)
console.log(' wheel over MAP area : grid moved ' + movedByMap + 'px (top ' + before.top + ' -> ' + afterMap.top + ')')
console.log(' wheel BELOW map : grid moved ' + movedTotal + 'px total (top -> ' + afterLow.top + ')')
console.log(' grid on screen : ' + onScreen)
console.log(' grid mounted : h=' + before.h + 'px, top=' + before.top)
console.log(' wheel OVER map (down) : page moved ' + movedByMap + 'px (ruling: 0)')
console.log(' wheel OVER map (zoom in) : zoom ' + zBefore.toFixed(2) + ' -> ' + zAfter.toFixed(2) + ' (ruling: zooms)')
console.log(' wheel BELOW map (y=' + belowY + ') : page moved ' + movedBelow + 'px (ruling: scrolls)')
console.log(' grid on screen : ' + onScreen)
if (movedTotal < 200) fail('grid did not move — a real wheel gesture in the SHELL scrolled ' + movedTotal + 'px. The grid is mounted at h=' + before.h + 'px and is UNREACHABLE.')
if (!onScreen) fail('grid moved ' + movedTotal + 'px but its top is still at ' + afterLow.top + ' (below the 900px fold) — still not reachable.')
// ★ ASSERT ON THE NUMBER WE ALREADY MEASURED. This gate computed `movedByMap` at step 2 — the
// gesture the Driver actually makes — and then graded only `movedTotal`, which sums in the
// step-3 gesture BELOW the map. So it reported PASS (2611px total) while wheeling OVER the map
// moved just 300px and the Driver still could not reach the grid: Mapbox scrollZoom was eating
// the wheel because syncScrollZoomToHost sampled the layout on three fixed timeouts and froze a
// stale answer. Passing on the total rewarded an escape hatch the user will never find — his
// cursor is over the map, which fills most of the band.
//
// The failure mode to remember: a gate that MEASURES the right thing and then ASSERTS on a
// different variable. Distinct from a gate that cannot fail; it fails honestly, just never on
// the condition it was built for. Audit every gate for values it computes but never checks.
// ★ THE THRESHOLD IS DERIVED, NOT GUESSED. This was `movedByMap < 200` — a number picked as a
// vague "did it move meaningfully" floor WITHOUT comparing it to the value already measured as
// broken, which was 300. So the assertion passed on the exact measurement that demonstrates the
// bug, and the gate reported PASS across three ticks while the Driver still could not scroll.
//
// The gesture requests WHEEL_TICKS * WHEEL_DY px. A healthy scrollport delivers most of that
// (capped only by the scroll range actually available). Delivering one tick's worth means the
// other nine were swallowed by a nested scrollport under the cursor. Comparing DELIVERED against
// REQUESTED is self-calibrating: change the tick count or delta and the assertion still holds,
// where a hardcoded constant silently rots.
// ★ THE BASELINE IS MEASURED, NOT COMPUTED. A first attempt derived the expected travel as
// `before.top - VIEWPORT_FOLD`, which is 911-900 = 11px — a plausible-looking quantity that is
// simply the wrong one (it asks how far until the grid reaches the fold, not how much scroll the
// PANE has). With expected=11 the assertion passed on the broken 300px, repeating the exact
// mistake it was written to fix.
//
// The honest baseline is the SAME gesture performed somewhere it is known to work: step 3 wheels
// identically lower down the page and delivers `movedTotal - movedByMap`. Two identical gestures
// on one surface should move the page comparably; if the one over the dashboard moves far less,
// something under that cursor is eating it. No constant to rot, and it self-adjusts to whatever
// scroll range the layout actually has.
const requested = WHEEL_TICKS * WHEEL_DY
const movedByLower = movedTotal - movedByMap
if (movedByLower > 100 && movedByMap < movedByLower * 0.5) {
fail('a nested scrollport ATE THE WHEEL — the SAME ' + requested + 'px gesture moved the grid ' +
movedByLower + 'px lower down the page but only ' + movedByMap + 'px over the dashboard' +
'. The grid is technically reachable but NOT by ' +
'the gesture the Driver makes.\n' +
' ★ Do NOT assume the map. MEASURED 2026-09-22: the culprit was .rd-grid — a virtualized table ' +
'with scrollHeight 54834 in a ~207px window — consuming the wheel by normal scroll-chaining, ' +
'while the Mapbox canvas sat ABOVE the cursor and was never involved. Fix was ' +
'overscroll-behavior:contain on .rd-grid. Identify the element UNDER THE CURSOR before blaming ' +
'the one nearby: compare each scrollport\'s scrollTop before and after the gesture.')
}
if (Math.abs(movedByMap) > 4) fail('a wheel OVER the map scrolled the page ' + movedByMap + 'px. The Driver ruled (2026-09-23) that a wheel over the map ZOOMS and must not move the page — is scrollZoom disabled on this map?')
if (!(zAfter > zBefore + 0.05)) fail('a zoom-in wheel OVER the map left the zoom at ' + zAfter.toFixed(2) + ' (was ' + zBefore.toFixed(2) + '). The Driver ruled that a wheel over the map zooms it.')
if (movedBelow < 200) fail('a wheel BELOW the map scrolled the page only ' + movedBelow + 'px. The grid is mounted at h=' + before.h + 'px and is UNREACHABLE.')
if (!onScreen) fail('the page scrolled ' + movedBelow + 'px but the grid top is still at ' + afterLow.top + ' (below the 900px fold) — still not reachable.')
console.log('SHELL-SCROLL: PASS — the grid is reachable by wheel in the cap-5 shell.')

View file

@ -1275,6 +1275,103 @@ function allLists() {
...(clusters.uncovered_lists || [])];
}
// ── BATCH SELECTION MAKES A PACKET OR A POSTER ──────────────────────────────
// Driver: the batch tool is "super buggy", and recolouring regions is not what is wanted —
// "the batch selection tool should let you create your own poster or prayer packet based on
// your own selection." So the selection is an INPUT to the two artefacts this app already
// builds, and neither of these grows a third pipeline: both end in printPrayerPacket /
// packetPrints.printPoster, the same calls the legend and the poster wall make.
const batchBusy = ref(false);
// The zones behind the current selection. selectedTerritories holds 'zone:<id>' keys, and the
// list objects carry the selectors the packet needs; resolve back to those rather than
// re-deriving membership from the map, which would be a second source of truth.
function selectedLists() {
const want = new Set(selectedTerritories.value
.filter((t) => t.kind === 'zone')
.map((t) => String(t.key).replace(/^zone:/, '')));
return allLists().filter((l) => want.has(l.id));
}
// A SYNTHETIC LIST standing for the whole selection, so every existing action works unchanged.
// ⚠ ITS SELECTOR IS A pg_slugs UNION, NOT A LIST OF SELECTORS. 12 of the 27 lists already use
// pg_slugs and the evaluator THROWS on that type, so the selection is resolved to concrete slugs
// HERE — once, against one fetch of the people-groups list — and handed on as the type every
// consumer already handles. Resolving per zone would refetch the whole catalogue per zone and
// still leave the caller to merge.
async function selectionAsList() {
const lists = selectedLists();
if (!lists.length) throw new Error('nothing selected');
if (!API_BASE) throw new Error('no API configured');
const fields = 'slug,name,country_code,population,religion,image_url';
const resp = await fetch(`${API_BASE}/api/people-groups/list?fields=${fields}&lang=${packetLang()}`,
{ cache: 'no-store' });
if (!resp.ok) throw new Error(`people-groups fetch failed (${resp.status})`);
const records = normalizeRecords(extractRecords(await resp.json()));
const { evaluate } = await import('./data/selector-evaluator.js');
const slugs = new Set();
for (const l of lists) {
if (l.selector && l.selector.type === 'pg_slugs') {
for (const s of (l.selector.values || [])) slugs.add(s);
} else {
for (const s of evaluate(l.selector, records)) slugs.add(s);
}
}
// The label is what the reader sees on the cover and in the print dialog's title. Name the
// territories they actually chose rather than inventing a title for their selection.
const names = lists.map((l) => l.label || l.id);
const label = names.length <= 3
? names.join(' + ')
: `${names.slice(0, 2).join(' + ')} +${names.length - 2} more`;
return { id: 'selection', label, count: slugs.size,
selector: { type: 'pg_slugs', values: [...slugs] } };
}
async function makeSelectionPacket() {
if (batchBusy.value) return;
batchBusy.value = true;
try {
const sel = await selectionAsList();
// The packet's map page is per-zone artwork; a multi-zone selection has no single poster to
// lift it from, so it prints a cover WITHOUT the map rather than one zone's map standing in
// for all of them. packetMapSvg already treats a miss as fine.
const [{ createPacketRenderer }, strings] = await Promise.all([
import('@map/packet/packet-render.js'),
import('@map/scripts/posters/packet-strings.json').then((m) => m.default || m),
]);
const R = createPacketRenderer({ strings, lang: packetLang() });
const lists = selectedLists();
const mapSvg = lists.length === 1 ? await packetMapSvg(lists[0].id, R) : '';
await packetPrints.printPacket(sel, { mapSvg });
} catch (e) {
status.value = `Selection packet failed: ${String(e?.message || e).slice(0, 90)}`;
setTimeout(() => { if (status.value.startsWith('Selection packet failed')) status.value = ''; }, 4000);
} finally { batchBusy.value = false; }
}
async function makeSelectionPoster() {
if (batchBusy.value) return;
batchBusy.value = true;
try {
const lists = selectedLists();
if (!lists.length) throw new Error('nothing selected');
// ⚠ A POSTER IS PRE-RENDERED PER ZONE — there is no artwork for an arbitrary union, and
// generating one would be the new pipeline the brief rules out. With one zone selected this
// is exactly the wall's poster; with several, say so plainly instead of silently printing
// the first zone's poster as though it covered the selection.
if (lists.length > 1) {
status.value = `Poster prints one territory at a time — ${lists.length} selected. `
+ 'Pick one, or use the prayer packet for the whole selection.';
setTimeout(() => { if (status.value.startsWith('Poster prints one')) status.value = ''; }, 5000);
return;
}
await packetPrints.printPoster(lists[0]);
} catch (e) {
status.value = `Selection poster failed: ${String(e?.message || e).slice(0, 90)}`;
setTimeout(() => { if (status.value.startsWith('Selection poster failed')) status.value = ''; }, 4000);
} finally { batchBusy.value = false; }
}
// The packet follows the app's locale so a reader gets it in their own language;
// packet-strings.json falls back PER KEY, so a half-finished translation renders
// its finished keys rather than blanking.
@ -2135,25 +2232,18 @@ onBeforeUnmount(() => {
</span>
</div>
<div v-if="selCount" class="upg-hud-names">{{ selectedTerritories.map(t => t.name).join(', ') }}</div>
<!-- R1.1 (g): color picker — 8 colorblind-aware swatches + custom hex. Picking
preview-recolors the selected territories (upg-sel-fill repaints); the
clipboard payload then ends with the chosen "· #hex" (h). The gate clicks
.upg-swatch[data-color] and reads __UPG_SELECT__.color. -->
<div v-if="selCount" class="upg-hud-swatches">
<button
v-for="c in SWATCHES" :key="c"
class="upg-swatch" :class="{ active: selectedColor === c }"
:data-color="c" :style="{ background: c }" :title="c"
@click="setSelectionColor(c)"
></button>
<input
v-model="hexDraft" class="upg-hex" placeholder="#hex"
spellcheck="false" @change="onHexInput" @keyup.enter="onHexInput"
/>
</div>
<!-- ★ THE SELECTION MAKES SOMETHING. Driver: the batch tool is "super buggy", and letting
you change the colour of regions is NOT what is wanted — "the batch selection tool
should let you create your own poster or prayer packet based on your own selection."
So the colour swatches and the hex input are GONE, and the actions that remain BUILD
the two artefacts this app already knows how to make, from exactly what was picked.
These reuse printPrayerPacket / printPoster rather than growing a third pipeline. -->
<div v-if="selCount" class="upg-hud-actions">
<button class="upg-hud-btn copy" @click="copySelection">Copy selection</button>
<button class="upg-hud-btn clear" @click="clearSelection">Clear</button>
<button class="upg-hud-btn make" :disabled="batchBusy"
@click="makeSelectionPacket">{{ t('buttons.prayerList') }}</button>
<button class="upg-hud-btn make" :disabled="batchBusy"
@click="makeSelectionPoster">{{ t('buttons.posterMap') }}</button>
<button class="upg-hud-btn clear" @click="clearSelection">{{ t('buttons.clearSelection') }}</button>
</div>
<div v-if="selCount" class="upg-hud-flash">{{ copyFlash }}</div>
</div>

View file

@ -18,8 +18,9 @@
*
* The `/` keyboard shortcut from the original PplrGeocoder is preserved.
*/
import { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue'
import { ensureGeocoder } from '../utils/pplrData.js'
import { ref, watch, onMounted, onBeforeUnmount, computed, inject } from 'vue'
import { ensureGeocoder, GEOCODER } from '../utils/pplrData.js'
import { resolveMapProvider } from '@map/composables/useMapProvider.js'
import { useInstance } from '../composables/usePplrInstance.js'
const props = defineProps({
@ -44,6 +45,10 @@ const wrapEl = ref(null)
const slotEl = ref(null)
let geocoder = null
// Same engine the map uses. Under maplibre (the keyless gallery) search runs on MaplibreGeocoder
// over this map's own local index only, so it makes no Mapbox request at all.
const provider = resolveMapProvider(inject('profileConfig', null))
const isMaplibre = provider === 'maplibre'
// Build the localGeocoder callback. Mapbox-gl-geocoder calls this on every
// keystroke; we return Carmen-GeoJSON-shaped features for matching local
@ -116,7 +121,7 @@ function renderSuggestion(item) {
// Shadow DOM is sealed from regular-document styles, so we must inject the same
// stylesheet into each shadow root that hosts a geocoder. Idempotent — checks
// if already present.
const GEOCODER_CSS_URL = 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css'
const GEOCODER_CSS_URL = (GEOCODER[provider] || GEOCODER.mapbox).css
function ensureGeocoderCss(root) {
if (!root || root === document) return
if (root.querySelector('link[data-pplr-geocoder-css]')) return
@ -134,6 +139,22 @@ function setup() {
// from mounting. Per user 2026-05-02: map went completely blank when
// the geocoder threw on shadow-DOM addTo().
try {
if (isMaplibre) {
if (!window.MaplibreGeocoder || !window.maplibregl) {
console.warn('[MapboxGeocoder] maplibre-gl-geocoder or maplibre-gl not on window — search disabled')
return
}
// Local-only: the PG/family index this map already holds. No place search, no key.
geocoder = new window.MaplibreGeocoder({ forwardGeocode: async () => ({ type: 'FeatureCollection', features: [] }) }, {
maplibregl: window.maplibregl,
placeholder: props.placeholder,
localGeocoder: buildLocalGeocoder(),
localGeocoderOnly: true,
minLength: props.minChars,
showResultsWhileTyping: true,
render: renderSuggestion,
})
} else {
if (!window.MapboxGeocoder || !window.mapboxgl) {
console.warn('[MapboxGeocoder] mapbox-gl-geocoder or mapbox-gl not on window — check CDN <script> tags')
return
@ -152,6 +173,7 @@ function setup() {
limit: 5,
render: renderSuggestion,
})
}
geocoder.on('result', (ev) => {
const f = ev.result
@ -222,7 +244,7 @@ function onGlobalKey(e) {
const isCmdK = (e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')
if (isSlash || isCmdK) {
e.preventDefault()
const input = slotEl.value?.querySelector('input.mapboxgl-ctrl-geocoder--input')
const input = slotEl.value?.querySelector('input.mapboxgl-ctrl-geocoder--input, input.maplibregl-ctrl-geocoder--input')
input?.focus()
input?.select?.()
}
@ -230,7 +252,7 @@ function onGlobalKey(e) {
onMounted(() => {
// setup() swallows its own errors; a failed script load leaves the map working without search.
ensureGeocoder().then(setup, (e) => console.warn('[MapboxGeocoder]', e.message))
ensureGeocoder(provider).then(setup, (e) => console.warn('[MapboxGeocoder]', e.message))
document.addEventListener('keydown', onGlobalKey)
})
onBeforeUnmount(() => {

View file

@ -21,6 +21,22 @@
* (one search per map cluster, per QA "unified geocoder" decision).
*/
import { ref, computed, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
// Accessible names from the shared catalog (legend.a11y.*, aria.*), the keys the library legend
// uses, so they are translated in all eleven locales. i18n is OPTIONAL: on a host without it, the
// English fallback below is used, never a raw key.
const A11Y_EN = {
'legend.a11y.expandRow': 'Expand {label}', 'legend.a11y.collapseRow': 'Collapse {label}',
'legend.a11y.clearSelection': 'Clear selection',
'aria.expandLegend': 'Expand legend', 'aria.collapseLegend': 'Collapse legend',
}
const fillA11y = (str, p) => String(str).replace(/\{(\w+)\}/g, (m, k) => (p && k in p ? p[k] : m))
let ta = (key, p) => fillA11y(A11Y_EN[key] ?? key, p)
try {
const i18n = useI18n()
ta = (key, p) => { const v = i18n.t(key, p || {}); return v === key ? fillA11y(A11Y_EN[key] ?? key, p) : v }
} catch (_) { /* no i18n on this host */ }
import { useInstance } from '../composables/usePplrInstance.js'
const props = defineProps({
@ -373,6 +389,8 @@ function rowClasses(row) {
class="stl-collapse-btn"
:class="{ closed: !panelOpen }"
:title="panelOpen ? 'Collapse legend' : 'Expand legend'"
:aria-label="panelOpen ? ta('aria.collapseLegend') : ta('aria.expandLegend')"
:aria-expanded="String(panelOpen)"
@click="panelOpen = !panelOpen">
<svg width="9" height="9" viewBox="0 0 12 12" fill="none">
<path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.8"
@ -442,6 +460,8 @@ function rowClasses(row) {
class="stl-trunk" :class="`stl-trunk-${seg}`"></span>
<button v-if="row.node.children?.length"
class="stl-caret" :class="{ open: expandedIds.has(row.node.id) }"
:aria-expanded="expandedIds.has(row.node.id) ? 'true' : 'false'"
:aria-label="ta(expandedIds.has(row.node.id) ? 'legend.a11y.collapseRow' : 'legend.a11y.expandRow', { label: row.node.label })"
@click.stop="toggleExpand(row.node.id, $event)">
<svg width="9" height="9" viewBox="0 0 12 12" fill="none">
<path d="M4.5 3L7.5 6L4.5 9" stroke="currentColor" stroke-width="1.6"
@ -456,7 +476,7 @@ function rowClasses(row) {
<span v-if="hasPop" class="stl-num">{{ fmtPop(row.node.pop) }}</span>
<span class="stl-x-slot">
<button v-if="selectedId === row.node.id"
class="stl-x-chip" title="Clear selection"
class="stl-x-chip" :title="ta('legend.a11y.clearSelection')" :aria-label="ta('legend.a11y.clearSelection')"
@click.stop="deselect($event)">
<svg width="8" height="8" viewBox="0 0 12 12" fill="none">
<path d="M2 2L10 10M10 2L2 10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
@ -485,7 +505,9 @@ function rowClasses(row) {
<button v-if="!panelOpen"
class="stl-reopen"
:data-theme="instance?.theme.value || 'dark'"
title="Expand legend"
:title="ta('aria.expandLegend')"
:aria-label="ta('aria.expandLegend')"
aria-expanded="false"
@click="panelOpen = true">
<svg class="stl-reopen-icon" width="11" height="11" viewBox="0 0 12 12" fill="none">
<path d="M2 4h8M2 6h8M2 8h5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>

View file

@ -33,17 +33,58 @@ export function loadNames() {
return namesPromise
}
// mapbox-gl-geocoder is a page global too (the original page carried a <script> tag). Load it
// once, on first need, so the search box works wherever the bundle is embedded.
const GEOCODER_JS = 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js'
let geocoderPromise = null
export function ensureGeocoder() {
if (typeof window === 'undefined' || window.MapboxGeocoder) return Promise.resolve()
if (!geocoderPromise) geocoderPromise = new Promise((res, rej) => {
// The geocoder is a page global too (the original page carried a <script> tag). Load it once, on
// first need, and pick it BY ENGINE, as library/components/map-controls/GeocoderComponent.vue does:
// the Mapbox plugin under provider=mapbox, MapLibre's under provider=maplibre. A hard-coded Mapbox
// script made this the only request to api.mapbox.com in the keyless (maplibre) gallery.
export const GEOCODER = {
mapbox: { js: 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js',
css: 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css',
global: 'MapboxGeocoder' },
maplibre: { js: 'https://cdn.jsdelivr.net/npm/@maplibre/maplibre-gl-geocoder@1/dist/maplibre-gl-geocoder.min.js',
css: 'https://cdn.jsdelivr.net/npm/@maplibre/maplibre-gl-geocoder@1/dist/maplibre-gl-geocoder.css',
global: 'MaplibreGeocoder' },
}
const geocoderPromises = {}
export function ensureGeocoder(provider = 'mapbox') {
const g = GEOCODER[provider] || GEOCODER.mapbox
if (typeof window === 'undefined' || window[g.global]) return Promise.resolve()
if (!geocoderPromises[provider]) geocoderPromises[provider] = new Promise((res, rej) => {
const s = document.createElement('script')
s.src = GEOCODER_JS
s.onload = () => res(); s.onerror = () => { geocoderPromise = null; rej(new Error('geocoder failed to load')) }
s.src = g.js
s.onload = () => res(); s.onerror = () => { delete geocoderPromises[provider]; rej(new Error('geocoder failed to load')) }
document.head.appendChild(s)
})
return geocoderPromise
return geocoderPromises[provider]
}
// The engine namespace that built THIS map (Popup, Marker, NavigationControl…). The views were
// written against the Mapbox global; under provider=maplibre that global does not exist and a
// `new mapboxgl.Popup` throws on the first pin click.
export function glFor(map) {
if (typeof window === 'undefined') return undefined
const ml = window.maplibregl
if (ml && map && ml.Map && map instanceof ml.Map) return ml
return window.mapboxgl || ml
}
// Verification handle (the gospel-resources __grState contract): what the ACTIVE view's map
// actually did, so a check can assert "loaded" rather than inferring it from missing requests.
// window.__pplrMap() -> { engine, loaded, styleLayers, pplrLayers }
export function exposeMap(map) {
if (typeof window === 'undefined' || !map) return
let loaded = false
map.once('load', () => { loaded = true })
window.__pplrMap = () => {
let layers = []
try { layers = map.getStyle()?.layers || [] } catch (_) {}
return {
engine: window.maplibregl && window.maplibregl.Map && map instanceof window.maplibregl.Map ? 'maplibre' : 'mapbox',
// 'load' may already have fired before this listener was attached; a loaded style says so too
loaded: loaded || !!(map.isStyleLoaded && map.isStyleLoaded()),
styleLayers: layers.length,
// sources this bundle added (not the basemap's): proof the view's own data reached the map
dataSources: (() => { try { return Object.keys(map.getStyle()?.sources || {}).filter((k) => !/^(composite|mapbox|openmaptiles|natural_earth|ne2_shaded|maplibre)/i.test(k)).length } catch (_) { return 0 } })(),
}
}
}

View file

@ -29,7 +29,7 @@ export const LOCALE_NAMES = {
}
// ── PPLR FeatureServer — country polygons (live, not bundled) ────────────────
import { dataUrl } from './pplrData.js'
import { dataUrl, glFor } from './pplrData.js'
export const COUNTRY_FS_URL = 'https://services2.arcgis.com/S4ydGgujXcif36k3/arcgis/rest/services/aCountry/FeatureServer/0/query'
export const COUNTRY_FS_FIELDS = ['GENCalpha3', 'ISOalpha2', 'CtryNm', 'ctryNmDisp']
@ -282,7 +282,7 @@ export function makePinIndicator() {
if (!map || !lngLat) return
ensureEl(color)
if (!marker) {
marker = new mapboxgl.Marker({ element: el, anchor: 'bottom' })
marker = new (glFor(map)).Marker({ element: el, anchor: 'bottom' })
}
marker.setLngLat(lngLat).addTo(map)
},

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap } from '../utils/pplrData.js'
/**
* pplr-coverage.vue — Country choropleth: avg # of locales filled per PG in that country.
*
@ -31,7 +31,7 @@ function reload() { window.location.reload() }
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [25, 18],
zoom: 1.6
})
@ -50,6 +50,7 @@ async function streamCoverage() {
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
map.value.on('load', async () => {
try {
status.value = 'Loading per-country coverage…'
@ -105,7 +106,7 @@ onMounted(async () => {
<b>${p.avgFilled != null ? p.avgFilled.toFixed(1) : '—'}</b> / 17</td></tr>
</table>
</div>`
new mapboxgl.Popup({ maxWidth: '300px' })
new (glFor(map.value)).Popup({ maxWidth: '300px' })
.setLngLat(e.lngLat).setHTML(html).addTo(map.value)
})
@ -113,7 +114,7 @@ onMounted(async () => {
const { pinLayerId } = await addPGPoints(map.value, { circleColor: '#fef3c7' })
map.value.on('click', pinLayerId, e => {
const f = e.features[0]
new mapboxgl.Popup({ maxWidth: '320px' })
new (glFor(map.value)).Popup({ maxWidth: '320px' })
.setLngLat(f.geometry.coordinates).setHTML(popupForPG(f.properties)).addTo(map.value)
})
status.value = `Ready · ${countryStats.size} countries colored by avg translation locales / 17 (all ${[...countryStats.values()].reduce((n, c) => n + c.pgCount, 0).toLocaleString('en-US')} PG-in-country rows)`

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap } from '../utils/pplrData.js'
/**
* pplr-language-families.vue — PG pins colored by PPLR ethnolinguistic family.
*
@ -12,6 +12,8 @@ import { dataUrl } from '../utils/pplrData.js'
* Each depth level = one tab. Filter expressions are pre-built into each tree node.
*/
import { inject, onMounted, onBeforeUnmount, ref, computed, watch } from 'vue'
import { resolveNamedStyle } from '@map/styles/registry.js'
import { resolveMapProvider } from '@map/composables/useMapProvider.js'
import { useMapInstance } from '@map/composables/useMapInstance.js'
import { addCountryPolygons, POPUP_CSS, FullscreenToggleControl, makePinIndicator, bindFuzzyPinClick } from '../utils/pplrMap.js'
import SemanticTreeLegend from '../components/SemanticTreeLegend.vue'
@ -23,6 +25,8 @@ import { createPplrInstance, provideInstance } from '../composables/usePplrInsta
const pplrInstance = createPplrInstance('language-families')
provideInstance(pplrInstance)
// Registry names resolve per engine, so the Dark/Satellite/Light switch works under MapLibre too.
const mapProvider = resolveMapProvider(inject('profileConfig', null))
const mapboxToken = inject('mapboxToken')
const mapContainer = ref(null)
const profileRoot = ref(null) // FullscreenControl target — fullscreens .profile so chrome stays visible
@ -32,7 +36,7 @@ const PIN_LAYER = 'lf-pins'
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [25, 18],
zoom: 1.6,
projection: 'mercator' // pinned at construct time — no globe→mercator reproject flash
@ -100,9 +104,9 @@ const projection = ref('mercator') // default mercator per user spec
const showBasemap = ref(false)
const currentBasemap = ref('dark')
const BASEMAPS = [
{ key: 'dark', label: 'Dark', url: 'mapbox://styles/mapbox/dark-v11' },
{ key: 'satellite', label: 'Satellite', url: 'mapbox://styles/mapbox/satellite-streets-v12' },
{ key: 'light', label: 'Light', url: 'mapbox://styles/mapbox/light-v11' },
{ key: 'dark', label: 'Dark', url: 'ink' },
{ key: 'satellite', label: 'Satellite', url: 'hybrid' },
{ key: 'light', label: 'Light', url: 'paper' },
]
const pgData = ref(null)
@ -370,7 +374,7 @@ function setBasemap(key) {
map.value.once('style.load', () => { setupLayers(); map.value.setProjection(projection.value) })
// diff:false suppresses Mapbox's "Unimplemented: setSprite" warning between
// dark-v11 and light-v11. We re-add layers in setupLayers anyway.
map.value.setStyle(style, { diff: false })
map.value.setStyle(resolveNamedStyle(style, mapProvider).style, { diff: false })
}
function toggleTheme() {
@ -422,8 +426,9 @@ function popupHtml(p) {
// ── Lifecycle ─────────────────────────────────────────────────────────────────
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
if (map.value) {
map.value.addControl(new mapboxgl.NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new (glFor(map.value)).NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new FullscreenToggleControl(profileRoot.value), 'bottom-right')
}
map.value.on('load', async () => {

View file

@ -5,6 +5,7 @@
* Individual pins at all zoom levels; click pin for full Rosetta popup; hover country for highlight.
*/
import { inject, onMounted, onBeforeUnmount, ref } from 'vue'
import { glFor, exposeMap } from '../utils/pplrData.js'
import { useMapInstance } from '@map/composables/useMapInstance.js'
import { addCountryPolygons, addPGPoints, popupForPG, POPUP_CSS } from '../utils/pplrMap.js'
import LoadingGlobe from '../components/LoadingGlobe.vue'
@ -26,13 +27,14 @@ const DATA_SOURCES = [
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [25, 18],
zoom: 1.6
})
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
map.value.on('load', async () => {
try {
status.value = 'Adding country polygons…'
@ -42,7 +44,7 @@ onMounted(async () => {
map.value.on('click', pinLayerId, e => {
const f = e.features[0]
new mapboxgl.Popup({ maxWidth: '340px' })
new (glFor(map.value)).Popup({ maxWidth: '340px' })
.setLngLat(f.geometry.coordinates)
.setHTML(popupForPG(f.properties))
.addTo(map.value)

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap } from '../utils/pplrData.js'
/**
* pplr-religion.vue — PG pins colored by primary religion.
*
@ -8,6 +8,8 @@ import { dataUrl } from '../utils/pplrData.js'
* Uses the `rg` field pre-computed in pg-points.geojson.
*/
import { inject, onMounted, onBeforeUnmount, ref, computed, watch } from 'vue'
import { resolveNamedStyle } from '@map/styles/registry.js'
import { resolveMapProvider } from '@map/composables/useMapProvider.js'
import { useMapInstance } from '@map/composables/useMapInstance.js'
import { addCountryPolygons, POPUP_CSS, COUNTRY_FS_URL, COUNTRY_FS_FIELDS, FullscreenToggleControl, makePinIndicator, bindFuzzyPinClick } from '../utils/pplrMap.js'
import MapboxGeocoder from '../components/MapboxGeocoder.vue'
@ -19,6 +21,8 @@ import { createPplrInstance, provideInstance } from '../composables/usePplrInsta
const pplrInstance = createPplrInstance('religion')
provideInstance(pplrInstance)
// Registry names resolve per engine, so the Dark/Satellite/Light switch works under MapLibre too.
const mapProvider = resolveMapProvider(inject('profileConfig', null))
const mapboxToken = inject('mapboxToken')
const mapContainer = ref(null)
const profileRoot = ref(null) // FullscreenControl target — fullscreens .profile so chrome stays visible
@ -30,9 +34,9 @@ const projection = ref('mercator')
const showBasemap = ref(false)
const currentBasemap = ref('dark')
const BASEMAPS = [
{ key: 'dark', label: 'Dark', url: 'mapbox://styles/mapbox/dark-v11' },
{ key: 'satellite', label: 'Satellite', url: 'mapbox://styles/mapbox/satellite-streets-v12' },
{ key: 'light', label: 'Light', url: 'mapbox://styles/mapbox/light-v11' },
{ key: 'dark', label: 'Dark', url: 'ink' },
{ key: 'satellite', label: 'Satellite', url: 'hybrid' },
{ key: 'light', label: 'Light', url: 'paper' },
]
const pgData = ref(null)
const dataReady = ref(false)
@ -57,7 +61,7 @@ function setBasemap(key) {
map.value.once('style.load', () => { setupLayers(); map.value.setProjection(projection.value) })
// diff:false suppresses Mapbox's "Unimplemented: setSprite" warning between
// dark-v11 and light-v11. We re-add layers in setupLayers anyway.
map.value.setStyle(style, { diff: false })
map.value.setStyle(resolveNamedStyle(style, mapProvider).style, { diff: false })
}
function setupLayers() {
@ -108,7 +112,7 @@ const DATA_SOURCES = [
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [25, 18],
zoom: 1.6,
projection: 'mercator' // pinned at construct time — no globe→mercator reproject flash
@ -240,8 +244,9 @@ function popupHtml(p) {
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
if (map.value) {
map.value.addControl(new mapboxgl.NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new (glFor(map.value)).NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new FullscreenToggleControl(profileRoot.value), 'bottom-right')
}
map.value.on('load', async () => {

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap } from '../utils/pplrData.js'
/**
* pplr-rop-tree.vue — ROP hierarchy: Affinity Bloc → Cluster → People Group → PGIC.
*
@ -8,6 +8,8 @@ import { dataUrl } from '../utils/pplrData.js'
* Pins are colored by Affinity Bloc. Each level sorts by pin count desc.
*/
import { inject, onMounted, onBeforeUnmount, ref, computed, shallowRef, watch } from 'vue'
import { resolveNamedStyle } from '@map/styles/registry.js'
import { resolveMapProvider } from '@map/composables/useMapProvider.js'
import { useMapInstance } from '@map/composables/useMapInstance.js'
import { addCountryPolygons, setCountryPolygonTheme, POPUP_CSS, COUNTRY_FS_URL, COUNTRY_FS_FIELDS, FullscreenToggleControl, makePinIndicator, bindFuzzyPinClick } from '../utils/pplrMap.js'
import SemanticTreeLegend from '../components/SemanticTreeLegend.vue'
@ -20,6 +22,8 @@ import { createPplrInstance, provideInstance } from '../composables/usePplrInsta
const pplrInstance = createPplrInstance('rop-tree')
provideInstance(pplrInstance)
// Registry names resolve per engine, so the Dark/Satellite/Light switch works under MapLibre too.
const mapProvider = resolveMapProvider(inject('profileConfig', null))
const mapboxToken = inject('mapboxToken')
const mapContainer = ref(null)
const profileRoot = ref(null) // FullscreenControl target — fullscreens the .profile so chrome stays visible
@ -29,7 +33,7 @@ const PIN_LAYER = 'rop-pins'
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [25, 18],
zoom: 1.6,
projection: 'mercator' // pinned at construct time — see useMapInstance projection arg
@ -78,9 +82,9 @@ const projection = ref('mercator') // default mercator per user spec
const showBasemap = ref(false)
const currentBasemap = ref('dark')
const BASEMAPS = [
{ key: 'dark', label: 'Dark', url: 'mapbox://styles/mapbox/dark-v11' },
{ key: 'satellite', label: 'Satellite', url: 'mapbox://styles/mapbox/satellite-streets-v12' },
{ key: 'light', label: 'Light', url: 'mapbox://styles/mapbox/light-v11' },
{ key: 'dark', label: 'Dark', url: 'ink' },
{ key: 'satellite', label: 'Satellite', url: 'hybrid' },
{ key: 'light', label: 'Light', url: 'paper' },
]
// ── ROP tree (4 generations: Affinity Bloc → People Cluster → ROP25 → ROP3byCountry)
@ -309,7 +313,7 @@ function setBasemap(key) {
// "Unimplemented: setSprite" warning whenever sprite sheets differ between
// dark-v11 and light-v11. We re-add all our layers in setupLayers anyway,
// so a full rebuild is correct and avoids the noisy warning.
map.value.setStyle(style, { diff: false })
map.value.setStyle(resolveNamedStyle(style, mapProvider).style, { diff: false })
}
// Quick light/dark toggle — flips between dark-v11 and light-v11 directly
@ -389,12 +393,13 @@ function popupHtml(p) {
// ── Lifecycle ─────────────────────────────────────────────────────────────────
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
// Navigation: zoom +/- (Mapbox built-in) + custom fullscreen toggle.
// See FullscreenToggleControl in pplrMap.js for why we don't use
// mapboxgl.FullscreenControl with a non-map container.
if (map.value) {
map.value.addControl(new mapboxgl.NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new (glFor(map.value)).NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new FullscreenToggleControl(profileRoot.value), 'bottom-right')
}

View file

@ -13,6 +13,7 @@
* Click any pin → popup with full Rosetta table.
*/
import { inject, onMounted, onBeforeUnmount, ref } from 'vue'
import { glFor, exposeMap } from '../utils/pplrData.js'
import { useMapInstance } from '@map/composables/useMapInstance.js'
import { addCountryPolygons, loadOnce, popupForPG, POPUP_CSS } from '../utils/pplrMap.js'
import LoadingGlobe from '../components/LoadingGlobe.vue'
@ -34,13 +35,14 @@ function reload() { window.location.reload() }
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [50, 22],
zoom: 1.8
})
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
map.value.on('load', async () => {
try {
status.value = 'Loading PG data…'
@ -126,7 +128,7 @@ onMounted(async () => {
${missingIds.join(', ')} ID${missingIds.length > 1 ? 's' : ''} not yet cross-mapped in PPLR — empty rows are a data gap, not an error.
</td></tr>`
: ''
new mapboxgl.Popup({ maxWidth: '360px' })
new (glFor(map.value)).Popup({ maxWidth: '360px' })
.setLngLat(f.geometry.coordinates)
.setHTML(popupForPG(p, rosettaRow + missingNote))
.addTo(map.value)

View file

@ -1,5 +1,5 @@
<script setup>
import { dataUrl } from '../utils/pplrData.js'
import { dataUrl, glFor, exposeMap } from '../utils/pplrData.js'
/**
* pplr-translations.vue — PG name in all 17 locales.
*
@ -26,7 +26,7 @@ const PIN_LAYER = 'trans-pins'
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
accessToken: mapboxToken.value,
style: 'mapbox://styles/mapbox/dark-v11',
style: 'ink', // named: dark-v11 under Mapbox, OpenFreeMap dark under MapLibre
center: [70, 22],
zoom: 2,
projection: 'mercator',
@ -56,8 +56,9 @@ const DATA_SOURCES = [
onMounted(async () => {
await initializeMap()
exposeMap(map.value)
if (map.value) {
map.value.addControl(new mapboxgl.NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new (glFor(map.value)).NavigationControl({ showCompass: false, visualizePitch: false }), 'bottom-right')
map.value.addControl(new FullscreenToggleControl(profileRoot.value), 'bottom-right')
}
map.value.on('load', async () => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <all-people-groups> custom element. -->
<script src="../all-people-groups.js" integrity="sha384-y1vbqdZBzO1YR4TEieGRDPE3lzO9f4mBqgVJCH1KICQRomjsGIu/pRk9uVGi0n86" crossorigin="anonymous"></script>
<script src="../all-people-groups.js" integrity="sha384-tLAMFSmOXgKsuumAYlSOGxWarmOzELfe4FfQqpvfBimiWBm23Ajinbv0qsxRZupf" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
<script src="../cities-internet.js" integrity="sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
<script src="../cities-internet.js" integrity="sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
<script src="../cities-internet.js" integrity="sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
<script src="../../dashboard-plus-grid.js" integrity="sha384-+qkzAU3t2PkgAUw2zihEdB39N5CSzR81Ak7O8wELgkxC/bGiQSXL8IjVUWoAAaT9" crossorigin="anonymous"></script>
<script src="../../dashboard-plus-grid.js" integrity="sha384-neDXieUP4BQiC0yam5g7W4c5d+JOiVaxck//TEhnxBZgDOLNQkYvfOlBTTZwPOBi" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
<script src="../dashboard-plus-grid.js" integrity="sha384-+qkzAU3t2PkgAUw2zihEdB39N5CSzR81Ak7O8wELgkxC/bGiQSXL8IjVUWoAAaT9" crossorigin="anonymous"></script>
<script src="../dashboard-plus-grid.js" integrity="sha384-neDXieUP4BQiC0yam5g7W4c5d+JOiVaxck//TEhnxBZgDOLNQkYvfOlBTTZwPOBi" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <demo-map> custom element. -->
<script src="../demo-map.js" integrity="sha384-lOu5irBnYEEu0wzHP5/F6Xdw90SQFOStrK8TosOU+7h36kCOv1jlLC4K+ZknVri1" crossorigin="anonymous"></script>
<script src="../demo-map.js" integrity="sha384-OiUABqAB3sINj95nEswpAJ2nJBd9bwrMR2bezKyN6WpxHw9/NvUmYMe8o0i1muWV" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -27,7 +27,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <demo-map> custom element. -->
<script src="../../demo-map.js" integrity="sha384-lOu5irBnYEEu0wzHP5/F6Xdw90SQFOStrK8TosOU+7h36kCOv1jlLC4K+ZknVri1" crossorigin="anonymous"></script>
<script src="../../demo-map.js" integrity="sha384-OiUABqAB3sINj95nEswpAJ2nJBd9bwrMR2bezKyN6WpxHw9/NvUmYMe8o0i1muWV" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -13,7 +13,7 @@
"bundle": "all-people-groups",
"scriptUrl": "./all-people-groups/all-people-groups.js",
"output": "doxa-maps/all-people-groups/all-people-groups.js",
"integrity": "sha384-y1vbqdZBzO1YR4TEieGRDPE3lzO9f4mBqgVJCH1KICQRomjsGIu/pRk9uVGi0n86",
"integrity": "sha384-tLAMFSmOXgKsuumAYlSOGxWarmOzELfe4FfQqpvfBimiWBm23Ajinbv0qsxRZupf",
"elements": [
"all-people-groups"
],
@ -29,7 +29,7 @@
"scriptUrl": "./all-people-groups/all-people-groups.js",
"embed": {
"iframe": "<iframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-y1vbqdZBzO1YR4TEieGRDPE3lzO9f4mBqgVJCH1KICQRomjsGIu/pRk9uVGi0n86\" crossorigin=\"anonymous\"></script>\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-tLAMFSmOXgKsuumAYlSOGxWarmOzELfe4FfQqpvfBimiWBm23Ajinbv0qsxRZupf\" crossorigin=\"anonymous\"></script>\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
},
"bundle": "all-people-groups"
}
@ -105,7 +105,7 @@
"bundle": "cities-internet",
"scriptUrl": "./cities-internet/cities-internet.js",
"output": "doxa-maps/cities-internet/cities-internet.js",
"integrity": "sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4",
"integrity": "sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe",
"elements": [
"cities-internet-map"
],
@ -121,7 +121,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/connectivity-list/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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
},
{
@ -135,7 +135,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
},
{
@ -149,7 +149,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/data-tables/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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
}
]
@ -158,7 +158,7 @@
"bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js",
"output": "doxa-maps/country-posters/country-posters.js",
"integrity": "sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo",
"integrity": "sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D",
"elements": [
"country-posters-map"
],
@ -178,7 +178,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -192,7 +192,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-poster-wall/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=\"./country-posters/country-posters.js\" integrity=\"sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -206,7 +206,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
}
]
@ -215,7 +215,7 @@
"bundle": "dashboard-plus-grid",
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"output": "doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js",
"integrity": "sha384-+qkzAU3t2PkgAUw2zihEdB39N5CSzR81Ak7O8wELgkxC/bGiQSXL8IjVUWoAAaT9",
"integrity": "sha384-neDXieUP4BQiC0yam5g7W4c5d+JOiVaxck//TEhnxBZgDOLNQkYvfOlBTTZwPOBi",
"elements": [
"dashboard-plus-grid"
],
@ -235,7 +235,7 @@
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-+qkzAU3t2PkgAUw2zihEdB39N5CSzR81Ak7O8wELgkxC/bGiQSXL8IjVUWoAAaT9\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-neDXieUP4BQiC0yam5g7W4c5d+JOiVaxck//TEhnxBZgDOLNQkYvfOlBTTZwPOBi\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
},
"instances": [
{
@ -259,7 +259,7 @@
"bundle": "demo-map",
"scriptUrl": "./demo-map/demo-map.js",
"output": "doxa-maps/demo-map/demo-map.js",
"integrity": "sha384-lOu5irBnYEEu0wzHP5/F6Xdw90SQFOStrK8TosOU+7h36kCOv1jlLC4K+ZknVri1",
"integrity": "sha384-OiUABqAB3sINj95nEswpAJ2nJBd9bwrMR2bezKyN6WpxHw9/NvUmYMe8o0i1muWV",
"elements": [
"demo-map"
],
@ -275,7 +275,7 @@
"scriptUrl": "./demo-map/demo-map.js",
"embed": {
"iframe": "<iframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-lOu5irBnYEEu0wzHP5/F6Xdw90SQFOStrK8TosOU+7h36kCOv1jlLC4K+ZknVri1\" crossorigin=\"anonymous\"></script>\n<demo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-OiUABqAB3sINj95nEswpAJ2nJBd9bwrMR2bezKyN6WpxHw9/NvUmYMe8o0i1muWV\" crossorigin=\"anonymous\"></script>\n<demo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
},
"instances": [
{
@ -354,7 +354,7 @@
"bundle": "doxa-research-map",
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"output": "doxa-maps/doxa-research-map/doxa-research-map.js",
"integrity": "sha384-jfyq06QMD5wn7PNdNkn96eZgO2SlRjqD5uzcgA3hJ9dBgHDHXbhLy8XR+FQAp9zM",
"integrity": "sha384-mCpa55tjv4oxjE8D/jJ0opyMqnZZPqBNSwaEr8C0vRlubYjqOJgx7kxjamu56E2k",
"elements": [
"doxa-research-map"
],
@ -370,7 +370,7 @@
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-jfyq06QMD5wn7PNdNkn96eZgO2SlRjqD5uzcgA3hJ9dBgHDHXbhLy8XR+FQAp9zM\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-mCpa55tjv4oxjE8D/jJ0opyMqnZZPqBNSwaEr8C0vRlubYjqOJgx7kxjamu56E2k\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
},
"instances": [
{
@ -396,7 +396,7 @@
"bundle": "doxa-simple-map",
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"output": "doxa-maps/doxa-simple-map/doxa-simple-map.js",
"integrity": "sha384-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus",
"integrity": "sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7",
"elements": [
"doxa-map",
"doxa-simple-map"
@ -413,7 +413,7 @@
"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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus\" crossorigin=\"anonymous\"></script>\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7\" crossorigin=\"anonymous\"></script>\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
},
"instances": [
{
@ -664,7 +664,7 @@
"bundle": "my-upg-100-list",
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"output": "doxa-maps/my-upg-100-list/my-upg-100-list.js",
"integrity": "sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD",
"integrity": "sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY",
"elements": [
"my-upg-100-list-map"
],
@ -684,7 +684,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -698,7 +698,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -712,7 +712,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
}
]
@ -837,7 +837,7 @@
"bundle": "pplr-data-maps",
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"output": "doxa-maps/pplr-data-maps/pplr-data-maps.js",
"integrity": "sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp",
"integrity": "sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH",
"elements": [
"pplr-data-maps"
],
@ -853,7 +853,7 @@
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"embed": {
"iframe": "<iframe src=\"./pplr-data-maps/pplr-data-maps/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
},
"bundle": "pplr-data-maps"
}
@ -863,7 +863,7 @@
"bundle": "prayer-walk-planner",
"scriptUrl": "./prayer-walk-planner/prayer-walk-planner.js",
"output": "doxa-maps/prayer-walk-planner/prayer-walk-planner.js",
"integrity": "sha384-MfnmQILsSWd3itg/eN9+UVcZZJnQGVMdX0X3XI9BbKlbaTTwY/IfQ5MlVPAv5MKj",
"integrity": "sha384-C6y6vsEGwkvIVSs0s8SKEh1LeEuEOs0KRPg/grFh7BMo9n4BDTJDq7/Sfy/V/VqY",
"elements": [
"prayer-walk-planner"
],
@ -879,7 +879,7 @@
"scriptUrl": "./prayer-walk-planner/prayer-walk-planner.js",
"embed": {
"iframe": "<iframe src=\"./prayer-walk-planner/prayer-walk-planner/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./prayer-walk-planner/prayer-walk-planner.js\" integrity=\"sha384-MfnmQILsSWd3itg/eN9+UVcZZJnQGVMdX0X3XI9BbKlbaTTwY/IfQ5MlVPAv5MKj\" crossorigin=\"anonymous\"></script>\n<prayer-walk-planner profile-config='{\"profile\":\"prayer-walk-planner\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></prayer-walk-planner>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./prayer-walk-planner/prayer-walk-planner.js\" integrity=\"sha384-C6y6vsEGwkvIVSs0s8SKEh1LeEuEOs0KRPg/grFh7BMo9n4BDTJDq7/Sfy/V/VqY\" crossorigin=\"anonymous\"></script>\n<prayer-walk-planner profile-config='{\"profile\":\"prayer-walk-planner\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></prayer-walk-planner>"
}
}
]
@ -888,7 +888,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r",
"integrity": "sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4",
"elements": [
"regions-dashboard"
],
@ -908,7 +908,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -917,12 +917,12 @@
"description": "A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into",
"element": "regions-dashboard",
"type": "single",
"isMap": true,
"isMap": false,
"iframeUrl": "./regions-dashboard/regions-list/index.html",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -936,7 +936,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]
@ -945,7 +945,7 @@
"bundle": "research-dashboard",
"scriptUrl": "./research-dashboard/research-dashboard.js",
"output": "doxa-maps/research-dashboard/research-dashboard.js",
"integrity": "sha384-mYz0wRhq1Kel13s+l46d5eObb7fHRFJ9tpmSj7qDnvoGOY6f1lgYH1UYQqDBPwKI",
"integrity": "sha384-XSv+S9wdJPrm9k2jVIG4/ePgm1bLW8LYytJuk5kqXP9yI5xOuJCZQ44m4XOt1u9n",
"elements": [
"research-dashboard-map"
],
@ -961,7 +961,7 @@
"scriptUrl": "./research-dashboard/research-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-mYz0wRhq1Kel13s+l46d5eObb7fHRFJ9tpmSj7qDnvoGOY6f1lgYH1UYQqDBPwKI\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-XSv+S9wdJPrm9k2jVIG4/ePgm1bLW8LYytJuk5kqXP9yI5xOuJCZQ44m4XOt1u9n\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
},
"bundle": "research-dashboard"
}
@ -1059,7 +1059,7 @@
"bundle": "upg-neighbor",
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"output": "doxa-maps/upg-neighbor/upg-neighbor.js",
"integrity": "sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5",
"integrity": "sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN",
"elements": [
"upg-neighbor"
],
@ -1075,7 +1075,7 @@
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"embed": {
"iframe": "<iframe src=\"./upg-neighbor/upg-neighbor/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-research-map> custom element. -->
<script src="../doxa-research-map.js" integrity="sha384-jfyq06QMD5wn7PNdNkn96eZgO2SlRjqD5uzcgA3hJ9dBgHDHXbhLy8XR+FQAp9zM" crossorigin="anonymous"></script>
<script src="../doxa-research-map.js" integrity="sha384-mCpa55tjv4oxjE8D/jJ0opyMqnZZPqBNSwaEr8C0vRlubYjqOJgx7kxjamu56E2k" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -27,7 +27,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <doxa-research-map> custom element. -->
<script src="../../doxa-research-map.js" integrity="sha384-jfyq06QMD5wn7PNdNkn96eZgO2SlRjqD5uzcgA3hJ9dBgHDHXbhLy8XR+FQAp9zM" crossorigin="anonymous"></script>
<script src="../../doxa-research-map.js" integrity="sha384-mCpa55tjv4oxjE8D/jJ0opyMqnZZPqBNSwaEr8C0vRlubYjqOJgx7kxjamu56E2k" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,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-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,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-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,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-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus" crossorigin="anonymous"></script>
<script src="../doxa-simple-map.js" integrity="sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,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-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -13,7 +13,7 @@
"bundle": "all-people-groups",
"scriptUrl": "./all-people-groups/all-people-groups.js",
"output": "doxa-maps/all-people-groups/all-people-groups.js",
"integrity": "sha384-y1vbqdZBzO1YR4TEieGRDPE3lzO9f4mBqgVJCH1KICQRomjsGIu/pRk9uVGi0n86",
"integrity": "sha384-tLAMFSmOXgKsuumAYlSOGxWarmOzELfe4FfQqpvfBimiWBm23Ajinbv0qsxRZupf",
"elements": [
"all-people-groups"
],
@ -29,7 +29,7 @@
"scriptUrl": "./all-people-groups/all-people-groups.js",
"embed": {
"iframe": "<iframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-y1vbqdZBzO1YR4TEieGRDPE3lzO9f4mBqgVJCH1KICQRomjsGIu/pRk9uVGi0n86\" crossorigin=\"anonymous\"></script>\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-tLAMFSmOXgKsuumAYlSOGxWarmOzELfe4FfQqpvfBimiWBm23Ajinbv0qsxRZupf\" crossorigin=\"anonymous\"></script>\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
},
"bundle": "all-people-groups"
}
@ -105,7 +105,7 @@
"bundle": "cities-internet",
"scriptUrl": "./cities-internet/cities-internet.js",
"output": "doxa-maps/cities-internet/cities-internet.js",
"integrity": "sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4",
"integrity": "sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe",
"elements": [
"cities-internet-map"
],
@ -121,7 +121,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/connectivity-list/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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
},
{
@ -135,7 +135,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
},
{
@ -149,7 +149,7 @@
"scriptUrl": "./cities-internet/cities-internet.js",
"embed": {
"iframe": "<iframe src=\"./cities-internet/data-tables/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=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tWFKP5lpuEIney1vkcEFXPPhlr/fgu3siY7p7J25oMxBOedxS/MJjwKD+FMKnsA4\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-mbN7vJ4SwlPjwow485s6WcZtjgHfRL9c/cEScZGYM4iZVwTX3imUfgz/lNX7UKJe\" crossorigin=\"anonymous\"></script>\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
}
}
]
@ -158,7 +158,7 @@
"bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js",
"output": "doxa-maps/country-posters/country-posters.js",
"integrity": "sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo",
"integrity": "sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D",
"elements": [
"country-posters-map"
],
@ -178,7 +178,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -192,7 +192,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-poster-wall/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=\"./country-posters/country-posters.js\" integrity=\"sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -206,7 +206,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q8/xltlJzL+Ils4lU+8lOsk9QUZQNVGyLQCeIokoWyaQQJyD66uYDHKBnblVaixo\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-Q11zxYDmXp/3KoxkMIRaPGn1KWAer2kQrJTpE/YiwUeasLLy4UNocmXO4qByMX+D\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
}
]
@ -215,7 +215,7 @@
"bundle": "dashboard-plus-grid",
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"output": "doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js",
"integrity": "sha384-+qkzAU3t2PkgAUw2zihEdB39N5CSzR81Ak7O8wELgkxC/bGiQSXL8IjVUWoAAaT9",
"integrity": "sha384-neDXieUP4BQiC0yam5g7W4c5d+JOiVaxck//TEhnxBZgDOLNQkYvfOlBTTZwPOBi",
"elements": [
"dashboard-plus-grid"
],
@ -235,7 +235,7 @@
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-+qkzAU3t2PkgAUw2zihEdB39N5CSzR81Ak7O8wELgkxC/bGiQSXL8IjVUWoAAaT9\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-neDXieUP4BQiC0yam5g7W4c5d+JOiVaxck//TEhnxBZgDOLNQkYvfOlBTTZwPOBi\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
},
"instances": [
{
@ -259,7 +259,7 @@
"bundle": "demo-map",
"scriptUrl": "./demo-map/demo-map.js",
"output": "doxa-maps/demo-map/demo-map.js",
"integrity": "sha384-lOu5irBnYEEu0wzHP5/F6Xdw90SQFOStrK8TosOU+7h36kCOv1jlLC4K+ZknVri1",
"integrity": "sha384-OiUABqAB3sINj95nEswpAJ2nJBd9bwrMR2bezKyN6WpxHw9/NvUmYMe8o0i1muWV",
"elements": [
"demo-map"
],
@ -275,7 +275,7 @@
"scriptUrl": "./demo-map/demo-map.js",
"embed": {
"iframe": "<iframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-lOu5irBnYEEu0wzHP5/F6Xdw90SQFOStrK8TosOU+7h36kCOv1jlLC4K+ZknVri1\" crossorigin=\"anonymous\"></script>\n<demo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-OiUABqAB3sINj95nEswpAJ2nJBd9bwrMR2bezKyN6WpxHw9/NvUmYMe8o0i1muWV\" crossorigin=\"anonymous\"></script>\n<demo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
},
"instances": [
{
@ -354,7 +354,7 @@
"bundle": "doxa-research-map",
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"output": "doxa-maps/doxa-research-map/doxa-research-map.js",
"integrity": "sha384-jfyq06QMD5wn7PNdNkn96eZgO2SlRjqD5uzcgA3hJ9dBgHDHXbhLy8XR+FQAp9zM",
"integrity": "sha384-mCpa55tjv4oxjE8D/jJ0opyMqnZZPqBNSwaEr8C0vRlubYjqOJgx7kxjamu56E2k",
"elements": [
"doxa-research-map"
],
@ -370,7 +370,7 @@
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-jfyq06QMD5wn7PNdNkn96eZgO2SlRjqD5uzcgA3hJ9dBgHDHXbhLy8XR+FQAp9zM\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-mCpa55tjv4oxjE8D/jJ0opyMqnZZPqBNSwaEr8C0vRlubYjqOJgx7kxjamu56E2k\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
},
"instances": [
{
@ -396,7 +396,7 @@
"bundle": "doxa-simple-map",
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
"output": "doxa-maps/doxa-simple-map/doxa-simple-map.js",
"integrity": "sha384-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus",
"integrity": "sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7",
"elements": [
"doxa-map",
"doxa-simple-map"
@ -413,7 +413,7 @@
"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": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-EfPGYd5J6I7s63wk0UDKzcR4cDdzlidQGYZ4+z20DTEExkCUMyUjTOxSY35eTLus\" crossorigin=\"anonymous\"></script>\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-QDnthNH0t4oJYQcYunMxIb9DUGBQRAUm+TPEg2RWvsrkJ4CYrbAlzbyCWoTqOXB7\" crossorigin=\"anonymous\"></script>\n<doxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-map>"
},
"instances": [
{
@ -664,7 +664,7 @@
"bundle": "my-upg-100-list",
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"output": "doxa-maps/my-upg-100-list/my-upg-100-list.js",
"integrity": "sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD",
"integrity": "sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY",
"elements": [
"my-upg-100-list-map"
],
@ -684,7 +684,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -698,7 +698,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
},
{
@ -712,7 +712,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
}
}
]
@ -837,7 +837,7 @@
"bundle": "pplr-data-maps",
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"output": "doxa-maps/pplr-data-maps/pplr-data-maps.js",
"integrity": "sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp",
"integrity": "sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH",
"elements": [
"pplr-data-maps"
],
@ -853,7 +853,7 @@
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
"embed": {
"iframe": "<iframe src=\"./pplr-data-maps/pplr-data-maps/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH\" crossorigin=\"anonymous\"></script>\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
},
"bundle": "pplr-data-maps"
}
@ -863,7 +863,7 @@
"bundle": "prayer-walk-planner",
"scriptUrl": "./prayer-walk-planner/prayer-walk-planner.js",
"output": "doxa-maps/prayer-walk-planner/prayer-walk-planner.js",
"integrity": "sha384-MfnmQILsSWd3itg/eN9+UVcZZJnQGVMdX0X3XI9BbKlbaTTwY/IfQ5MlVPAv5MKj",
"integrity": "sha384-C6y6vsEGwkvIVSs0s8SKEh1LeEuEOs0KRPg/grFh7BMo9n4BDTJDq7/Sfy/V/VqY",
"elements": [
"prayer-walk-planner"
],
@ -879,7 +879,7 @@
"scriptUrl": "./prayer-walk-planner/prayer-walk-planner.js",
"embed": {
"iframe": "<iframe src=\"./prayer-walk-planner/prayer-walk-planner/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./prayer-walk-planner/prayer-walk-planner.js\" integrity=\"sha384-MfnmQILsSWd3itg/eN9+UVcZZJnQGVMdX0X3XI9BbKlbaTTwY/IfQ5MlVPAv5MKj\" crossorigin=\"anonymous\"></script>\n<prayer-walk-planner profile-config='{\"profile\":\"prayer-walk-planner\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></prayer-walk-planner>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./prayer-walk-planner/prayer-walk-planner.js\" integrity=\"sha384-C6y6vsEGwkvIVSs0s8SKEh1LeEuEOs0KRPg/grFh7BMo9n4BDTJDq7/Sfy/V/VqY\" crossorigin=\"anonymous\"></script>\n<prayer-walk-planner profile-config='{\"profile\":\"prayer-walk-planner\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></prayer-walk-planner>"
}
}
]
@ -888,7 +888,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r",
"integrity": "sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4",
"elements": [
"regions-dashboard"
],
@ -908,7 +908,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -917,12 +917,12 @@
"description": "A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into",
"element": "regions-dashboard",
"type": "single",
"isMap": true,
"isMap": false,
"iframeUrl": "./regions-dashboard/regions-list/index.html",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -936,7 +936,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]
@ -945,7 +945,7 @@
"bundle": "research-dashboard",
"scriptUrl": "./research-dashboard/research-dashboard.js",
"output": "doxa-maps/research-dashboard/research-dashboard.js",
"integrity": "sha384-mYz0wRhq1Kel13s+l46d5eObb7fHRFJ9tpmSj7qDnvoGOY6f1lgYH1UYQqDBPwKI",
"integrity": "sha384-XSv+S9wdJPrm9k2jVIG4/ePgm1bLW8LYytJuk5kqXP9yI5xOuJCZQ44m4XOt1u9n",
"elements": [
"research-dashboard-map"
],
@ -961,7 +961,7 @@
"scriptUrl": "./research-dashboard/research-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-mYz0wRhq1Kel13s+l46d5eObb7fHRFJ9tpmSj7qDnvoGOY6f1lgYH1UYQqDBPwKI\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-XSv+S9wdJPrm9k2jVIG4/ePgm1bLW8LYytJuk5kqXP9yI5xOuJCZQ44m4XOt1u9n\" crossorigin=\"anonymous\"></script>\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
},
"bundle": "research-dashboard"
}
@ -1059,7 +1059,7 @@
"bundle": "upg-neighbor",
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"output": "doxa-maps/upg-neighbor/upg-neighbor.js",
"integrity": "sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5",
"integrity": "sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN",
"elements": [
"upg-neighbor"
],
@ -1075,7 +1075,7 @@
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"embed": {
"iframe": "<iframe src=\"./upg-neighbor/upg-neighbor/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-wV3C24ByQTO9YmoAwC+/XTH++0E+XCv7L3L9zp8NW7aRqp/u7gKmXOJ94bsFWRrD" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-vqUN7kfMcv1vUvjQIzNxapDlwPSdtR8atXoIETM0hxoqOCKG+7FAPPltRsA4jukY" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <pplr-data-maps> custom element. -->
<script src="../pplr-data-maps.js" integrity="sha384-7Fp8ORq9SNmtZLwCG1euRM+dGy6sDgD/+Ts4s1B1WVk8XKuMJpYr3dCjoK80zvJp" crossorigin="anonymous"></script>
<script src="../pplr-data-maps.js" integrity="sha384-+EOcMT3P42D39HGIjZBbJ6waYI2yJDhNDwNf+8wFruaHqEhdYYa36M+iLIT5fRiH" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <prayer-walk-planner> custom element. -->
<script src="../prayer-walk-planner.js" integrity="sha384-MfnmQILsSWd3itg/eN9+UVcZZJnQGVMdX0X3XI9BbKlbaTTwY/IfQ5MlVPAv5MKj" crossorigin="anonymous"></script>
<script src="../prayer-walk-planner.js" integrity="sha384-C6y6vsEGwkvIVSs0s8SKEh1LeEuEOs0KRPg/grFh7BMo9n4BDTJDq7/Sfy/V/VqY" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -5,13 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>regions-list · regions-dashboard</title>
<link rel="icon" href="data:,">
<!-- Mapbox peer loaded ONCE here; the map custom element mounts in this page and
uses window.mapboxgl directly (same realm → no cross-realm container errors). -->
<link rel="preconnect" href="https://api.mapbox.com" crossorigin />
<link href="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css" rel="stylesheet" />
<link href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
<!-- NON-MAP leaf (app-shell / composite / grid / card / chart-table): NO map peer.
This element draws no map tiles, so it needs no Mapbox/MapLibre engine and MUST NOT
gate on a token. Any nested MAP (in a composite/shell) renders in its own iframe leaf,
which loads its own map peer + token. A peer here would be dead weight + imply a gate. -->
<style>
/* BARE full-page map — no chrome. The custom element IS the page, so the
browser URL shares the map itself. */
@ -25,11 +22,11 @@
</head>
<body>
<div id="slot"></div>
<div id="status">loading token…</div>
<div id="status">loading…</div>
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:
@ -48,7 +45,7 @@
var BASE_CONFIG = {"pinHint":false,"profile":"regions-list","dataSource":"pray-tools","instanceId":"regions-dashboard-regions-list","assetsBase":"../../assets"};
// MapLibre map pages AND shell pages (app-shell / nested-mfe-spa) need NO token:
// the ladder below is skipped entirely and the element mounts immediately.
var TOKEN_FREE = false;
var TOKEN_FREE = true;
// A token-free COMPOSITE still nests plugins, and a nested Mapbox map reads window.MAP_TOKEN
// rather than this page's profile-config. With no baked token (the public copy), it must get
// one from ?tk= or a pasted token, or be asked for one — see the rungs below.

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-bt2/psRmAJTmO9mm3KULoLN7GSzcxwIeOTbVwIvSkMZOqyzaU5pOt7G0r2w8ir9r" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <research-dashboard-map> custom element. -->
<script src="../research-dashboard.js" integrity="sha384-mYz0wRhq1Kel13s+l46d5eObb7fHRFJ9tpmSj7qDnvoGOY6f1lgYH1UYQqDBPwKI" crossorigin="anonymous"></script>
<script src="../research-dashboard.js" integrity="sha384-XSv+S9wdJPrm9k2jVIG4/ePgm1bLW8LYytJuk5kqXP9yI5xOuJCZQ44m4XOt1u9n" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../upg-neighbor.js" integrity="sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5" crossorigin="anonymous"></script>
<script src="../upg-neighbor.js" integrity="sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-7MsLJUu/zKAjJKFXL9WssodTCfVjVDwIBT7aGrXqfkvlIjavDDzsthDzHkUeiAR5" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-YViVG3snYU3j6AZ8ZxBIJlBBMUL5Pj2guGdWiiKvexR9VNtQFCHrX+PQ6lLa2DrN" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -44,7 +44,9 @@ function readMfeName() {
const MFE_NAME = readMfeName()
// Default output tree. generate-tree.mjs reads the same name + copies it onward.
const OUT_ROOT = resolve(__dirname, 'public', MFE_NAME)
// DOXA_OUT_ROOT redirects a build's output WITHOUT touching public/: check:composite rebuilds each
// composite into a temp dir and compares bytes with the committed bundle. Unset = unchanged behaviour.
const OUT_ROOT = process.env.DOXA_OUT_ROOT ? resolve(process.env.DOXA_OUT_ROOT) : resolve(__dirname, 'public', MFE_NAME)
function discoverBundles() {
if (!existsSync(profilesDir)) return {}