Merge main into bundler-1/work (regenerate the tree, re-hash)
Some checks are pending
build-gate / build (push) Waiting to run

Six conflicts, all in generated artifacts under public/, including this
bundle's own — another builder's tree regeneration had rebuilt it.
Regenerated rather than hand-merged: the three PosterWall consumers
rebuilt, then the tree and the hashes.

PosterWall.vue itself auto-merged with both sides intact — this
branch's embedded/sectionHeads props and main's usePosterPrints
refactor — and the four suites that exercise it through the regions
poster dropdown were re-run green after the merge, since their change
lands inside the component this feature renders.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 05:33:32 +00:00
commit ba6b9229f3
67 changed files with 1734 additions and 642 deletions

View file

@ -143,6 +143,8 @@ and a change here can break other maps.
`import … from '@map/composables/useMapSelection.js'`
- **useMapTheme** — useMapTheme.js — shared light/dark theme for every map profile.
`import … from '@map/composables/useMapTheme.js'`
- **usePosterPrints** — usePosterPrints.js — THE print actions every poster wall shares.
`import … from '@map/composables/usePosterPrints.js'`
- **usePplrInstance** — usePplrInstance.js — per-map "instance store" mediator.
`import … from '@map/composables/usePplrInstance.js'`
- **usePrayedLocally** — usePrayedLocally.js — the "my dot glows" encouragement loop.

View file

@ -0,0 +1,99 @@
// check-deck-click — click the REAL Card Deck button and read what it prints.
//
// Every other packet/card gate drives the RENDERERS directly. This one drives the
// UI: it finds the button in the shadow root, clicks it, and captures the document
// that reaches the print frame. That is the only path that proves the wiring —
// records provider, stock picker, language, shared composable, print frame — is
// connected end to end. A renderer can be perfect while the button that calls it
// passes the wrong stock, or nothing at all.
//
// ⚠ IT INTERCEPTS THE PRINTED HTML rather than reading the iframe back.
// openPrintFrame calls window.print(), which BLOCKS in headless Chromium, and the
// frame self-removes on afterprint — reading it back measures a frame that has
// already gone and reports 0 cards for a correct deck.
//
// Usage: node internal/check-deck-click.mjs <wallUrl> [stockId] [expectedPage]
import { chromium } from 'playwright'
const URL_ = process.argv[2]
if (!URL_) { console.error('usage: check-deck-click.mjs <wallUrl> [stock] [expectedPage]'); process.exit(2) }
const STOCK = process.argv[3] || ''
const EXPECT_PAGE = process.argv[4] || (STOCK === 'pokerLandscape' ? '3.5in 2.5in' : '2.5in 3.5in')
const b = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] })
const pg = await b.newPage({ viewport: { width: 1280, height: 1000 } })
const errs = []
pg.on('pageerror', (e) => errs.push(String(e).slice(0, 140)))
await pg.goto(URL_, { waitUntil: 'load', timeout: 45000 })
await pg.waitForTimeout(9000)
await pg.evaluate(() => {
window.__printed = ''
const real = document.createElement.bind(document)
document.createElement = (t) => {
const el = real(t)
if (t === 'iframe') {
Object.defineProperty(el, 'contentDocument', {
get: () => ({ open() {}, close() {}, write(h) { window.__printed += h } }),
})
Object.defineProperty(el, 'contentWindow', {
get: () => ({ addEventListener() {}, focus() {}, print() {} }),
})
}
return el
}
})
const r = await pg.evaluate(async (stock) => {
const host = [...document.querySelectorAll('*')]
.find((e) => e.shadowRoot && e.shadowRoot.querySelector('.pbtn'))
if (!host) return { error: 'no poster wall found on this page' }
const sr = host.shadowRoot
const btns = [...sr.querySelectorAll('.pbtn')]
const deck = btns.find((x) => /card deck/i.test(x.textContent))
if (!deck) return { error: 'no Card Deck button', buttons: btns.map((x) => x.textContent.trim()) }
if (stock) {
const sel = sr.querySelector('.pstock')
if (!sel) return { error: 'no card-stock picker to drive' }
sel.value = stock
sel.dispatchEvent(new Event('change', { bubbles: true }))
await new Promise((res) => setTimeout(res, 400))
}
deck.click()
await new Promise((res) => setTimeout(res, 9000))
const doc = new DOMParser().parseFromString(window.__printed, 'text/html')
return {
cards: doc.querySelectorAll('.card').length,
qrs: doc.querySelectorAll('.qr svg').length,
imgs: doc.querySelectorAll('img.ph').length,
page: (window.__printed.match(/@page\{size:([^;}]*)/) || [])[1] || '',
}
}, STOCK)
await b.close()
if (r.error) {
console.error(`DECK-CLICK: FAIL — ${r.error}`
+ (r.buttons ? ` (buttons present: ${r.buttons.join(' | ')})` : ''))
process.exit(1)
}
const fails = []
if (!r.cards) fails.push('the click printed no cards at all — the button is not wired to the renderer')
if (r.page.trim() !== EXPECT_PAGE) {
fails.push(`@page size is "${r.page.trim()}", expected "${EXPECT_PAGE}"`
+ (STOCK ? ' — the stock picker is not reaching the renderer' : ''))
}
if (r.qrs !== r.cards) fails.push(`${r.qrs} QR code(s) for ${r.cards} cards`)
if (errs.length) fails.push(`${errs.length} page error(s): ${errs[0]}`)
console.log(` stock : ${STOCK || '(default)'}`)
console.log(` cards printed : ${r.cards} (${r.imgs} portraits, ${r.qrs} QR codes)`)
console.log(` @page size : ${r.page.trim()} (want ${EXPECT_PAGE})`)
console.log(` page errors : ${errs.length}`)
if (fails.length) {
console.error(`\nDECK-CLICK: FAIL — ${fails.length} problem(s)`)
for (const f of fails) console.error(' - ' + f)
process.exit(1)
}
console.log(`\nDECK-CLICK: PASS — the real button printed ${r.cards} cards at ${r.page.trim()}.`)

View file

@ -0,0 +1,172 @@
// check-poster-prints — the SHARED print actions must produce real documents.
//
// Context: the poster wall was generalised into library/components/poster/
// PosterWall.vue and two bundles migrated onto it, but the ORIGINAL 903-line
// my-upg-100-list wall — the one carrying the actual print actions — was left
// behind. So the shared component's packet path was a "coming soon" stub, and
// every print fix in the 100-list was invisible to the three bundles using the
// shared wall. usePosterPrints.js closes that: the ACTIONS are shared, the RECORD
// FETCHING stays per-bundle behind a `records` provider.
//
// ★ THE SEAM IS THE THING TO ASSERT. A wall WITH a provider must produce a real
// document; a wall WITHOUT one must degrade honestly rather than failing at click
// time. Both directions are checked, because "the button is there" and "the button
// works" are different claims — this flight has repeatedly found the first true
// and the second false.
//
// Runs the composable in a browser page (it calls document/DOM), driving it with a
// fake records provider so the assertion is about the DOCUMENTS, not about any
// bundle's API being reachable.
import { chromium } from 'playwright'
import { readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const REPO = process.env.DOXA_REPO
// Third instance of one pattern this flight (after check-zone-census and
// check-share-policy): a gate authored in one worktree hardcodes THAT worktree as its
// fallback, and check:no-abs-paths — which is inside build:unlocked — turns red for every
// 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)), '..')
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
// URL there is no directory to resolve '../packet/...' against, so those imports
// fail with "Failed to resolve module specifier" — a harness limitation, not a
// product bug (Vite resolves them, and the built bundle inlines both renderers).
// Read them here and substitute blob URLs so the gate exercises the REAL code.
const packetRender = readFileSync(resolve(REPO, 'library/packet/packet-render.js'), 'utf8')
const cardRender = readFileSync(resolve(REPO, 'library/packet/card-render.js'), 'utf8')
const qr = readFileSync(resolve(REPO, 'library/packet/qr.js'), 'utf8')
const strings = readFileSync(resolve(REPO, 'library/scripts/posters/packet-strings.json'), 'utf8')
const b = await chromium.launch()
const pg = await b.newPage({ viewport: { width: 1000, height: 800 } })
const errs = []
pg.on('pageerror', (e) => errs.push(String(e).slice(0, 140)))
await pg.setContent('<!DOCTYPE html><html><body></body></html>')
const r = await pg.evaluate(async ({ src, frame, packetRender, cardRender, qr, strings }) => {
// Shim the two module deps so the composable can be evaluated standalone: a tiny
// `ref`, and printInPlace's real source so the frame mechanism is the real one.
const frameUrl = URL.createObjectURL(new Blob([frame], { type: 'text/javascript' }))
const vueShim = URL.createObjectURL(new Blob([
'export const ref = (v) => ({ value: v })',
], { type: 'text/javascript' }))
const blob = (code) => URL.createObjectURL(new Blob([code], { type: 'text/javascript' }))
const qrUrl = blob(qr)
const packetUrl = blob(packetRender.replace("from './qr.js'", `from '${qrUrl}'`))
const cardUrl = blob(cardRender.replace("from './qr.js'", `from '${qrUrl}'`))
const stringsUrl = URL.createObjectURL(new Blob([`export default ${strings}`], { type: 'text/javascript' }))
const patched = src
.replace("from 'vue'", `from '${vueShim}'`)
.replace("from './printInPlace.js'", `from '${frameUrl}'`)
.replace("'../packet/packet-render.js'", `'${packetUrl}'`)
.replace("'../packet/card-render.js'", `'${cardUrl}'`)
.replace(/'\.\.\/scripts\/posters\/packet-strings\.json'/g, `'${stringsUrl}'`)
const modUrl = URL.createObjectURL(new Blob([patched], { type: 'text/javascript' }))
const { usePosterPrints } = await import(modUrl)
const ROWS = [
{ slug: 'a', name: 'Aheu', country_code_label: 'Laos', population: 2700, religion_label: 'Animism' },
{ slug: 'b', name: 'Akha', country_code_label: 'Laos', population: 113000, religion_label: 'Animism' },
]
const out = {}
// ── with a provider ──────────────────────────────────────────────────────
// ⚠ CAPTURE THE HTML, do not read the frame back. openPrintFrame calls
// window.print() under autoprint, which BLOCKS in headless Chromium, and the
// frame self-removes on afterprint — so reading it back measured a frame that had
// already gone and reported 0 rows for a correct document. Intercept the document
// the composable hands to the frame instead: that IS what gets printed.
let printed = ''
const realCreate = document.createElement.bind(document)
document.createElement = (tag) => {
const el = realCreate(tag)
if (tag === 'iframe') {
Object.defineProperty(el, 'contentDocument', {
get: () => ({ open() {}, close() {}, write(h) { printed += h } }),
})
Object.defineProperty(el, 'contentWindow', { get: () => ({ addEventListener() {} }) })
}
return el
}
const withProv = usePosterPrints({ records: async () => ROWS, label: (i) => i.label })
await withProv.printList({ id: 'laos', label: 'Laos' })
await new Promise((r2) => setTimeout(r2, 400))
document.createElement = realCreate
const doc = new DOMParser().parseFromString(printed, 'text/html')
out.listRows = doc ? doc.querySelectorAll('tbody tr').length : 0
out.listHasCheckbox = doc ? /□/.test(doc.body.innerText) : false
out.listPhase = withProv.exportState.value?.phase ?? 'cleared'
// ── the PACKET action: same provider, the full booklet ───────────────────
// Migrated from upg-100-list.vue, where it was a second hand-maintained copy of
// the same renderer call. Asserted here so the shared version cannot regress
// silently — the packet is the product's core artefact.
const PKT = [
{ slug: 'a', name: 'Aheu', country_code_label: 'Laos', population: 2700, image_url: 'x.jpg' },
{ slug: 'b', name: 'Akha', country_code_label: 'Laos', population: 113000, image_url: 'y.jpg' },
]
printed = ''
document.createElement = (tag) => {
const el = realCreate(tag)
if (tag === 'iframe') {
Object.defineProperty(el, 'contentDocument', {
get: () => ({ open() {}, close() {}, write(h) { printed += h } }),
})
Object.defineProperty(el, 'contentWindow', { get: () => ({ addEventListener() {} }) })
}
return el
}
const pk = usePosterPrints({ records: async () => PKT, label: (i) => i.label })
await pk.printPacket({ id: 'laos', label: 'Laos' })
await new Promise((r2) => setTimeout(r2, 600))
document.createElement = realCreate
const pdoc = new DOMParser().parseFromString(printed, 'text/html')
out.packetSheets = pdoc.querySelectorAll('.sheet').length
out.packetCards = pdoc.querySelectorAll('.card').length
out.packetWheel = pdoc.querySelectorAll('svg path[fill^="hsl"]').length
out.packetMark5 = /Gadarenes/.test(pdoc.body.innerText)
// ── without a provider: must FAIL HONESTLY, not throw ────────────────────
const noProv = usePosterPrints({ label: (i) => i.label })
await noProv.printList({ id: 'x', label: 'X' })
out.noProvPhase = noProv.exportState.value?.phase
out.noProvMsg = noProv.exportState.value?.message || ''
return out
}, { src, frame, packetRender, cardRender, qr, strings })
await b.close()
const fails = []
if (r.listRows !== 2) fails.push(`the list print rendered ${r.listRows} row(s), expected 2`)
if (!r.listHasCheckbox) fails.push('the list print has no prayer checkbox column')
if (r.noProvPhase !== 'error') {
fails.push(`without a records provider the phase was "${r.noProvPhase}", expected "error" — `
+ 'it must degrade honestly rather than silently doing nothing')
}
if (!/no records provider/i.test(r.noProvMsg)) {
fails.push(`the no-provider message was "${r.noProvMsg}" — it should name the missing provider`)
}
// The packet is the product's core artefact: map page + people pages + points +
// wheel. Assert its SHAPE, not merely that bytes were produced. With 2 groups
// that is 1 map + 1 people page + points + wheel = 4 sheets.
if (r.packetSheets !== 4) fails.push(`the packet printed ${r.packetSheets} sheets, expected 4`)
if (r.packetCards !== 2) fails.push(`the packet printed ${r.packetCards} people cards, expected 2`)
if (r.packetWheel !== 12) fails.push(`the prayer wheel has ${r.packetWheel} segments, expected 12`)
if (!r.packetMark5) fails.push('the packet is missing Mark 5:1-20 in full')
if (errs.length) fails.push(`${errs.length} page error(s): ${errs[0]}`)
console.log(` list print : ${r.listRows} rows, checkbox=${r.listHasCheckbox}`)
console.log(` without provider : phase=${r.noProvPhase} "${r.noProvMsg.slice(0, 60)}"`)
console.log(` packet : ${r.packetSheets} sheets, ${r.packetCards} cards, `
+ `${r.packetWheel} wheel segments, Mark5=${r.packetMark5}`)
console.log(` page errors : ${errs.length}`)
if (fails.length) {
console.error(`\nPOSTER-PRINTS: FAIL — ${fails.length} problem(s)`)
for (const f of fails) console.error(' - ' + f)
process.exit(1)
}
console.log('\nPOSTER-PRINTS: PASS — shared print actions produce real documents, and degrade honestly without a provider.')

View file

@ -45,7 +45,7 @@ const DESIGNS = join(REPO, 'plugin-designs')
const POLICY = {
'doxa-simple-map': 'own-button', // Prayer Progress — the embed flagship
'doxa-research-map': 'own-button',
'country-posters': 'own-button',
'country-posters': 'header', // country-posters-mfe header owns it
'cities-internet': 'own-button',
'gospel-resources': 'own-button',
'demo-map': 'own-button',

View file

@ -41,6 +41,8 @@
*/
import { computed, ref } from 'vue'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { usePosterPrints } from '@map/composables/usePosterPrints.js'
import { useI18n } from 'vue-i18n'
// Export-modal geometry. A fixed;inset:0 scrim covers only the CONTENT PANE in the cap-5 shell
// (contain:layout on .vtl-content). Measured 1024x793 @ (256,107) at 1280x900 — rail clickable
// behind the "modal". See library/utils/viewportScrim.js for the measurements and rejected fixes.
@ -77,6 +79,17 @@ const props = defineProps({
// prayer list for each country generated.")
prayerListSoonMessage: { type: String,
default: 'Coming soon — the prayer list for each people group is in build.' },
// ★ RECORDS PROVIDER — the seam that makes the print actions shareable.
// The PRINT ACTIONS are common to every wall; the RECORD FETCHING is not. The
// 100-list resolves its rows through the people-groups API, the selector
// evaluator and a pg_slugs special case; Key Cities and Country Posters have
// entirely different data shapes. So a bundle that CAN supply rows passes this
// and gets the list + card-deck prints; one that cannot simply omits it and
// those buttons stay hidden, rather than failing at click time.
records: { type: Function, default: null },
// Card stock for the deck print, when this wall offers one. See card-render.js
// STOCK — both orientations are available.
cardStock: { type: String, default: 'poker' },
// EMBEDDED mode (added 2026-09-24 for the regions-tab poster dropdown). The wall's root is
// `position:absolute;inset:0` because in country-posters and my-upg-100-list it IS a pane and
// fills it. Dropped inside another component — a region card, say — that absolute root covers
@ -208,11 +221,43 @@ async function exportPoster(item) {
}
}
// ── SHARED PRINT ACTIONS ──────────────────────────────────────────────────
// The list and card-deck prints live in the shared composable so every wall gets
// the same documents. They need ROWS, which only the bundle can fetch — hence the
// `records` prop. Without it the buttons are hidden and this stays honest rather
// than offering an action that cannot work.
// The wall follows the app's locale: the card deck and its QR codes are localised,
// and packet-strings.json falls back per key.
const { locale: wallLocale } = useI18n()
// ⚠ ONE exportState, not two. The composable owns a ref of its own; if the HUD
// keeps rendering the wall's while the print actions write to the composable's, a
// card-deck print shows NO progress and its errors vanish silently — the mechanism
// works and the feedback does not. Hand the composable this wall's ref so both
// write to the surface the reader is actually looking at.
const prints = usePosterPrints({
state: exportState,
records: props.records ? (item) => props.records(item) : null,
// useI18n()'s locale can be a ref or a plain string depending on how the host
// installed it, so normalise rather than assume.
lang: () => {
const l = wallLocale && (wallLocale.value ?? wallLocale)
return (typeof l === 'string' && l) || 'en'
},
stock: () => props.cardStock,
label: (item) => item?.label || item?.id || '',
})
const canPrintRows = computed(() => typeof props.records === 'function')
function downloadPacket(item) {
// NOT an error — unbuilt, not broken. phase:'error' reads to a user as
// "something went wrong" rather than "not yet".
exportState.value = { label: item.label, kind: 'Prayer List', phase: 'soon',
message: props.prayerListSoonMessage, pct: 0 }
// Still the honest "not yet" when this wall has no records provider: NOT an
// error — unbuilt, not broken. phase:'error' reads to a user as "something went
// wrong" rather than "not yet".
if (!canPrintRows.value) {
exportState.value = { label: item.label, kind: 'Prayer List', phase: 'soon',
message: props.prayerListSoonMessage, pct: 0 }
return
}
prints.printList(item)
}
function download(blob, name) {
@ -422,6 +467,11 @@ useShadowStyles(
<fieldset class="tape pfs">
<legend>Print Options</legend>
<button type="button" class="pbtn" @click="downloadPacket(item)">Prayer List</button>
<!-- CARD DECK — only when this wall can supply rows. A button that
cannot work is worse than an absent one, so it is gated on the
records provider rather than shown and then failing at click. -->
<button v-if="canPrintRows" type="button" class="pbtn"
@click="prints.printCardDeck(item)">Card Deck</button>
<button type="button" class="pbtn" @click="exportPoster(item)">Poster Map</button>
</fieldset>
</div>

View file

@ -31,6 +31,8 @@
* outline.clearCountry()
*/
import { resolveAssetsBase } from '../utils/embedBase.js'
const SOURCE_ID = 'doxa-country-outline'
const LAYER_ID = 'doxa-country-outline-line'
@ -124,12 +126,51 @@ export function useCountryOutline(getMap, opts = {}) {
// Swap any stale source, then (re)create the singleton outline layer.
clearCountry()
try {
m.addSource(SOURCE_ID, { type: 'vector', url: COUNTRY_BOUNDARIES_URL })
// ── COUNTRY POLYGONS, PER ENGINE ────────────────────────────────────────────────────────
// ★ COUNTRY_BOUNDARIES_URL IS A MAPBOX-HOSTED VECTOR TILESET, AND MAPLIBRE CANNOT FETCH IT.
// It throws "Fetch API cannot load mapbox://mapbox.country-boundaries-v1. URL scheme
// 'mapbox' is not supported" and then draws everything else perfectly, so the country
// outline is simply ABSENT with no visible failure. MEASURED on country-posters-mfe the
// moment it landed: engine swapped, 14 -> 4 mapbox requests, canvas fine, and this error in
// the console. THREE consumers share this composable — doxa-simple-map, countries-map and
// research-map — so the fix belongs here, once, not three times.
// Same shape already proven in DashboardMapPanel and RegionCountryMap.
//
// The ENGINE is read off the live map rather than guessed from config: the container carries
// maplibregl-map or mapboxgl-map, which is a fact about the instance in hand. Consumers call
// useCountryOutline(() => map.value) with no options, so there is no provider to pass in.
const isMaplibre = (() => {
try { return !!m.getContainer?.()?.classList?.contains('maplibregl-map') } catch (_) { return false }
})()
if (isMaplibre) {
// Add EMPTY first, then setData after the fetch, so addLayer below stays SYNCHRONOUS and
// the outline keeps its place in the layer order.
m.addSource(SOURCE_ID, { type: 'geojson', data: { type: 'FeatureCollection', features: [] } })
fetch(`${resolveAssetsBase(null)}/boundaries/world-adm0.geojson`)
.then((r) => r.json())
.then((gj) => {
// The tileset names the ISO field iso_3166_1_alpha_3; the geojson names it iso3.
// countryFilter() reads the tileset name, so normalise ONCE here and every filter
// downstream keeps reading the single name it always read.
for (const f of gj.features || []) {
if (f.properties && f.properties.iso3) f.properties.iso_3166_1_alpha_3 = f.properties.iso3
}
const live = typeof getMap === 'function' ? getMap() : null
const src = live && live.getSource(SOURCE_ID)
if (src) src.setData(gj)
})
.catch((e) => console.warn('[country-outline] boundaries failed to load', e))
} else {
m.addSource(SOURCE_ID, { type: 'vector', url: COUNTRY_BOUNDARIES_URL })
}
m.addLayer({
id: LAYER_ID,
type: 'line',
source: SOURCE_ID,
'source-layer': SOURCE_LAYER,
// 'source-layer' applies to VECTOR TILE sources only — MapLibre rejects a layer that
// declares one on a geojson source ("does not need a source-layer").
...(isMaplibre ? {} : { 'source-layer': SOURCE_LAYER }),
filter,
layout: { 'line-join': 'round', 'line-cap': 'round' },
// Outline only — no fill layer — so pins / labels under the country

View file

@ -4,6 +4,7 @@
// Import Vue functions from NPM package
import { ref, toRaw } from 'vue';
import { resolveAssetsBase } from '../utils/embedBase.js'
import { emissivePaint } from './useMapProvider.js';
// Import utilities
@ -416,16 +417,48 @@ export function useMapLayers(options = {}) {
? 'language-family-pins'
: undefined;
map.addSource('regions', {
type: 'vector',
url: 'mapbox://mapbox.country-boundaries-v1'
});
// ── REGION POLYGONS, PER ENGINE ────────────────────────────────────────────────────
// ★ SECOND SITE OF THE SAME DEFECT. mapbox://mapbox.country-boundaries-v1 is a
// Mapbox-hosted VECTOR TILESET; MapLibre cannot fetch a mapbox:// URL and throws
// Fetch API cannot load mapbox://mapbox.country-boundaries-v1. URL scheme "mapbox"
// then renders everything else correctly with this fill simply MISSING. Fixing the
// country-OUTLINE composable cleaned doxa-simple-map and research-map but left
// countries-map (and the new country-posters-mfe that nests it) still throwing —
// measured, which is how this second site was found at all.
// The keyless equivalent already ships: assets/boundaries/world-adm0.geojson.
// Engine read off the live map (its container carries maplibregl-map), not guessed.
const _isMaplibre = (() => {
try { return !!map.getContainer?.()?.classList?.contains('maplibregl-map') } catch (_) { return false }
})();
if (_isMaplibre) {
// EMPTY first so the addLayer below stays synchronous and keeps its z-order slot
// (regions → shadow → pins → hitbox); setData once the fetch lands.
map.addSource('regions', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
fetch(`${resolveAssetsBase(null)}/boundaries/world-adm0.geojson`)
.then((r) => r.json())
.then((gj) => {
// Tileset field is iso_3166_1_alpha_3; the geojson's is iso3. Normalise ONCE
// so every colour/filter expression downstream reads the name it always read.
for (const f of gj.features || []) {
if (f.properties && f.properties.iso3) f.properties.iso_3166_1_alpha_3 = f.properties.iso3;
}
const src = map.getSource('regions');
if (src) src.setData(gj);
})
.catch((e) => console.warn('[map-layers] region boundaries failed to load', e));
} else {
map.addSource('regions', {
type: 'vector',
url: 'mapbox://mapbox.country-boundaries-v1'
});
}
map.addLayer({
id: 'regions-fill',
type: 'fill',
source: 'regions',
'source-layer': 'country_boundaries',
// VECTOR-TILE only; MapLibre rejects a layer declaring source-layer on geojson.
...(_isMaplibre ? {} : { 'source-layer': 'country_boundaries' }),
paint: {
'fill-color': fillColorExpr,
'fill-opacity': colorScheme === 'none' ? 0.1 : 0.7,

View file

@ -0,0 +1,200 @@
// usePosterPrints.js — THE print actions every poster wall shares.
//
// ★ WHY THIS EXISTS. The poster wall was generalised into
// library/components/poster/PosterWall.vue and two bundles were migrated onto it —
// but the ORIGINAL, my-upg-100-list/poster-wall.vue (903 lines), was left behind.
// So the one wall carrying the real print actions is the one wall nothing else can
// benefit from, and the shared component's packet path is still a "coming soon"
// stub. Every print fix made in the 100-list is invisible to the three bundles on
// the shared component, and vice versa. That divergence is the bug.
//
// ── THE SEAM ────────────────────────────────────────────────────────────────
// The PRINT ACTIONS are shareable; the RECORD FETCHING is not. fetchListRecords in
// the 100-list knows the people-groups API, the selector evaluator and the pg_slugs
// special case; Key Cities and Country Posters have entirely different data shapes.
// So this composable takes a `records(item)` PROVIDER and owns everything after it:
// the HUD state machine, the in-place print frame, and the three documents.
//
// That is also what keeps the bundles honest — a wall that cannot supply records
// simply does not pass a provider, and the actions that need them stay hidden
// rather than failing at click time.
//
// ⚠ PRINTS IN PLACE, never window.open(). A popup is blockable and drops the reader
// out of the map; the shared openPrintFrame mounts a hidden same-page iframe. This
// is the mechanism the Driver asked for by name: "not supposed to open up a new
// browser, but should open up the print dialog directly in the browser".
import { ref } from 'vue';
import { openPrintFrame } from './printInPlace.js';
const esc = (s) => String(s ?? '')
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
/**
* @param {object} opts
* records async (item) => rows[] — REQUIRED for the list and deck prints.
* strings () => Promise<object> — the packet/card i18n catalog.
* lang () => string — the app locale; the deck and packet follow it.
* stock () => string — card stock id (see card-render.js STOCK).
* posterUrl (item) => string|null — the pre-rendered image, when one exists.
* label (item) => string
*/
export function usePosterPrints(opts = {}) {
// ⚠ ACCEPT the caller's state ref when it has one. A wall that renders its OWN
// exportState while these actions write to a private ref shows no progress and
// swallows errors — the mechanism works and the feedback does not. Owning a ref
// here is only the standalone default.
const exportState = opts.state || ref(null);
const busy = () => exportState.value !== null;
const label = opts.label || ((item) => item?.label || item?.id || '');
const lang = opts.lang || (() => 'en');
const stock = opts.stock || (() => 'poker');
const set = (item, kind, phase, message, pct) => {
exportState.value = { label: label(item), kind, phase, message, pct };
};
const done = (item, kind) => {
set(item, kind, 'done', 'print dialog opened', 100);
setTimeout(() => { exportState.value = null; }, 900);
};
const fail = (item, kind, e) => {
set(item, kind, 'error', String(e?.message || e).slice(0, 140), 0);
};
function dismiss() { exportState.value = null; }
async function rows(item) {
if (typeof opts.records !== 'function') {
throw new Error('this wall supplies no records provider');
}
const r = await opts.records(item);
if (!r || !r.length) throw new Error('that list resolved to no records');
return r;
}
// ── 1. THE POSTER ─────────────────────────────────────────────────────────
// Prefers the pre-rendered image; falls back to rasterising the poster's own
// HTML in the browser, which is what lets the repo stop shipping 41 MB of webp.
async function printPoster(item) {
if (busy()) return;
const kind = 'Poster';
set(item, kind, 'fetch', 'fetching the poster', 25);
try {
const url = opts.posterUrl ? opts.posterUrl(item) : null;
let html = '';
if (url) {
const res = await fetch(url, { cache: 'force-cache' });
if (res.ok) html = await res.text();
}
if (!html) throw new Error('no poster available for this item');
openPrintFrame(html, { autoprint: true });
done(item, kind);
} catch (e) { fail(item, kind, e); }
}
// ── 2. THE LIST ───────────────────────────────────────────────────────────
// The Driver's FIRST option — "that's what the whole app was about": the list
// itself as a print-paginated table with a prayer checkbox per row.
async function printList(item, { columns, title } = {}) {
if (busy()) return;
const kind = 'List';
set(item, kind, 'fetch', 'fetching records', 30);
try {
const data = await rows(item);
const cols = columns || [
{ key: 'name', head: 'Name', cls: 'c-name' },
{ key: 'country_code_label', head: 'Country' },
{ key: 'population', head: 'People', cls: 'c-num', num: true },
{ key: 'religion_label', head: 'Religion' },
];
const head = cols.map((c) => `<th>${esc(c.head)}</th>`).join('') + '<th class="c-pray">✓</th>';
const body = data.map((r) => '<tr>' + cols.map((c) => {
const raw = r[c.key] ?? r[`${c.key}_label`] ?? '—';
const v = c.num && Number.isFinite(Number(raw))
? Number(raw).toLocaleString(lang()) : raw;
return `<td${c.cls ? ` class="${c.cls}"` : ''}>${esc(v)}</td>`;
}).join('') + '<td class="c-pray">□</td></tr>').join('');
set(item, kind, 'render', `laying out ${data.length} rows`, 70);
const docTitle = title || `${label(item)} — ${data.length}`;
openPrintFrame(
`<!DOCTYPE html><html><head><meta charset="utf-8"><title>${esc(docTitle)}</title>`
+ '<style>'
+ '@page{size:letter portrait;margin:14mm 12mm}'
+ 'html,body{margin:0;background:#fff;color:#0f1419;'
+ "font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}"
+ '.hd{margin-bottom:14px}.hd h1{margin:0 0 3px;font-size:18px}'
+ '.hd p{margin:0;font-size:11px;color:#5b6672}'
+ 'table{width:100%;border-collapse:collapse;font-size:11px}'
+ 'thead th{text-align:left;border-bottom:2px solid #0f1419;padding:5px 6px;'
+ 'font-size:9.5px;text-transform:uppercase;letter-spacing:.05em}'
+ 'tbody td{padding:5px 6px;border-bottom:1px solid #e3e6ea}'
+ '.c-name{font-weight:600}.c-num{font-variant-numeric:tabular-nums}'
+ '.c-pray{width:26px;text-align:center;font-size:13px}'
+ 'tbody tr{break-inside:avoid}'
+ '</style></head><body>'
+ `<div class="hd"><h1>${esc(docTitle)}</h1>`
+ `<p>${data.length} entries · pray for them by name</p></div>`
+ `<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`
+ '</body></html>',
{ autoprint: true },
);
done(item, kind);
} catch (e) { fail(item, kind, e); }
}
// ── 3. THE CARD DECK ──────────────────────────────────────────────────────
// One card per PAGE at the chosen stock, so the print dialog shows CARDS rather
// than letter paper. The geometry lives in the shared card renderer.
async function printCardDeck(item) {
if (busy()) return;
const kind = 'Card Deck';
set(item, kind, 'fetch', 'fetching records', 30);
try {
const data = await rows(item);
const [{ createCardRenderer }, strings] = await Promise.all([
import('../packet/card-render.js'),
opts.strings ? opts.strings() : import('../scripts/posters/packet-strings.json').then((m) => m.default || m),
]);
set(item, kind, 'render', `laying out ${data.length} cards`, 70);
const R = createCardRenderer({ strings, lang: lang(), stock: stock() });
openPrintFrame(R.buildDeck(item, data), { autoprint: true });
done(item, kind);
} catch (e) { fail(item, kind, e); }
}
// ── 4. THE PRAYER PACKET ──────────────────────────────────────────────────
// The full booklet: map one-pager, people groups 20-per-page with photos, the 12
// prayer points, the prayer wheel. Shares the renderer with the build-time
// generator (library/packet/packet-render.js), so a printed packet and a
// generated one cannot diverge.
//
// The MAP PAGE is optional by design: `mapSvg` is supplied by the caller because
// only the bundle knows where its posters are served from. If it cannot be
// fetched the packet still prints — a cover without the map beats no packet.
async function printPacket(item, { mapSvg = '' } = {}) {
if (busy()) return;
const kind = 'Prayer Packet';
set(item, kind, 'fetch', 'fetching people groups', 30);
try {
const data = await rows(item);
const [{ createPacketRenderer }, strings] = await Promise.all([
import('../packet/packet-render.js'),
opts.strings ? opts.strings() : import('../scripts/posters/packet-strings.json').then((m) => m.default || m),
]);
const R = createPacketRenderer({ strings, lang: lang() });
// Photos stay as URLs. The Node generator base64-inlines them so its file is
// self-contained offline; in a browser the print frame fetches them, and
// inlining ~95 images client-side would stall the tab for no gain.
const photos = new Map(
data.filter((g) => g.image_url).map((g) => [g.slug, g.image_url]),
);
set(item, kind, 'render', `printing ${data.length} people groups`, 70);
openPrintFrame(R.buildPacket(item, data, photos, mapSvg), { autoprint: true });
done(item, kind);
} catch (e) { fail(item, kind, e); }
}
return { exportState, dismiss, printPoster, printList, printCardDeck, printPacket };
}

View file

@ -57,6 +57,8 @@
"build:unlocked": "bun run gates:pre && bun run library-index && bun run check:library-index && bun run clean && bun run token && bun run build:bundles && bun run tree && bun run gates:post && bun run gallery",
"check:card-deck": "node internal/check-card-deck.mjs",
"check:poster-raster": "node internal/check-poster-raster.mjs",
"check:deck-click": "node internal/check-deck-click.mjs",
"check:poster-prints": "node internal/check-poster-prints.mjs",
"check:provider-switch": "node internal/check-provider-switch.mjs",
"check:mfe-two-tab": "node internal/check-mfe-two-tab.mjs",
"check:share-policy": "node internal/check-share-policy.mjs",

View file

@ -441,7 +441,11 @@ const mapTheme = useMapTheme(uiStore)
const FEATURES = {
doxaRegions: true,
adoption: false, // Adoption tab hidden per UX request; wiring kept warm (flip to true to restore)
share: true, // Share button enabled — parameterized share control on every map
// The map-level ShareButton is GONE, not flagged off — country-posters-mfe.vue's
// header owns sharing for this bundle. See the note at its former site in the
// toolbar. This flag is kept only so the FEATURES shape stays stable for any
// other consumer reading it.
share: false,
hamburgerMenu: false, // canonical default — emit/listen mismatch + drawer surplus
// Per QA building-round-1 R3 A4: clear the geocoder text on tab switch by
// default; flip to true to preserve the previous tab's query when returning.
@ -2137,9 +2141,14 @@ onBeforeUnmount(() => {
<LocationButton :map="map" :is-dark="isDark" />
<FullscreenButton :map-container="mapContainer" :is-dark="isDark" />
<ThemeToggleButton :is-dark="isDark" @toggle="handleToggleTheme" />
<!-- Share button hidden behind FEATURES.share (glitch — PR gate). All
ShareButton code kept warm; flip FEATURES.share=true to restore. -->
<ShareButton v-if="FEATURES.share" :is-dark="isDark" :embed-path="'country-posters/countries-map/index.html'" />
<!-- ShareButton intentionally OMITTED; country-posters-mfe.vue's header owns
sharing for this bundle, exactly as upg-100-mfe does for the 100-list.
Rendering both gave the bundle TWO share affordances on one surface — the
BUG-UPG100-DOUBLE-SHARE state the 100-list already fixed once, caught here
by check:share-policy rather than by a reader seeing two buttons.
REMOVED, not flag-gated: a `v-if="false"` button is dead code that still
reads as an affordance to every reader and every counter. A map embedded
WITHOUT the MFE header would add one back deliberately. -->
</MapToolbar>
<!-- Fly + Clusters floating toolbar — sibling of the legend card, mounted

View file

@ -2,6 +2,11 @@
/**
* country-posters-mfe.vue — Country Posters as a TWO-TAB element.
*
* @element country-posters-mfe
* @description Country Posters as ONE two-tab element — the countries map and the printable
* poster wall behind a single view switcher, structured like the UPG-100 list. The switcher is
* the library's shared ViewSelect, not a third hand-rolled copy.
*
* Driver: "Country Posters is supposed to have two tabs, structured like the
* UPG-100 list."
*

View file

@ -260,9 +260,36 @@ const NOT_EXPERIMENTAL = new Set([
'template-bundle',
'people-groups-grid',
'research-dashboard',
'big-picture'
'big-picture',
// roundup ruling (EXPERIMENTAL-MAPS-ROUNDUP.md, 2026-09-24): scaffolding and authoring
// tooling, not maps -- the studio still surfaces them for developers.
'demo-map',
'golden-path-check',
// doxa-research-map is the PART already composed into the Simple Research tab
// (research-map-plus-grid) -- same redundancy rule as research-dashboard/big-picture.
'doxa-research-map',
// stack-demo is broken and Driver-DEFERRED; a broken tab in the giveaway shelf reads as
// product damage. It returns when someone fixes it (a tracked issue).
'stack-demo'
])
// ── EXPERIMENTAL ORDER = KINGDOM IMPACT, not the alphabet ────────────────────────────────
// The roundup charter orders the folder by "potential to get people TO the unreached groups
// and help them meet and understand Jesus", finalized 2026-09-24 AFTER reading every map's
// rendered output (EXPERIMENTAL-MAPS-ROUNDUP.md has the per-map reasoning). Rank is
// EDITORIAL, the same class of decision as the curated TAB_DEF above -- which is why it
// lives here and not in per-bundle meta: the manifest deliberately strips meta config, and
// an order is a statement about the whole shelf, not about one bundle. Unranked bundles
// still appear (discovery is never gated by this) -- they sort after the ranked, A-Z.
const EXPERIMENTAL_RANK = {
'prayer-walk-planner': 1, // feet on streets: zones -> printed one-page walking maps
'upg-neighbor': 2, // the rescued legacy set; entry = send-map ("who goes, to whom")
'key-cities': 3, // where the unreached physically live
'all-people-groups': 4, // the census view that feeds everything else
'country-posters': 5, // mobilization print artifacts
'doxa-vision-card': 6, // vision framing
}
// The tab manifest. Each entry CURATES one tab: which bundle+profile it shows, in what order, under
// what human label. Every tab nests its plugin's REAL custom element — there is no iframe mode.
//
@ -570,8 +597,12 @@ function experimentalTabsFromManifest(manifest) {
discovered: true // marks an auto-discovered tab (used by the rail + tests)
})
}
// Stable, predictable order so the folder does not reshuffle between builds.
out.sort((a, z) => a.label.localeCompare(z.label))
// Ranked first (Kingdom impact, see EXPERIMENTAL_RANK), then A-Z -- stable between builds.
out.sort((a, z) => {
const ra = EXPERIMENTAL_RANK[a.id] || 999
const rz = EXPERIMENTAL_RANK[z.id] || 999
return ra !== rz ? ra - rz : a.label.localeCompare(z.label)
})
return out
}

View file

@ -38,6 +38,8 @@
*/
import { computed, inject, ref, watch, onBeforeUnmount } from 'vue'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { usePosterPrints } from '@map/composables/usePosterPrints.js'
import { openPrintFrame } from '@map/composables/printInPlace.js'
import { useI18n } from 'vue-i18n'
// Export-modal geometry — see the note at .modal-scrim below, and library/utils/viewportScrim.js.
import { useViewportScrim } from '@map/utils/viewportScrim.js'
@ -339,34 +341,13 @@ const { scrimRef, scrimStyle } = useViewportScrim(exportState)
// "popup blocked -> fall back to opening the raw image" branch every caller carried is now dead.
// The returned object keeps a truthy `.document`-less shape only so existing `if (!w)` guards stay
// harmless; callers should no longer rely on a popup-blocked null.
function openPrintWindow(title, bodyHtml, { autoprint = false } = {}) {
const frame = document.createElement('iframe')
// Off-screen rather than display:none — a display:none iframe does not lay out in every engine,
// and a zero-height layout can print a blank first page.
frame.setAttribute('aria-hidden', 'true')
frame.style.cssText = 'position:fixed;right:0;bottom:0;width:1px;height:1px;opacity:0;border:0'
document.body.appendChild(frame)
const doc = frame.contentDocument
if (!doc) { frame.remove(); return null } // no document -> caller keeps its fallback
doc.open()
doc.write(bodyHtml)
if (autoprint) {
// Appended AFTER the caller's markup so it runs once the document is parsed —
// matches the <img onload> timing the poster path already relies on.
doc.write(
'<script>window.addEventListener("load",function(){setTimeout(function(){window.focus();window.print()},150)})</' + 'script>')
}
doc.close()
// Reclaim the node once the dialog has been dismissed. `afterprint` is the honest signal; the
// timeout is a floor for engines that never fire it, so a click can never leak a frame per press.
const drop = () => { if (frame.parentNode) frame.remove() }
try { frame.contentWindow.addEventListener('afterprint', () => setTimeout(drop, 0)) } catch { /* cross-doc guard */ }
setTimeout(drop, 60000)
return frame.contentWindow
}
// ★ ONE FRAME MECHANISM, not a fourth copy. This was a local reimplementation of
// library/composables/printInPlace.js — same off-screen iframe, same doc.write,
// same autoprint script, differing only in an unused `title` argument. Four copies
// of one mechanism is how a fix to the blank-first-page behaviour reaches one
// caller and not the others. The shared helper takes (bodyHtml, opts), so the
// title is dropped here: it was never used, the document's own <title> carries it.
const openPrintWindow = (title, bodyHtml, opts) => openPrintFrame(bodyHtml, opts);
// Escape the handful of characters that break out of an HTML text node or a "..." attribute.
// Print-window markup is built by string concatenation (no template-literal/backtick rule for
@ -533,43 +514,35 @@ async function printList100(list) {
// pre-baked PDF pointer exists there, `prayer-card-{country}-{lang}.pdf`, not a per-PG
// generator). Styling below borrows the poster-wall's own vocabulary (tape header, thin
// card border, Material Light palette) instead of inventing a fourth visual language.
// ── CARD DECK: the SHARED action ────────────────────────────────────────────
// This function used to be a second, hand-maintained copy of the deck print —
// same renderer call, same stock, same language resolution — living beside the
// composable's. Two copies of one fact drift: a fix to either was invisible to
// the other, which is the whole reason the wall was being consolidated.
//
// It now delegates to library/composables/usePosterPrints.js, sharing THIS wall's
// exportState so the HUD keeps rendering one surface. The bundle still owns the
// part that cannot be shared — fetchListRecords knows the people-groups API, the
// selector evaluator and the pg_slugs special case — and hands it over as the
// `records` provider.
const prints = usePosterPrints({
state: exportState,
records: (list) => fetchListRecords(list),
lang: () => deckLang(),
stock: () => deckStock.value,
label: (list) => list?.label || list?.id || '',
})
async function printCardDeck(list) {
if (exportState.value) return
exportState.value = { label: list.label, kind: 'Card Deck', phase: 'fetch',
message: 'fetching people-group records', pct: 30 }
try {
const rows = await fetchListRecords(list)
// ★ THE PAGE IS THE CARD. This used to set @page{size:letter portrait} and give
// each card height:100%, so a "deck" printed as 95 mostly-empty 8.5x11 sheets
// with no photo. The Driver: "the print dialog that opens up needs to show all
// the cards not in a paper format but a CARD format, the aspect ratio that is
// used in the card generator."
//
// The pages now come from library/packet/card-render.js — one renderer, every
// caller — which sets @page to the card stock's own inches, so one card is
// exactly one page at 2.5x3.5in. Print on cardstock and cut, or send the PDF
// to a card printer.
const [{ createCardRenderer }, strings] = await Promise.all([
import('@map/packet/card-render.js'),
import('@map/scripts/posters/packet-strings.json').then((m) => m.default || m),
])
const R = createCardRenderer({ strings, lang: deckLang(), stock: deckStock.value })
const title = `Doxa UPG prayer cards — ${escapeHtml(list.label || list.id)}`
exportState.value = { ...exportState.value, phase: 'render',
message: `laying out ${rows.length} cards`, pct: 70 }
const w = openPrintWindow(title, R.buildDeck(list, rows), { autoprint: true })
if (!w) {
// See the note in the list path: an iframe cannot be pop-up blocked.
exportState.value = { label: list.label, kind: 'Card Deck', phase: 'error',
message: 'Could not open the print dialog in this view — try the Download button.', pct: 0 }
return
}
exportState.value = { ...exportState.value, phase: 'done', message: 'print dialog opened', pct: 100 }
setTimeout(() => { exportState.value = null }, 900)
} catch (e) {
exportState.value = { label: list.label, kind: 'Card Deck', phase: 'error',
message: String(e?.message || e).slice(0, 140) + (API_BASE ? '' :
' (no API configured in this preview — set window.MAP_APP_API_URL)'), pct: 0 }
await prints.printCardDeck(list)
// Keep the preview-specific hint the shared action cannot know about: in a
// dropped tree with no API configured the failure is a CONFIGURATION problem,
// not a broken button, and saying so is the difference between "this is broken"
// and "this needs a URL".
const st = exportState.value
if (st && st.phase === 'error' && !API_BASE && !/MAP_APP_API_URL/.test(st.message)) {
exportState.value = { ...st,
message: `${st.message} (no API configured in this preview — set window.MAP_APP_API_URL)` }
}
}

View file

@ -81,7 +81,7 @@ import { useI18n } from 'vue-i18n'
// The in-place print helper: a hidden same-page iframe, NOT window.open(). A popup
// is blockable and drops the user out of the map; the iframe opens the print dialog
// right here. Shared so the packet, the poster and the card deck all print the same way.
import { openPrintFrame } from '@map/composables/printInPlace.js'
import { usePosterPrints } from '@map/composables/usePosterPrints.js'
import { buildLegendNodes, leafZones } from './data/legend-tree.js'
import { resolveAllZones } from './data/zone-geometry.js'
// slice 09b-9 §3 (L18/L29 FIX): the per-zone color OVERRIDE map. zone-colors.json
@ -344,10 +344,10 @@ const API_BASE = (typeof window !== 'undefined' && window.MAP_APP_API_URL)
|| import.meta.env.VITE_API_BASE_URL
|| ''
// Prayer-packet printing (legend Export > Prayer packet). openPrintFrame is the
// SHARED in-place print helper — a hidden same-page iframe, so the dialog opens in
// this tab rather than a popup a browser may block. The packet's locale follows the
// app's, so a reader gets it in their own language.
// Prayer-packet printing (legend Export > Prayer packet). The ACTION lives in the
// shared usePosterPrints composable — it prints through a hidden same-page iframe,
// so the dialog opens in this tab rather than a popup a browser may block. The
// packet's locale follows the app's, so a reader gets it in their own language.
const { locale: packetLocale } = useI18n()
const DEAF_COUNT = 123
@ -1132,68 +1132,84 @@ async function onLegendExport(kind) {
// Build and print a zone's prayer packet without leaving the page.
// PAGE ORDER IS THE PRODUCT SPEC: map · people groups 20/page with photos ·
// 12 prayer points · prayer wheel.
let packetBusy = false;
async function printPrayerPacket(zoneId) {
if (packetBusy) return; // a second click must not open a second job
packetBusy = true;
try {
const zone = allLists().find((z) => z.id === zoneId);
if (!zone) throw new Error(`no list "${zoneId}"`);
status.value = 'Building the prayer packet…';
// ── THE PRAYER PACKET: the SHARED action ────────────────────────────────────
// This was a second hand-maintained copy of the packet print — its own renderer
// import, its own strings fetch, its own language resolution — beside the card
// deck's, which was consolidated first. Two copies of one fact drift.
//
// It now delegates to library/composables/usePosterPrints.js. What stays HERE is
// what genuinely cannot be shared: resolving which people groups are in a list
// (the people-groups API, the selector evaluator, and the pg_slugs special case
// that 12 of the 27 lists need), and where this bundle's posters are served from.
const packetPrints = usePosterPrints({
records: (zone) => packetRecords(zone),
lang: () => packetLang(),
label: (zone) => zone?.label || zone?.id || '',
});
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()));
// ⚠ pg_slugs BYPASSES the shared evaluator — it intentionally THROWS on that
// type, and 12 of the 27 lists use it. Same special-case the map and the poster
// wall already apply, so all three agree about who is in a list.
let slugs;
if (zone.selector && zone.selector.type === 'pg_slugs') {
slugs = new Set(zone.selector.values || []);
} else {
const { evaluate } = await import('./data/selector-evaluator.js');
slugs = new Set(evaluate(zone.selector, records)); // evaluate returns a Set
}
const groups = records.filter((r) => slugs.has(r.slug))
.sort((a, b) => String(a.name).localeCompare(String(b.name), packetLang()));
if (!groups.length) throw new Error('that list resolved to no people groups');
// Photos stay as URLs here. The Node generator base64-inlines them so its file
// is self-contained offline; in a browser the print frame can fetch them, and
// inlining 95 images client-side would stall the tab for no gain.
const photos = new Map(groups.filter((g) => g.image_url).map((g) => [g.slug, g.image_url]));
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() });
// The map page takes the poster's <svg>. If it cannot be fetched the packet
// still prints — a cover without the map beats no packet at all.
let mapSvg = '';
try {
const base = resolveAssetsBase(profileConfig.value);
const html = await (await fetch(`${base}/posters/${zoneId}.html`, { cache: 'force-cache' })).text();
const svg = html.match(/<svg[\s\S]*?<\/svg>/);
if (svg) mapSvg = `<style>${R.mapCss(html)}</style>`
+ `<div style="position:absolute;left:0;top:250px;width:${R.W}px;`
+ `transform:scale(0.62);transform-origin:top center">${svg[0]}</div>`;
} catch { /* cover page without the map */ }
status.value = `Printing ${groups.length} people groups…`;
openPrintFrame(R.buildPacket(zone, groups, photos, mapSvg), { autoprint: true });
status.value = '';
} catch (err) {
status.value = `Packet failed: ${err && err.message ? err.message : err}`;
setTimeout(() => { status.value = ''; }, 4000);
} finally {
packetBusy = false;
// ⚠ BRIDGE THE PROGRESS BACK TO THE SURFACE THIS MAP ACTUALLY SHOWS.
// The composable reports through its own exportState; this profile has no HUD for
// that and displays `status` instead. Without this watcher the packet would print
// SILENTLY — no "building…", and a failure would vanish with nothing said — which
// is the mechanism working while the feedback does not.
watch(packetPrints.exportState, (st) => {
if (!st) { if (/packet/i.test(status.value)) status.value = ''; return; }
if (st.phase === 'error') {
status.value = `Packet failed: ${st.message}`;
setTimeout(() => { if (status.value.startsWith('Packet failed')) status.value = ''; }, 4000);
return;
}
status.value = st.phase === 'done' ? '' : `${st.message}…`;
});
// BUNDLE-SPECIFIC: who is in this list. ⚠ pg_slugs BYPASSES the shared evaluator —
// it intentionally THROWS on that type, and 12 of the 27 lists use it. Same
// special-case the map and the poster wall apply, so all three agree about
// membership.
async function packetRecords(zone) {
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()));
let slugs;
if (zone.selector && zone.selector.type === 'pg_slugs') {
slugs = new Set(zone.selector.values || []);
} else {
const { evaluate } = await import('./data/selector-evaluator.js');
slugs = new Set(evaluate(zone.selector, records)); // evaluate returns a Set
}
return records.filter((r) => slugs.has(r.slug))
.sort((a, b) => String(a.name).localeCompare(String(b.name), packetLang()));
}
// BUNDLE-SPECIFIC: the map page's <svg>, lifted from this bundle's own poster.
// Absent is fine — the packet prints a cover without the map rather than failing.
async function packetMapSvg(zoneId, R) {
try {
const base = resolveAssetsBase(profileConfig.value);
const html = await (await fetch(`${base}/posters/${zoneId}.html`, { cache: 'force-cache' })).text();
const svg = html.match(/<svg[\s\S]*?<\/svg>/);
if (!svg) return '';
return `<style>${R.mapCss(html)}</style>`
+ `<div style="position:absolute;left:0;top:250px;width:${R.W}px;`
+ `transform:scale(0.62);transform-origin:top center">${svg[0]}</div>`;
} catch { return ''; }
}
async function printPrayerPacket(zoneId) {
const zone = allLists().find((z) => z.id === zoneId);
if (!zone) return;
// The map svg needs the renderer's own mapCss/W, so it is resolved here and
// handed in rather than fetched inside the shared action, which knows nothing
// about where this bundle serves its posters.
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() });
await packetPrints.printPacket(zone, { mapSvg: await packetMapSvg(zoneId, R) });
}
// Every list across all three cluster buckets, flattened.

View file

@ -2,8 +2,8 @@
/**
* upg-neighbor.vue — LEGACY-WRAPPER bundle (experimental roundup).
*
* @description Rescues the three pre-bundler experimental maps that live inside the legacy
* upg-neighbor IIFE — send-map, flower-mesh and lang-cluster — without rebuilding them and
* @description Rescues the five pre-bundler experimental maps that live inside the legacy
* upg-neighbor IIFE — send-map, the upg-neighbors/bloom cockpit, lang-cluster and flower-mesh — without rebuilding them and
* without letting their old engine touch the modern bundles: each profile mounts its element
* inside a sandboxed IFRAME whose host page (assets/legacy/upg-neighbor/index.html) loads the
* prebuilt script, shims its relative /api fetches to the live API, and passes the token.

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-cqk7oshuPW5FdnbZ/DyfBBXN2FObAI+V1jrbxmZR2xzNiBtWG65PETQ5/w5PCDB2" crossorigin="anonymous"></script>
<script src="../all-people-groups.js" integrity="sha384-R42WaDgwpLQcS2xBY+w+VgqpnTlojkRR3jqMq56IOaLn2MmuDoYwtXysc9CMN58c" 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

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <bp-big-picture> custom element. -->
<script src="../../big-picture.js" integrity="sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h" crossorigin="anonymous"></script>
<script src="../../big-picture.js" integrity="sha384-voYmtWeACTFBJOOeqcbf5cuBKSjyskA89cjvm5qcvrx9IUFTAhLuICyRXUM6fL+Y" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <bp-big-picture> custom element. -->
<script src="../../big-picture.js" integrity="sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h" crossorigin="anonymous"></script>
<script src="../../big-picture.js" integrity="sha384-voYmtWeACTFBJOOeqcbf5cuBKSjyskA89cjvm5qcvrx9IUFTAhLuICyRXUM6fL+Y" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -37,7 +37,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <bp-big-picture> custom element. -->
<script src="../big-picture.js" integrity="sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h" crossorigin="anonymous"></script>
<script src="../big-picture.js" integrity="sha384-voYmtWeACTFBJOOeqcbf5cuBKSjyskA89cjvm5qcvrx9IUFTAhLuICyRXUM6fL+Y" 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-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V" 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-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V" 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-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R" crossorigin="anonymous"></script>
<script src="../cities-internet.js" integrity="sha384-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V" 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-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk" 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-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk" 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-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk" 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-YbJgljynZ4t3/Ro44oHQy9ihl9/5o7j4IKts8MNRfA73Cb5cyD7U52DklXjzpLpG" crossorigin="anonymous"></script>
<script src="../../dashboard-plus-grid.js" integrity="sha384-KWYs/unuewlt9PJ4CC/VlyoghFmN4KtHqsweo68lJwUNL1g3FJ8VctASOYWAt91q" 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-YbJgljynZ4t3/Ro44oHQy9ihl9/5o7j4IKts8MNRfA73Cb5cyD7U52DklXjzpLpG" crossorigin="anonymous"></script>
<script src="../dashboard-plus-grid.js" integrity="sha384-KWYs/unuewlt9PJ4CC/VlyoghFmN4KtHqsweo68lJwUNL1g3FJ8VctASOYWAt91q" 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-QiUJUgeFn27tiHKVhAYtJuIWo+1z5xsCUP1Nj4m1H4cFUV3vUA7ADTP58Ky3oVtQ" crossorigin="anonymous"></script>
<script src="../demo-map.js" integrity="sha384-2ItGdUG5OW9uHN4ldNF6To6hvTy3XGdFnJrJenyyqShBjIi2NgcyXTyhYu2mttHH" 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-QiUJUgeFn27tiHKVhAYtJuIWo+1z5xsCUP1Nj4m1H4cFUV3vUA7ADTP58Ky3oVtQ" crossorigin="anonymous"></script>
<script src="../../demo-map.js" integrity="sha384-2ItGdUG5OW9uHN4ldNF6To6hvTy3XGdFnJrJenyyqShBjIi2NgcyXTyhYu2mttHH" 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 <doxa-maps-spa> custom element. -->
<script src="../../doxa-maps-spa.js" integrity="sha384-Ce+DKsLgwfej+rgoWeaWFuMMbR3Y4H+8io/lVL+lQfX0O3iGKwDLf4remDkHGCrP" crossorigin="anonymous"></script>
<script src="../../doxa-maps-spa.js" integrity="sha384-1f+/rBSGo/1jyUmRmGg34CIU/PcGkB7M3jah1DO0ocb5O0zj81wpUemXcz4WAFz8" 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 <doxa-maps-spa> custom element. -->
<script src="../doxa-maps-spa.js" integrity="sha384-Ce+DKsLgwfej+rgoWeaWFuMMbR3Y4H+8io/lVL+lQfX0O3iGKwDLf4remDkHGCrP" crossorigin="anonymous"></script>
<script src="../doxa-maps-spa.js" integrity="sha384-1f+/rBSGo/1jyUmRmGg34CIU/PcGkB7M3jah1DO0ocb5O0zj81wpUemXcz4WAFz8" 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-research-map> custom element. -->
<script src="../doxa-research-map.js" integrity="sha384-oecoezttPmpRe5GNG/Yko6IkEbtLjCD7PD7qghU1ZigprnfLKhbf0eBEiEbE24iR" crossorigin="anonymous"></script>
<script src="../doxa-research-map.js" integrity="sha384-ycDpVdTjizEUxzsO+lQpTagpdLenXlLnvFQ/juKcWeazo2Qvo1B1QYpDxykzA9Zp" 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-oecoezttPmpRe5GNG/Yko6IkEbtLjCD7PD7qghU1ZigprnfLKhbf0eBEiEbE24iR" crossorigin="anonymous"></script>
<script src="../../doxa-research-map.js" integrity="sha384-ycDpVdTjizEUxzsO+lQpTagpdLenXlLnvFQ/juKcWeazo2Qvo1B1QYpDxykzA9Zp" 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-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-8drw8bByutNX9KwjTP4nrgMqtlu7xgrqxK6ebWLh8VTWAYC39TQ3d/8qwBxhPJMC" 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-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-8drw8bByutNX9KwjTP4nrgMqtlu7xgrqxK6ebWLh8VTWAYC39TQ3d/8qwBxhPJMC" 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-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W" crossorigin="anonymous"></script>
<script src="../doxa-simple-map.js" integrity="sha384-8drw8bByutNX9KwjTP4nrgMqtlu7xgrqxK6ebWLh8VTWAYC39TQ3d/8qwBxhPJMC" 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-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W" crossorigin="anonymous"></script>
<script src="../../doxa-simple-map.js" integrity="sha384-8drw8bByutNX9KwjTP4nrgMqtlu7xgrqxK6ebWLh8VTWAYC39TQ3d/8qwBxhPJMC" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0" 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 <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0" 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

@ -9,7 +9,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-cqk7oshuPW5FdnbZ/DyfBBXN2FObAI+V1jrbxmZR2xzNiBtWG65PETQ5/w5PCDB2",
"integrity": "sha384-R42WaDgwpLQcS2xBY+w+VgqpnTlojkRR3jqMq56IOaLn2MmuDoYwtXysc9CMN58c",
"elements": [
"all-people-groups"
],
@ -25,7 +25,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-cqk7oshuPW5FdnbZ/DyfBBXN2FObAI+V1jrbxmZR2xzNiBtWG65PETQ5/w5PCDB2\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-R42WaDgwpLQcS2xBY+w+VgqpnTlojkRR3jqMq56IOaLn2MmuDoYwtXysc9CMN58c\" 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"
}
@ -35,7 +35,7 @@
"bundle": "big-picture",
"scriptUrl": "./big-picture/big-picture.js",
"output": "doxa-maps/big-picture/big-picture.js",
"integrity": "sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h",
"integrity": "sha384-voYmtWeACTFBJOOeqcbf5cuBKSjyskA89cjvm5qcvrx9IUFTAhLuICyRXUM6fL+Y",
"elements": [
"bp-big-picture",
"bp-research-header",
@ -57,7 +57,7 @@
"scriptUrl": "./big-picture/big-picture.js",
"embed": {
"iframe": "<iframe src=\"./big-picture/big-picture/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=\"./big-picture/big-picture.js\" integrity=\"sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h\" crossorigin=\"anonymous\"></script>\n<bp-big-picture profile-config='{\"profile\":\"big-picture\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></bp-big-picture>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./big-picture/big-picture.js\" integrity=\"sha384-voYmtWeACTFBJOOeqcbf5cuBKSjyskA89cjvm5qcvrx9IUFTAhLuICyRXUM6fL+Y\" crossorigin=\"anonymous\"></script>\n<bp-big-picture profile-config='{\"profile\":\"big-picture\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></bp-big-picture>"
},
"instances": [
{
@ -101,7 +101,7 @@
"bundle": "cities-internet",
"scriptUrl": "./cities-internet/cities-internet.js",
"output": "doxa-maps/cities-internet/cities-internet.js",
"integrity": "sha384-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R",
"integrity": "sha384-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V",
"elements": [
"cities-internet-map"
],
@ -117,7 +117,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-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R\" 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-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V\" 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>"
}
},
{
@ -131,7 +131,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V\" 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>"
}
},
{
@ -145,7 +145,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-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R\" 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-lF6OGT0FH7sBXnALLiAoji8TADE05n7yqqoortZAifVdH6fNJ4yQeRRwjT8oXq+V\" 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>"
}
}
]
@ -154,7 +154,7 @@
"bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js",
"output": "doxa-maps/country-posters/country-posters.js",
"integrity": "sha384-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7",
"integrity": "sha384-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk",
"elements": [
"country-posters-map"
],
@ -174,7 +174,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk\" 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>"
}
},
{
@ -188,21 +188,21 @@
"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-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7\" 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-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk\" 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>"
}
},
{
"profile": "country-posters-mfe",
"file": "country-posters-mfe.vue",
"description": "",
"element": "country-posters-map",
"description": "Country Posters as ONE two-tab element — the countries map and the printable",
"element": "country-posters-mfe",
"type": "nested-elements-mfe",
"isMap": true,
"iframeUrl": "./country-posters/country-posters-mfe/index.html",
"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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-QdkwinBM2mJMqVlEX7HYctnr/9k9Nj01MDwFGwYL7tT62O8Nm3JuujLJ8qxZTFJ7\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-XR0q0Rq4Uy+Ivttgq3+NsAtNNQJU6r3ktQTxdfjF2HEjwpbQZTxXyeqJhFb8wJVk\" 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>"
}
}
]
@ -211,7 +211,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-YbJgljynZ4t3/Ro44oHQy9ihl9/5o7j4IKts8MNRfA73Cb5cyD7U52DklXjzpLpG",
"integrity": "sha384-KWYs/unuewlt9PJ4CC/VlyoghFmN4KtHqsweo68lJwUNL1g3FJ8VctASOYWAt91q",
"elements": [
"dashboard-plus-grid"
],
@ -231,7 +231,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-YbJgljynZ4t3/Ro44oHQy9ihl9/5o7j4IKts8MNRfA73Cb5cyD7U52DklXjzpLpG\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-KWYs/unuewlt9PJ4CC/VlyoghFmN4KtHqsweo68lJwUNL1g3FJ8VctASOYWAt91q\" 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": [
{
@ -255,7 +255,7 @@
"bundle": "demo-map",
"scriptUrl": "./demo-map/demo-map.js",
"output": "doxa-maps/demo-map/demo-map.js",
"integrity": "sha384-QiUJUgeFn27tiHKVhAYtJuIWo+1z5xsCUP1Nj4m1H4cFUV3vUA7ADTP58Ky3oVtQ",
"integrity": "sha384-2ItGdUG5OW9uHN4ldNF6To6hvTy3XGdFnJrJenyyqShBjIi2NgcyXTyhYu2mttHH",
"elements": [
"demo-map"
],
@ -271,7 +271,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-QiUJUgeFn27tiHKVhAYtJuIWo+1z5xsCUP1Nj4m1H4cFUV3vUA7ADTP58Ky3oVtQ\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-2ItGdUG5OW9uHN4ldNF6To6hvTy3XGdFnJrJenyyqShBjIi2NgcyXTyhYu2mttHH\" 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": [
{
@ -298,7 +298,7 @@
"bundle": "doxa-maps-spa",
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"output": "doxa-maps/doxa-maps-spa/doxa-maps-spa.js",
"integrity": "sha384-Ce+DKsLgwfej+rgoWeaWFuMMbR3Y4H+8io/lVL+lQfX0O3iGKwDLf4remDkHGCrP",
"integrity": "sha384-1f+/rBSGo/1jyUmRmGg34CIU/PcGkB7M3jah1DO0ocb5O0zj81wpUemXcz4WAFz8",
"elements": [
"doxa-maps-spa"
],
@ -322,7 +322,7 @@
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
"embed": {
"iframe": "<iframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-Ce+DKsLgwfej+rgoWeaWFuMMbR3Y4H+8io/lVL+lQfX0O3iGKwDLf4remDkHGCrP\" crossorigin=\"anonymous\"></script>\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-1f+/rBSGo/1jyUmRmGg34CIU/PcGkB7M3jah1DO0ocb5O0zj81wpUemXcz4WAFz8\" crossorigin=\"anonymous\"></script>\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
},
"instances": [
{
@ -349,7 +349,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-oecoezttPmpRe5GNG/Yko6IkEbtLjCD7PD7qghU1ZigprnfLKhbf0eBEiEbE24iR",
"integrity": "sha384-ycDpVdTjizEUxzsO+lQpTagpdLenXlLnvFQ/juKcWeazo2Qvo1B1QYpDxykzA9Zp",
"elements": [
"doxa-research-map"
],
@ -365,7 +365,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-oecoezttPmpRe5GNG/Yko6IkEbtLjCD7PD7qghU1ZigprnfLKhbf0eBEiEbE24iR\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-ycDpVdTjizEUxzsO+lQpTagpdLenXlLnvFQ/juKcWeazo2Qvo1B1QYpDxykzA9Zp\" 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": [
{
@ -391,7 +391,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-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W",
"integrity": "sha384-8drw8bByutNX9KwjTP4nrgMqtlu7xgrqxK6ebWLh8VTWAYC39TQ3d/8qwBxhPJMC",
"elements": [
"doxa-map",
"doxa-simple-map"
@ -408,7 +408,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-8drw8bByutNX9KwjTP4nrgMqtlu7xgrqxK6ebWLh8VTWAYC39TQ3d/8qwBxhPJMC\" 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": [
{
@ -591,7 +591,7 @@
"bundle": "gospel-resources",
"scriptUrl": "./gospel-resources/gospel-resources.js",
"output": "doxa-maps/gospel-resources/gospel-resources.js",
"integrity": "sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI",
"integrity": "sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0",
"elements": [
"gospel-resources-map"
],
@ -607,7 +607,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/charts/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -621,7 +621,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -635,7 +635,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/resources-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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -649,7 +649,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/resources-map/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=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-XgF3DvRZEHIzZxcUqixiOsEk5bh6vIpwP7Drf9fBYy9F9cB7bp3NP2G2E56pPGt0\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
}
]
@ -658,7 +658,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-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra",
"integrity": "sha384-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g",
"elements": [
"my-upg-100-list-map"
],
@ -678,7 +678,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-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra\" 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-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g\" 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>"
}
},
{
@ -692,7 +692,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": "<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-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra\" 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": "<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-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g\" 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>"
}
},
{
@ -706,7 +706,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": "<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-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra\" 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": "<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-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g\" 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>"
}
}
]
@ -856,7 +856,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj",
"integrity": "sha384-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU",
"elements": [
"regions-dashboard"
],
@ -876,7 +876,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU\" 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>"
}
},
{
@ -890,7 +890,7 @@
"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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj\" 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-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU\" 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>"
}
},
{
@ -904,7 +904,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU\" 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>"
}
}
]
@ -913,7 +913,7 @@
"bundle": "research-dashboard",
"scriptUrl": "./research-dashboard/research-dashboard.js",
"output": "doxa-maps/research-dashboard/research-dashboard.js",
"integrity": "sha384-VvOYWBcDd7NtnB1eveECKX88ZHIpGnOf79tVdCUqGZzXLgoZPc+oTTPIC3ajaYsn",
"integrity": "sha384-60D2NR4qeGMpN3QBUP2aTS2Ay90/Qqka2PiJQR12HoyewjpUeJXWDV3qihflLQGX",
"elements": [
"research-dashboard-map"
],
@ -929,7 +929,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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-VvOYWBcDd7NtnB1eveECKX88ZHIpGnOf79tVdCUqGZzXLgoZPc+oTTPIC3ajaYsn\" 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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-60D2NR4qeGMpN3QBUP2aTS2Ay90/Qqka2PiJQR12HoyewjpUeJXWDV3qihflLQGX\" 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"
}
@ -1027,7 +1027,7 @@
"bundle": "template-bundle",
"scriptUrl": "./template-bundle/template-bundle.js",
"output": "doxa-maps/template-bundle/template-bundle.js",
"integrity": "sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX",
"integrity": "sha384-6SGiWbRI14LXEIhcKTQ3rZPX2ZLKlxaBBZkrnWx4zNsgG5J0l2MxdeI8Fbf0O5R3",
"elements": [
"template-bundle"
],
@ -1043,7 +1043,7 @@
"scriptUrl": "./template-bundle/template-bundle.js",
"embed": {
"iframe": "<iframe src=\"./template-bundle/template-map-a/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=\"./template-bundle/template-bundle.js\" integrity=\"sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-6SGiWbRI14LXEIhcKTQ3rZPX2ZLKlxaBBZkrnWx4zNsgG5J0l2MxdeI8Fbf0O5R3\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
},
"bundle": "template-bundle"
},
@ -1058,7 +1058,7 @@
"scriptUrl": "./template-bundle/template-bundle.js",
"embed": {
"iframe": "<iframe src=\"./template-bundle/template-map-b/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=\"./template-bundle/template-bundle.js\" integrity=\"sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-6SGiWbRI14LXEIhcKTQ3rZPX2ZLKlxaBBZkrnWx4zNsgG5J0l2MxdeI8Fbf0O5R3\" crossorigin=\"anonymous\"></script>\n<template-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></template-bundle>"
},
"bundle": "template-bundle"
}
@ -1076,7 +1076,7 @@
{
"profile": "upg-neighbor",
"file": "upg-neighbor.vue",
"description": "Rescues the three pre-bundler experimental maps that live inside the legacy",
"description": "Rescues the five pre-bundler experimental maps that live inside the legacy",
"element": "upg-neighbor",
"type": "parameterized",
"isMap": false,

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 <my-upg-100-list-map> custom element. -->
<script src="../my-upg-100-list.js" integrity="sha384-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g" 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-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g" 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-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra" crossorigin="anonymous"></script>
<script src="../my-upg-100-list.js" integrity="sha384-Ej+g06Za6ycJVU7jiduRD2BxYmc5BVxj+CgbSilao4zU2CvutTxaX910/k6p033g" 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-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU" 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 <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU" 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 <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-GsQLqiHQdApXzRR0D9D/clAnmTpBaqMvRIiMCjcVzSC0etkqJ2IN2eA8vs+C56Xj" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-qSWSJjfPRgTvmbnURwFTIuIAHQwRzhqC/CvLS8L6sInh9LrP8ZWerPSAuyU9wXJU" 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-VvOYWBcDd7NtnB1eveECKX88ZHIpGnOf79tVdCUqGZzXLgoZPc+oTTPIC3ajaYsn" crossorigin="anonymous"></script>
<script src="../research-dashboard.js" integrity="sha384-60D2NR4qeGMpN3QBUP2aTS2Ay90/Qqka2PiJQR12HoyewjpUeJXWDV3qihflLQGX" 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 <template-bundle> custom element. -->
<script src="../template-bundle.js" integrity="sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX" crossorigin="anonymous"></script>
<script src="../template-bundle.js" integrity="sha384-6SGiWbRI14LXEIhcKTQ3rZPX2ZLKlxaBBZkrnWx4zNsgG5J0l2MxdeI8Fbf0O5R3" 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 <template-bundle> custom element. -->
<script src="../template-bundle.js" integrity="sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX" crossorigin="anonymous"></script>
<script src="../template-bundle.js" integrity="sha384-6SGiWbRI14LXEIhcKTQ3rZPX2ZLKlxaBBZkrnWx4zNsgG5J0l2MxdeI8Fbf0O5R3" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: