doxa-maps/public/index.html
doxa-maps-88655c70 ff2c12fcff
Some checks are pending
build-gate / build (push) Waiting to run
rebuild the leaf pages with the baked public token
The generated leaf pages carry BAKED_TOKEN, so they change whenever the
build-config token changes. public/doxa-maps is committed as part of the
product, so shipping the config without the regenerated leaves would leave the
tree inconsistent with its own source.

26 of 26 map leaves now carry the pk; the rest are the staging and SPA pages
the generator writes the same block into.

Staged with 'git add -u' so only TRACKED modifications are included: a broad
'git add public/doxa-maps' swept in 378 untracked poster webp files and 376k
insertions, which is the separate untracked-build-outputs question and not this
change. That commit was reset and redone.
2026-09-21 22:48:48 +00:00

363 lines
52 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>doxa-maps</title>
<link rel="icon" href="data:,">
<style>
:root{--bg:#fff;--bg2:#f6f7f9;--bg3:#eceef2;--bd:#e2e5ea;--fg:#15181d;--mut:#667085;--err:#d13c3c}
@media(prefers-color-scheme:dark){:root{--bg:#111318;--bg2:#181b21;--bg3:#232730;--bd:#2b3038;--fg:#eef0f3;--mut:#9aa3b2;--err:#ff7b7b}}
*{box-sizing:border-box}html,body{margin:0;height:100%}
body{background:var(--bg);color:var(--fg);font:14px/1.4 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;display:flex;flex-direction:column;height:100vh}
header{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:8px 14px;border-bottom:1px solid var(--bd)}
header b{font-size:15px}.n{color:var(--mut);font-size:12px}
#menu{display:none;margin-left:auto}
button,.btn{font:inherit;font-size:12px;padding:5px 10px;border:1px solid var(--bd);border-radius:8px;background:var(--bg2);color:var(--fg);cursor:pointer;text-decoration:none;white-space:nowrap}
button:hover,.btn:hover{background:var(--bg3)}
.body{flex:1 1 auto;display:flex;min-height:0}
nav{flex:0 0 270px;overflow:auto;border-right:1px solid var(--bd);padding:6px 0 20px}
.gh{padding:12px 14px 4px;font-weight:600;font-size:13px;display:flex;justify-content:space-between;gap:8px}.gh span{color:var(--mut);font-weight:400}
.row{display:block;width:100%;text-align:left;border:0;border-radius:0;background:none;padding:6px 14px;font-size:13px;line-height:1.3;white-space:normal}
.row.in{padding-left:28px}.row code{display:block;font-size:11px;color:var(--mut)}
.row:hover{background:var(--bg2)}.row[aria-current=true]{background:var(--bg3);font-weight:600}
main{flex:1 1 auto;display:flex;flex-direction:column;min-width:0;padding:10px 12px 12px;gap:8px}
.bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.bar .t{flex:1 1 auto;min-width:0}.bar b{font-size:15px}.s{display:block;font-size:12px;color:var(--mut)}
code{font-family:ui-monospace,Menlo,monospace}
#embed{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--bd);border-radius:8px;background:var(--bg2)}
#embed label{font-size:12px;color:var(--mut);display:flex;justify-content:space-between;align-items:center}
#embed textarea{width:100%;height:58px;font:11px/1.35 ui-monospace,Menlo,monospace;border:1px solid var(--bd);border-radius:6px;background:var(--bg);color:var(--fg);padding:6px;resize:vertical}
#slot{flex:1 1 auto;position:relative;min-height:240px;border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg2)}
#slot>*{position:absolute;inset:0;width:100%;height:100%;display:block;border:0}
.hint{display:grid;place-items:center;padding:20px;text-align:center;color:var(--mut);font-size:13px}
.hint.err{color:var(--err)}
.hint input{font:12px ui-monospace,Menlo,monospace;padding:6px 8px;border-radius:6px;border:1px solid var(--bd);background:var(--bg);color:var(--fg);width:min(320px,70vw)}
/* Capability tiers (5 → 1): the vertical-tab sections of the nested-mfe-spa sidebar. */
.tier{padding:12px 14px 2px;display:flex;align-items:baseline;gap:7px}
.tier .cap{font:700 10px/1 system-ui,sans-serif;letter-spacing:.5px;color:var(--bg);background:var(--mut);border-radius:999px;padding:3px 7px}
.tier .tl{font-weight:600;font-size:12px}.tier .td{color:var(--mut);font-size:11px;margin-left:auto}
#app{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
[hidden]{display:none!important}
@media(max-width:700px){.body{flex-direction:column}nav{flex:0 0 auto;max-height:45vh;border-right:0;border-bottom:1px solid var(--bd)}#menu{display:inline-block}}
</style>
</head>
<body>
<!-- The doxa-maps nested-mfe-spa (capability 5, the reserved app-shell): a Vue single-page app
whose sidebar is the composability ladder rendered as vertical tabs (capability 5 → 1),
each row mounting that plugin's custom element in the one slot. This SAME page is the
served studio root (public/index.html) AND the Capacitor mobile-app entry (webDir: public).
Vue drives the shell; the map elements mount imperatively into #slot (they own their DOM). -->
<div id="app">
<header>
<b>doxa-maps</b><span class="n">{{ countLabel }}</span>
<button id="menu" type="button" aria-controls="nav" aria-expanded="true" @click="navOpen = !navOpen">Maps</button>
</header>
<div class="body">
<nav id="nav" aria-label="Maps · sorted by capability 5 → 1" v-show="navOpen">
<div class="grp" v-for="grp in groups" :key="grp.cap">
<div class="tier"><span class="cap">CAP {{ grp.cap }}</span><span class="tl">{{ grp.label }}</span><span class="td">{{ grp.rows.length }}</span></div>
<button v-for="r in grp.rows" :key="r.key" type="button" class="row" :class="{ in: r.kind === 'instance' }"
:aria-current="String(activeKey === r.key)" @click="go(r.key)">
<span>{{ r.label }}</span><code>&lt;{{ r.tag }}&gt;</code>
</button>
</div>
</nav>
<main>
<div class="bar">
<div class="t"><b>{{ ttl }}</b><span class="s">{{ sub }}</span></div>
<a class="btn" :href="openHref" target="_blank" rel="noopener" v-show="openHref">Open page</a>
<button type="button" v-show="hasSel" :aria-expanded="String(embedOpen)" @click="embedOpen = !embedOpen">Copy embed</button>
</div>
<div id="embed" v-show="embedOpen && hasSel">
<label>iframe embed <button type="button" @click="copyText(embedIframe, $event.target)">Copy</button></label>
<textarea readonly :value="embedIframe"></textarea>
<div v-show="embedScript"><label>script embed <button type="button" @click="copyText(embedScript, $event.target)">Copy</button></label>
<textarea readonly :value="embedScript"></textarea></div>
</div>
<div id="slot" ref="slot"><div class="hint">Choose a map from the list.</div></div>
</main>
</div>
</div>
<script>
(function () {
var TREE = "doxa-maps", TOKEN_SRC = "https://doxa.life/api/maps/token", BAKED_TOKEN = "pk.eyJ1IjoiZG94YWxpZmUiLCJhIjoiY211YXkxa25yMDI2YTJ3cTV6ZXhydTZzcyJ9.fBMmvu0Y0S9u8tnHOyVhxg", API_BASE = "https://pray.doxa.life", ASSETS_BASE = "", QA = false;
var DEFAULT_TOKEN_URL = '/api/maps/token';
var BAKED = [{"b":"all-people-groups","s":"./all-people-groups/all-people-groups.js","i":"sha384-AeyIQqjjJaKGyHFYQuaB9q+qglqSGJn3+tuwsyThqyk0QvIZd00wjWOG0f/7FgDB","e":["all-people-groups"],"p":[{"p":"all-people-groups","e":"all-people-groups","u":"./all-people-groups/all-people-groups/index.html","d":"All 12,379 people groups (peoplegroups.org) in four colors — reached,","c":1,"em":{"iframe":"\u003ciframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-AeyIQqjjJaKGyHFYQuaB9q+qglqSGJn3+tuwsyThqyk0QvIZd00wjWOG0f/7FgDB\" crossorigin=\"anonymous\">\u003c/script>\n\u003call-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/all-people-groups>"}}]},{"b":"big-picture","s":"./big-picture/big-picture.js","i":"sha384-soRngAClVe33aZ0x9qX5YGVjmOCWml6J6p7h6ZYAFgGI+fpWolApFo/Y3UZYlMBi","e":["bp-big-picture","bp-research-header","people-groups-grid"],"p":[{"p":"big-picture","e":"bp-big-picture","u":"./big-picture/big-picture/index.html","d":"The Cat-4 composite that PAIRS the two real capability elements of the","c":4,"em":{"iframe":"\u003ciframe src=\"./big-picture/big-picture/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./big-picture/big-picture.js\" integrity=\"sha384-soRngAClVe33aZ0x9qX5YGVjmOCWml6J6p7h6ZYAFgGI+fpWolApFo/Y3UZYlMBi\" crossorigin=\"anonymous\">\u003c/script>\n\u003cbp-big-picture profile-config='{\"profile\":\"big-picture\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/bp-big-picture>"},"in":[{"id":"big-picture","t":"Big Picture — research header ↔ people-groups grid (composite)","a":"","pr":{"profile":"big-picture","provider":"maplibre","instanceId":"bp-big-picture"},"e":"bp-big-picture","u":"./big-picture/big-picture/big-picture/index.html"},{"id":"frontier","t":"Big Picture — frontier accent","a":"","pr":{"profile":"big-picture","provider":"maplibre","instanceId":"bp-big-picture-frontier","accent":"#22d3ee","title":"Big Picture — Frontier Focus"},"e":"bp-big-picture","u":"./big-picture/big-picture/frontier/index.html"}]}]},{"b":"cities-internet","s":"./cities-internet/cities-internet.js","i":"sha384-qbG3fPSVDKs4rfjkWEweI1+EunifrZM3FBqO9yMeVAwolOdUxBoA5g39a8XPIktR","e":["cities-internet-map"],"p":[{"p":"connectivity-list","e":"cities-internet-map","u":"./cities-internet/connectivity-list/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-qbG3fPSVDKs4rfjkWEweI1+EunifrZM3FBqO9yMeVAwolOdUxBoA5g39a8XPIktR\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/cities-internet-map>"}},{"p":"connectivity-map","e":"cities-internet-map","u":"./cities-internet/connectivity-map/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-qbG3fPSVDKs4rfjkWEweI1+EunifrZM3FBqO9yMeVAwolOdUxBoA5g39a8XPIktR\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/cities-internet-map>"}},{"p":"data-tables","e":"cities-internet-map","u":"./cities-internet/data-tables/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./cities-internet/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-qbG3fPSVDKs4rfjkWEweI1+EunifrZM3FBqO9yMeVAwolOdUxBoA5g39a8XPIktR\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/cities-internet-map>"}}]},{"b":"country-posters","s":"./country-posters/country-posters.js","i":"sha384-POdBeC047yvfZfATNOCKl9o+6y1GUpGIF7TqM08xEZOyp6z8ddQUsAo6g/RmLgXD","e":["country-posters-map"],"p":[{"p":"countries-map","e":"country-posters-map","u":"./country-posters/countries-map/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-POdBeC047yvfZfATNOCKl9o+6y1GUpGIF7TqM08xEZOyp6z8ddQUsAo6g/RmLgXD\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/country-posters-map>"}},{"p":"country-poster-wall","e":"country-posters-map","u":"./country-posters/country-poster-wall/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-POdBeC047yvfZfATNOCKl9o+6y1GUpGIF7TqM08xEZOyp6z8ddQUsAo6g/RmLgXD\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/country-posters-map>"}}]},{"b":"dashboard-plus-grid","s":"./dashboard-plus-grid/dashboard-plus-grid.js","i":"sha384-GaAMOd1oLnQs0M3h8IwpjV7Iwi/FI32DBPmjACf2X8CykYxJbPg+4rpJZ459r9BK","e":["dashboard-plus-grid"],"p":[{"p":"dashboard-plus-grid","e":"dashboard-plus-grid","u":"./dashboard-plus-grid/dashboard-plus-grid/index.html","d":"Both children are IMPORTED DIRECTLY (compiled into this ONE bundle — no runtime bundle","c":4,"em":{"iframe":"\u003ciframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-GaAMOd1oLnQs0M3h8IwpjV7Iwi/FI32DBPmjACf2X8CykYxJbPg+4rpJZ459r9BK\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/dashboard-plus-grid>"},"in":[{"id":"default","t":"default","a":"","pr":{"profile":"dashboard-plus-grid"},"e":"dashboard-plus-grid","u":"./dashboard-plus-grid/dashboard-plus-grid/default/index.html"}]}]},{"b":"demo-map","s":"./demo-map/demo-map.js","i":"sha384-wdzP6YWWGnw/NVBIByscFsrzGjwzPwXjQ82bRMQNiyYvy9IdPK/wVodaNckMAjGU","e":["demo-map"],"p":[{"p":"demo-map","e":"demo-map","u":"./demo-map/demo-map/index.html","d":"Simple two-tab demo map. Tab 1 colors pins by religion (shared","c":2,"em":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./demo-map/demo-map.js\" integrity=\"sha384-wdzP6YWWGnw/NVBIByscFsrzGjwzPwXjQ82bRMQNiyYvy9IdPK/wVodaNckMAjGU\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdemo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/demo-map>"},"in":[{"id":"maplibre","t":"Demo map — MapLibre (token-free)","a":"","pr":{"profile":"demo-map","provider":"maplibre","instanceId":"demo-maplibre"},"e":"demo-map","u":"./demo-map/demo-map/maplibre/index.html"}]}]},{"b":"doxa-maps-spa","s":"./doxa-maps-spa/doxa-maps-spa.js","i":"sha384-RZwhOAJ23jbKxK/lRCDOz7ggpDTuLcnY+BXl4n6R5KIE9QutGmFdqBJokVbgoduu","e":["doxa-maps-spa"],"p":[{"p":"doxa-maps-spa","e":"doxa-maps-spa","u":"./doxa-maps-spa/doxa-maps-spa/index.html","d":"","c":5,"em":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-RZwhOAJ23jbKxK/lRCDOz7ggpDTuLcnY+BXl4n6R5KIE9QutGmFdqBJokVbgoduu\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-maps-spa>"},"in":[{"id":"app","t":"DX-Maps SPA — Big Picture + every research map (app-shell)","a":"","pr":{"profile":"doxa-maps-spa","provider":"maplibre","instanceId":"doxa-maps-spa-app","title":"DX-Maps"},"e":"doxa-maps-spa","u":"./doxa-maps-spa/doxa-maps-spa/app/index.html"}]}]},{"b":"doxa-research-map","s":"./doxa-research-map/doxa-research-map.js","i":"sha384-edr6cRv7WR9RhOAghHSVSDzZQyGvkNysQgaFyWcrAH5L/jVs4k9bF8LmdInVU8AD","e":["doxa-research-map"],"p":[{"p":"research-map","e":"doxa-research-map","u":"./doxa-research-map/research-map/index.html","d":"","c":3,"em":{"iframe":"\u003ciframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-edr6cRv7WR9RhOAghHSVSDzZQyGvkNysQgaFyWcrAH5L/jVs4k9bF8LmdInVU8AD\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-research-map>"}}]},{"b":"doxa-simple-map","s":"./doxa-simple-map/doxa-simple-map.js","i":"sha384-IG4s3lPJ7ynu/C2T71hjZh+dB4kUpm4W3bqfQ1R6JN+wUMNVIB32Y55k4B/x2lxq","e":["doxa-map","doxa-simple-map"],"p":[{"p":"doxa-simple-map","e":"doxa-map","u":"./doxa-simple-map/doxa-simple-map/index.html","d":"","c":2,"em":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-IG4s3lPJ7ynu/C2T71hjZh+dB4kUpm4W3bqfQ1R6JN+wUMNVIB32Y55k4B/x2lxq\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-map>"},"in":[{"id":"prayer","t":"Prayer Map","a":"https://pray.doxa.life","pr":{"profile":"doxa-simple-map","instanceId":"embed-prayer","dataSource":"pray-tools","tabs":[{"id":"prayer","colorStrategy":"prayer","legend":"prayer","popup":"prayer"}]},"e":"doxa-map","u":"./doxa-simple-map/doxa-simple-map/prayer/index.html"},{"id":"engagement","t":"Engagement Map","a":"https://pray.doxa.life","pr":{"profile":"doxa-simple-map","instanceId":"embed-engagement","dataSource":"pray-tools","tabs":[{"id":"engagement","colorStrategy":"engagement","legend":"engagement","popup":"engagement"}]},"e":"doxa-map","u":"./doxa-simple-map/doxa-simple-map/engagement/index.html"},{"id":"adoption","t":"Adoption Map","a":"https://pray.doxa.life","pr":{"profile":"doxa-simple-map","instanceId":"embed-adoption","dataSource":"pray-tools","tabs":[{"id":"adoption","colorStrategy":"adoption","legend":"adoption","popup":"adoption"}]},"e":"doxa-map","u":"./doxa-simple-map/doxa-simple-map/adoption/index.html"}]}]},{"b":"doxa-vision-card","s":"./doxa-vision-card/doxa-vision-card.js","i":"sha384-2mza3cltN5LQQIrlENG/IVL6Icd9zzcFve3E8R8VWuCdYOGW9uGKFvxcbu6eFfwn","e":["doxa-vision-card"],"p":[{"p":"doxa-vision-card","e":"doxa-vision-card","u":"./doxa-vision-card/doxa-vision-card/index.html","d":"The Capability-1 example: ONE folder -> ONE bundle -> ONE element.","c":2,"em":{"iframe":"\u003ciframe src=\"./doxa-vision-card/doxa-vision-card/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-vision-card/doxa-vision-card.js\" integrity=\"sha384-2mza3cltN5LQQIrlENG/IVL6Icd9zzcFve3E8R8VWuCdYOGW9uGKFvxcbu6eFfwn\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-vision-card profile-config='{\"profile\":\"doxa-vision-card\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-vision-card>"},"in":[{"id":"default","t":"Vision card — reach at a glance","a":"","pr":{"profile":"doxa-vision-card","provider":"maplibre","instanceId":"vision-card-default"},"e":"doxa-vision-card","u":"./doxa-vision-card/doxa-vision-card/default/index.html"},{"id":"frontier","t":"Vision card — frontier accent","a":"","pr":{"profile":"doxa-vision-card","provider":"maplibre","instanceId":"vision-card-frontier","accent":"#22d3ee","title":"The last frontier of the gospel.","tagline":"Frontier people groups — where fewer than one in a thousand follow Jesus.","stats":[{"value":"1,910","label":"Frontier groups","note":"under 0.1% believers"},{"value":"2.1B","label":"People","note":"living in frontier settings"},{"value":"0.1%","label":"Christian","note":"or fewer, in each group"}]},"e":"doxa-vision-card","u":"./doxa-vision-card/doxa-vision-card/frontier/index.html"}]}]},{"b":"gospel-resources","s":"./gospel-resources/gospel-resources.js","i":"sha384-/LdKKZJjypsYpSZ+EH+5/6ZCKIcar2/g7LQn3Qm5AKS4+XBZPmHxGPBvWFNXy2qR","e":["gospel-resources-map"],"p":[{"p":"charts","e":"gospel-resources-map","u":"./gospel-resources/charts/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-/LdKKZJjypsYpSZ+EH+5/6ZCKIcar2/g7LQn3Qm5AKS4+XBZPmHxGPBvWFNXy2qR\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}},{"p":"data-tables","e":"gospel-resources-map","u":"./gospel-resources/data-tables/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-/LdKKZJjypsYpSZ+EH+5/6ZCKIcar2/g7LQn3Qm5AKS4+XBZPmHxGPBvWFNXy2qR\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}},{"p":"resources-list","e":"gospel-resources-map","u":"./gospel-resources/resources-list/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-/LdKKZJjypsYpSZ+EH+5/6ZCKIcar2/g7LQn3Qm5AKS4+XBZPmHxGPBvWFNXy2qR\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}},{"p":"resources-map","e":"gospel-resources-map","u":"./gospel-resources/resources-map/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-/LdKKZJjypsYpSZ+EH+5/6ZCKIcar2/g7LQn3Qm5AKS4+XBZPmHxGPBvWFNXy2qR\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}}]},{"b":"my-upg-100-list","s":"./my-upg-100-list/my-upg-100-list.js","i":"sha384-X9WZKdiwnNZBM7HlwFcfeYl9uboXxcTUYdFzOxXg13jhxNYvGleuAt/AwhfDdfg+","e":["my-upg-100-list-map"],"p":[{"p":"poster-wall","e":"my-upg-100-list-map","u":"./my-upg-100-list/poster-wall/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-X9WZKdiwnNZBM7HlwFcfeYl9uboXxcTUYdFzOxXg13jhxNYvGleuAt/AwhfDdfg+\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/my-upg-100-list-map>"}},{"p":"upg-100-list","e":"my-upg-100-list-map","u":"./my-upg-100-list/upg-100-list/index.html","d":"","c":1,"em":{"iframe":"\u003ciframe 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>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-X9WZKdiwnNZBM7HlwFcfeYl9uboXxcTUYdFzOxXg13jhxNYvGleuAt/AwhfDdfg+\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/my-upg-100-list-map>"}},{"p":"upg-100-mfe","e":"my-upg-100-list-map","u":"./my-upg-100-list/upg-100-mfe/index.html","d":"","c":4,"em":{"iframe":"\u003ciframe 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>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-X9WZKdiwnNZBM7HlwFcfeYl9uboXxcTUYdFzOxXg13jhxNYvGleuAt/AwhfDdfg+\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/my-upg-100-list-map>"}}]},{"b":"people-groups-grid","s":"./people-groups-grid/people-groups-grid.js","i":"sha384-JCol/vri0hsYL5ziRSlphv7jLoyhu5ZS5Ll1gREPmX0gs4zw1jmLpdIVbxqAAMie","e":["people-groups-grid"],"p":[{"p":"people-groups-grid","e":"people-groups-grid","u":"./people-groups-grid/people-groups-grid/index.html","d":"","c":2,"em":{"iframe":"\u003ciframe src=\"./people-groups-grid/people-groups-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./people-groups-grid/people-groups-grid.js\" integrity=\"sha384-JCol/vri0hsYL5ziRSlphv7jLoyhu5ZS5Ll1gREPmX0gs4zw1jmLpdIVbxqAAMie\" crossorigin=\"anonymous\">\u003c/script>\n\u003cpeople-groups-grid profile-config='{\"profile\":\"people-groups-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/people-groups-grid>"},"in":[{"id":"people-groups-grid","t":"People Groups Grid — standalone (own search + 4 dropdowns)","a":"","pr":{"profile":"people-groups-grid","provider":"maplibre","instanceId":"people-groups-grid","pray-base-url":"https://pray.doxa.life"},"e":"people-groups-grid","u":"./people-groups-grid/people-groups-grid/people-groups-grid/index.html"},{"id":"driven","t":"People Groups Grid — externally driven (controls off, props/events)","a":"","pr":{"profile":"people-groups-grid","provider":"maplibre","instanceId":"people-groups-grid-driven","pray-base-url":"https://pray.doxa.life","show-search":"false","show-filters":"false"},"e":"people-groups-grid","u":"./people-groups-grid/people-groups-grid/driven/index.html"}]}]},{"b":"plugin-studio","s":"./plugin-studio/plugin-studio.js","i":"sha384-TgQEX+LPYUYcF1PcPli9N3VH1sNm9P+1SOv+EkbGBBYjcXYA6xYjJ4UPaB+1HCHU","e":["plugin-studio"],"p":[{"p":"plugin-studio","e":"plugin-studio","u":"./plugin-studio/plugin-studio/index.html","d":"","c":5,"em":{"iframe":"\u003ciframe src=\"./plugin-studio/plugin-studio/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./plugin-studio/plugin-studio.js\" integrity=\"sha384-TgQEX+LPYUYcF1PcPli9N3VH1sNm9P+1SOv+EkbGBBYjcXYA6xYjJ4UPaB+1HCHU\" crossorigin=\"anonymous\">\u003c/script>\n\u003cplugin-studio profile-config='{\"profile\":\"plugin-studio\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/plugin-studio>"},"in":[{"id":"studio","t":"Plugin Studio — every element by capability 5 -> 1 (developer studio)","a":"","pr":{"profile":"plugin-studio","provider":"maplibre","instanceId":"plugin-studio","title":"Plugin Studio"},"e":"plugin-studio","u":"./plugin-studio/plugin-studio/studio/index.html"}]}]},{"b":"research-dashboard","s":"./research-dashboard/research-dashboard.js","i":"sha384-9toNG/82fNOb+lT3PjrqSqfaaE8GoXmEwP+GaiToF0xT3iTkwRMVykZH4u0gN3fm","e":["research-dashboard-map"],"p":[{"p":"research-dashboard","e":"research-dashboard-map","u":"./research-dashboard/research-dashboard/index.html","d":"Coordinated-Multiple-Views research dashboard for 10/40-window UPGs (live pray.doxa.life): docked facet rail + map-as-hero + linked table/charts, brushing-and-linking over one in-memory store.","c":3,"em":{"iframe":"\u003ciframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-9toNG/82fNOb+lT3PjrqSqfaaE8GoXmEwP+GaiToF0xT3iTkwRMVykZH4u0gN3fm\" crossorigin=\"anonymous\">\u003c/script>\n\u003cresearch-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/research-dashboard-map>"}}]},{"b":"stack-demo","s":"./stack-demo/stack-demo.js","i":"sha384-OERfDCY+oHWG9Z0VYc1UMO5LGVSyKMJj6W0C+yAcb78PIJP81pkwK3HRcC0hoTkq","e":["stack-demo"],"p":[{"p":"stack-demo","e":"stack-demo","u":"./stack-demo/stack-demo/index.html","d":"Groups TWO independent elements TOP-TO-BOTTOM so you can embed the pair on a page and","c":4,"em":{"iframe":"\u003ciframe src=\"./stack-demo/stack-demo/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./stack-demo/stack-demo.js\" integrity=\"sha384-OERfDCY+oHWG9Z0VYc1UMO5LGVSyKMJj6W0C+yAcb78PIJP81pkwK3HRcC0hoTkq\" crossorigin=\"anonymous\">\u003c/script>\n\u003cstack-demo profile-config='{\"profile\":\"stack-demo\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/stack-demo>"},"in":[{"id":"pair","t":"pair","a":"","pr":{"profile":"stack-demo"},"e":"stack-demo","u":"./stack-demo/stack-demo/pair/index.html"}]}]},{"b":"template-bundle","s":"./template-bundle/template-bundle.js","i":"sha384-x0d82IF+B56DBn+C1LWBjImIwRcsQplBMHQmwZaj/ZJL6KT4kpnU1ykOA59k9Ic4","e":["template-bundle"],"p":[{"p":"template-map-a","e":"template-bundle","u":"./template-bundle/template-map-a/index.html","d":"Minimal example profile A — a colored placeholder, no Mapbox or data.","c":1,"em":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-a/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-x0d82IF+B56DBn+C1LWBjImIwRcsQplBMHQmwZaj/ZJL6KT4kpnU1ykOA59k9Ic4\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/template-bundle>"}},{"p":"template-map-b","e":"template-bundle","u":"./template-bundle/template-map-b/index.html","d":"Minimal example profile B — a second profile in the same bundle.","c":1,"em":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-b/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-x0d82IF+B56DBn+C1LWBjImIwRcsQplBMHQmwZaj/ZJL6KT4kpnU1ykOA59k9Ic4\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/template-bundle>"}}]}];
var snip = function snip(b, p, x) {
var u = (x || p).u, tag = (x && x.e) || p.e || b.e[0] || b.b
var r = { iframe: '<iframe src="' + u + '?utm_source=YOUR_SOURCE" width="100%" height="600" style="border:none;border-radius:12px;" allowfullscreen></iframe>' }
if (!x) r.script = '<script src="' + b.s + '"' + (b.i ? ' integrity="' + b.i + '" crossorigin="anonymous"' : '') + '><\/script>\n<' + tag + " profile-config='" + JSON.stringify({ profile: p.p, tk: 'YOUR_MAPBOX_TOKEN', utmSource: 'YOUR_SOURCE' }) + "'></" + tag + '>'
return r
};
var compact = function compact(m, lean, tierMap) {
tierMap = tierMap || {}
// Composition tier → capability number, self-contained so the browser copy of this function
// (shipped via src(compact) and re-run on the live manifest) needs no import. 5 = app-shell
// (nested-mfe-spa) … 1 = single. The model may bake `sp.type`; else tierMap resolves it.
var PT = { 'nested-mfe-spa': 0, 'nested-elements-mfe': 1, 'monolithic': 2, 'parameterized': 3, 'single': 4 }
var capFor = function (t) { return PT[t] != null ? 5 - PT[t] : 1 }
return (m.bundles || []).map(function (src) {
var b = { b: src.bundle, s: src.scriptUrl || '', i: src.integrity || '', e: src.elements || [], p: [] }
b.p = (src.profiles || []).map(function (sp) {
var type = sp.type || tierMap[src.bundle + '/' + sp.profile] || 'single'
var c = capFor(type)
var p = { p: sp.profile, e: sp.element || '', u: sp.iframeUrl || '', d: sp.description || '', c: c }
var em = sp.embed || {}, d = snip(b, p, null)
if (!lean || em.iframe !== d.iframe || em.script !== d.script) p.em = em
var ins = (sp.instances || []).map(function (sx) {
var x = { id: sx.id, t: sx.title || sx.id, a: sx.apiBaseUrl || '', pr: sx.props || {}, e: sx.element || '', u: sx.iframeUrl || '' }
var xe = sx.embed || {}, xd = snip(b, p, x)
if (!lean || xe.iframe !== xd.iframe) x.em = xe
return x
})
if (ins.length) p.in = ins
return p
})
return b
})
};
// Peer engines, the same tags every leaf page carries; loaded once, before the bundle.
var PEERS = {
mapbox: { css: ['https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css', 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css'],
js: ['https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js', 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js'] },
maplibre: { css: ['https://cdn.jsdelivr.net/npm/maplibre-gl@5/dist/maplibre-gl.css'], js: ['https://cdn.jsdelivr.net/npm/maplibre-gl@5/dist/maplibre-gl.js'] }
};
var $ = function (id) { return document.getElementById(id) };
// The map custom elements mount imperatively into this slot (they own their own DOM and must
// not be re-rendered by Vue); the Vue app writes its ref here after mount. Falls back to the
// element id so the loader works even before the app boots.
var slotEl = function () { return (window.__spa && window.__spa.slot) || $('slot') };
var TIER_MAP = {"all-people-groups/all-people-groups":"single","big-picture/big-picture":"nested-elements-mfe","cities-internet/connectivity-list":"single","cities-internet/connectivity-map":"single","cities-internet/data-tables":"single","country-posters/countries-map":"single","country-posters/country-poster-wall":"single","dashboard-plus-grid/dashboard-plus-grid":"nested-elements-mfe","demo-map/demo-map":"parameterized","doxa-maps-spa/doxa-maps-spa":"nested-mfe-spa","doxa-research-map/research-map":"monolithic","doxa-simple-map/doxa-simple-map":"parameterized","doxa-vision-card/doxa-vision-card":"parameterized","gospel-resources/charts":"single","gospel-resources/data-tables":"single","gospel-resources/resources-list":"single","gospel-resources/resources-map":"single","key-cities/cities-list":"single","key-cities/cities-map":"single","key-cities/city-poster-wall":"single","key-cities/data-tables":"single","my-upg-100-list/poster-wall":"single","my-upg-100-list/upg-100-list":"single","my-upg-100-list/upg-100-mfe":"nested-elements-mfe","people-groups-grid/people-groups-grid":"parameterized","plugin-studio/plugin-studio":"nested-mfe-spa","research-dashboard/research-dashboard":"monolithic","stack-demo/stack-demo":"nested-elements-mfe","template-bundle/template-map-a":"single","template-bundle/template-map-b":"single"}; // "<bundle>/<profile>" → composition tier, for live re-compact
// THIS PAGE'S OWN DIRECTORY. A host may serve the folder at /…/doxa-maps-build (no trailing
// slash); the browser would then resolve every relative ref against the PARENT directory and
// drop the folder name, so bundles 404. Derive the directory explicitly and build refs from it.
var BASE = (function () {
var p = location.pathname || '/';
return /\.[a-z0-9]+$/i.test(p) ? p.replace(/[^/]*$/, '') : p.replace(/\/*$/, '') + '/';
})();
var ASSETS = ASSETS_BASE || new URL(BASE + 'assets', location.origin).href.replace(/\/+$/, '');
var REG = BAKED, active = null, seq = 0, loaded = {}, fetchedToken = '';
var narrow = function () { return window.matchMedia && window.matchMedia('(max-width:700px)').matches };
// Manifest refs are relative to the tree root; this page sits one level above it. Refs are
// anchored to BASE (above) so they resolve the same with or without a trailing slash.
function rel(u) { return /^([a-z]+:|\/)/i.test(u) ? u : BASE + TREE + '/' + u.replace(/^\.\//, '') }
function qp(name) { try { return new URLSearchParams(location.search).get(name) || '' } catch (e) { return '' } }
function hint(msg, err) { var slot = slotEl(); var d = document.createElement('div'); d.className = 'hint' + (err ? ' err' : ''); d.textContent = msg; slot.innerHTML = ''; slot.appendChild(d); return d }
function assign(t) { for (var i = 1; i < arguments.length; i++) { var s = arguments[i]; for (var k in s) if (Object.prototype.hasOwnProperty.call(s, k)) t[k] = s[k] } return t }
function once(url, kind, attrs) {
if (loaded[url]) return loaded[url];
loaded[url] = new Promise(function (ok, fail) {
var el = document.createElement(kind === 'css' ? 'link' : 'script');
if (kind === 'css') { el.rel = 'stylesheet'; el.href = url } else { el.src = url; assign(el, attrs || {}) }
el.onload = function () { ok() }; el.onerror = function () { delete loaded[url]; fail(new Error('failed to load ' + url)) };
document.head.appendChild(el);
});
return loaded[url];
}
function peer(provider) {
var p = PEERS[provider] || PEERS.mapbox, chain = Promise.resolve();
p.css.forEach(function (u) { once(u, 'css') });
p.js.forEach(function (u) { chain = chain.then(function () { return once(u, 'js') }) });
return chain;
}
function bundle(b) { return once(rel(b.s), 'js', b.i ? { integrity: b.i, crossOrigin: 'anonymous' } : {}) }
// Resolve a deep-link key ("<bundle>/<profile>[/<instance>]") against the live registry.
function find(parts) {
for (var i = 0; i < REG.length; i++) if (REG[i].b === parts[0]) {
var b = REG[i];
for (var j = 0; j < b.p.length; j++) if (b.p[j].p === parts[1]) {
var p = b.p[j];
if (parts.length < 3) return { b: b, p: p, x: null };
for (var k = 0; k < (p.in || []).length; k++) if (p.in[k].id === parts[2]) return { b: b, p: p, x: p.in[k] };
}
}
return null;
}
// Same profile-config the leaf page bakes (profile page: profile + dataSource + instanceId;
// instance page: its props), plus the host-supplied share URL of that leaf page.
function mount(b, p, x, tag) {
var cfg = x ? assign({ assetsBase: ASSETS }, x.pr) : { profile: p.p, dataSource: 'pray-tools', instanceId: b.b + '-' + p.p, assetsBase: ASSETS };
if (QA) cfg.qa = true;
cfg.shareUrl = new URL(rel((x || p).u), location.href).href;
window.MAP_APP_API_URL = (x && x.a) || API_BASE;
var provider = cfg.provider === 'maplibre' ? 'maplibre' : 'mapbox', my = ++seq;
hint(provider === 'maplibre' ? 'loading map…' : 'loading token…');
peer(provider).then(function () { return bundle(b) }).then(function () {
if (my !== seq) return;
resolveToken(cfg, provider === 'maplibre', function (token) {
if (my !== seq) return;
try {
var c = token ? assign({}, cfg, { tk: token }) : cfg;
var el = document.createElement(tag); el.setAttribute('profile-config', JSON.stringify(c));
var slot = slotEl(); slot.innerHTML = ''; slot.appendChild(el);
} catch (e) { hint(e && e.message ? e.message : String(e), true) }
});
}).catch(function (e) { if (my === seq) hint('Failed to load ' + b.b + ': ' + (e && e.message ? e.message : e), true) });
}
// Mapbox token resolution — the leaf pages' ladder, in precedence order:
// 1. prop (config.tk) 2. ?tk= on this page's URL (stored once for every page of the tree)
// 3. baked token — then, none found, a token ENDPOINT is fetched, first answer wins:
// 4. window.MAP_TOKEN_URL 5. configured TOKEN_SRC 6. same-origin /api/maps/token — else ask once.
function resolveToken(cfg, tokenFree, done) {
if (tokenFree) return done('');
var stored = ''; try { stored = localStorage.getItem('MAPS_STAGING_TK') || '' } catch (e) {}
var urlTk = qp('tk'); if (urlTk) { try { localStorage.setItem('MAPS_STAGING_TK', urlTk) } catch (e) {} }
var directToken = cfg.tk || urlTk || stored || BAKED_TOKEN || fetchedToken;
if (directToken) return done(directToken);
function askToken(prefix) {
var d = hint('', true);
d.innerHTML = (prefix ? '<div></div>' : '') + '<div>map needs a Mapbox token — <input id="tkIn" type="password" placeholder="paste pk.… (stays in this browser)"> <button id="tkGo" type="button">use it</button></div>';
if (prefix) d.firstChild.textContent = prefix;
$('tkGo').onclick = function () { var v = ($('tkIn').value || '').trim(); if (!v) return; try { localStorage.setItem('MAPS_STAGING_TK', v) } catch (e) {} done(v) };
}
var candidates = [];
function addCandidate(u) { if (u && candidates.indexOf(u) === -1) candidates.push(u) }
addCandidate(window.MAP_TOKEN_URL);
addCandidate(TOKEN_SRC && TOKEN_SRC !== 'window.MAP_TOKEN_URL' ? TOKEN_SRC : '');
addCandidate(DEFAULT_TOKEN_URL);
function extractToken(data) { if (typeof data === 'string') return data.trim(); if (data && typeof data === 'object') return data.token || data.tk || data.accessToken || ''; return '' }
function fetchToken(i, errors) {
if (i >= candidates.length) return askToken('Token fetch failed from ' + candidates.join(', ') + ' — set window.MAP_TOKEN_URL or pass ?tk=. (' + errors.join(' | ') + ')');
var url = candidates[i];
return fetch(url).then(function (r) {
if (!r.ok) throw new Error(url + ' → HTTP ' + r.status);
return (r.headers.get('content-type') || '').indexOf('json') !== -1 ? r.json() : r.text();
}).then(function (data) {
var tk = extractToken(data); if (!tk) throw new Error(url + ' → no token in response');
fetchedToken = tk; done(tk);
}).catch(function (e) { errors.push(e && e.message ? e.message : String(e)); return fetchToken(i + 1, errors) });
}
fetchToken(0, []);
}
function copyText(text, btn) {
var ok = function () { var t = btn.textContent; btn.textContent = 'Copied'; setTimeout(function () { btn.textContent = t }, 1200) };
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(ok, function () {}); else {
try { var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); ok() } catch (e) {}
}
}
// ── The capability ladder for the sidebar headings (5 → 1), reader-facing names ──
var CAP_LABEL = { 5: 'App shell · nested-mfe-spa', 4: 'Composite element', 3: 'Multi-view element', 2: 'Preset element', 1: 'Single element' };
// REG → tab groups keyed by capability, most-composed first (5 → 1). Each group lists every
// profile (and its preset instances) as one row; a bundle contributes rows to the tier of each
// profile it holds, so one bundle can appear under several capability headings.
function toGroups() {
var byCap = {};
REG.forEach(function (b) {
(b.p || []).forEach(function (p) {
var cap = p.c || 1, tag = p.e || b.e[0] || b.b;
(byCap[cap] = byCap[cap] || []).push({ key: b.b + '/' + p.p, kind: 'profile', label: p.p, tag: tag, bundle: b.b });
(p.in || []).forEach(function (x) {
byCap[cap].push({ key: b.b + '/' + p.p + '/' + x.id, kind: 'instance', label: x.t, tag: x.e || tag, bundle: b.b });
});
});
});
return [5, 4, 3, 2, 1].filter(function (c) { return byCap[c] && byCap[c].length }).map(function (c) {
return { cap: c, label: CAP_LABEL[c] || ('capability ' + c), rows: byCap[c] };
});
}
function bootVue(Vue) {
var app = Vue.createApp({
data: function () {
return {
groups: toGroups(), activeKey: '', navOpen: true, embedOpen: false,
ttl: 'Choose a map', sub: '', openHref: '', hasSel: false, embedIframe: '', embedScript: ''
};
},
computed: {
countLabel: function () {
var nb = REG.length, np = 0, ni = 0;
REG.forEach(function (b) { np += (b.p || []).length; (b.p || []).forEach(function (p) { ni += (p.in || []).length }) });
return nb + ' bundles · ' + np + ' profiles · ' + ni + ' instances';
}
},
methods: {
go: function (key) { location.hash = '#/' + key.split('/').map(encodeURIComponent).join('/') },
copyText: copyText,
rebuild: function (m) { if (m) { try { REG = compact(m, false, TIER_MAP) } catch (e) { REG = BAKED } } this.groups = toGroups(); this.route(); },
route: function () { var h = (location.hash || '').replace(/^#\/?/, ''); if (h) this.select(h); },
select: function (key) {
var parts = key.split('/').map(function (s) { try { return decodeURIComponent(s) } catch (e) { return s } });
var hit = find(parts); this.activeKey = parts.join('/');
if (!hit) { this.ttl = 'No such map'; this.sub = key; this.openHref = ''; this.hasSel = false; this.embedOpen = false; hint('No such map: ' + key, true); return; }
var b = hit.b, p = hit.p, x = hit.x, tag = (x && x.e) || p.e || b.e[0] || b.b;
this.ttl = x ? x.t : p.p; this.sub = b.b + ' · <' + tag + '>' + (p.d ? ' · ' + p.d : '');
this.openHref = rel((x || p).u); this.hasSel = true; this.embedOpen = false;
var em = x ? (x.em || snip(b, p, x)) : (p.em || snip(b, p, null));
this.embedIframe = em.iframe || ''; this.embedScript = em.script || '';
if (narrow()) this.navOpen = false;
mount(b, p, x, tag);
}
},
mounted: function () {
var self = this;
window.__spa = { slot: this.$refs.slot, app: this };
window.addEventListener('hashchange', function () { self.route() });
try { window.matchMedia('(max-width:700px)').addEventListener('change', function (e) { if (!e.matches) self.navOpen = true }) } catch (e) {}
// Live manifest first (the page stays right when bundles change); baked copy is the fallback.
if (typeof fetch === 'function' && location.protocol !== 'file:') {
fetch(TREE + '/manifest.json', { cache: 'no-cache' }).then(function (r) { return r.ok ? r.json() : null })
.then(function (m) { self.rebuild(m) }, function () { self.route() });
} else { self.route(); }
}
});
app.config.compilerOptions.isCustomElement = function () { return true }; // map tags are custom elements
app.mount('#app');
}
// Vue drives the shell (the nested-mfe-spa). Self-hosted first (portable / offline / no-CDN),
// then a CDN fallback; if neither loads, degrade to a plain rendered list so the page never
// blanks. once() resolves the tree-relative ASSETS path the same way every leaf page does.
function loadVue() {
if (window.Vue) return Promise.resolve(window.Vue);
var local = ASSETS + '/vendor/vue/vue.global.prod.js';
var cdn = 'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js';
return once(local, 'js').then(function () { return window.Vue }, function () {
return once(cdn, 'js').then(function () { return window.Vue });
});
}
loadVue().then(function (Vue) {
if (Vue) bootVue(Vue);
else fallbackRender();
}).catch(function () { fallbackRender(); });
// No-Vue degrade: render the same capability-sorted list with plain DOM so a map is still
// reachable (parse-safe, dependency-free). Rare — Vue is vendored in the tree.
function fallbackRender() {
var host = $('app'); if (!host) return;
function paint() {
var nav = host.querySelector('#nav'); if (!nav) return;
nav.innerHTML = '';
toGroups().forEach(function (g) {
var h = document.createElement('div'); h.className = 'tier'; h.textContent = 'CAP ' + g.cap + ' · ' + g.label; nav.appendChild(h);
g.rows.forEach(function (r) {
var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'row' + (r.kind === 'instance' ? ' in' : '');
btn.textContent = r.label; btn.onclick = function () { location.hash = '#/' + r.key.split('/').map(encodeURIComponent).join('/') };
nav.appendChild(btn);
});
});
}
function route() {
var h = (location.hash || '').replace(/^#\/?/, ''); if (!h) return;
var parts = h.split('/').map(function (s) { try { return decodeURIComponent(s) } catch (e) { return s } });
var hit = find(parts); if (hit) mount(hit.b, hit.p, hit.x, (hit.x && hit.x.e) || hit.p.e || hit.b.e[0] || hit.b.b); else hint('No such map: ' + h, true);
}
window.__spa = { slot: $('slot') };
if (typeof fetch === 'function' && location.protocol !== 'file:') {
fetch(TREE + '/manifest.json', { cache: 'no-cache' }).then(function (r) { return r.ok ? r.json() : null })
.then(function (m) { if (m) { try { REG = compact(m, false, TIER_MAP) } catch (e) { REG = BAKED } } paint(); route(); }, function () { paint(); route(); });
} else { paint(); route(); }
window.addEventListener('hashchange', route);
}
})();
</script>
</body>
</html>