Update from source 26f952c9
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions

This commit is contained in:
Doxa Maps 2026-09-26 08:52:57 +00:00
parent 92abbb468b
commit ca98c1fcf9
43 changed files with 1233 additions and 194 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,290 @@
{
"meta": {
"title": "Who to contact — screened, fetched homepages for the organisations named in the credits",
"generated": "2026-09-26",
"grain": "one entry per organisation, matched to a credit string by the regexes it carries",
"verified_orgs": 16,
"orgs_that_did_not_answer": 0,
"states": {
"verified": "this script fetched it on the run that wrote this file — homepage 200, or robots.txt 200 where a bot challenge answers a datacentre IP",
"unknown": "did not answer this run; NOT shipped, and never read as broken"
},
"not_linked": [
{
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the agent's ruling, not this script's"
},
{
"reason": "doctrine not established from published material — left out rather than guessed"
}
],
"note": "Hand-screened allowlist. eBible's own homeDomain/publicationURL fields are deliberately NOT used: a refresh of someone else's catalogue must not be able to put a publisher on this map."
},
"held": [
{
"pattern": "bible society|biblical society|bibelselskab|bibelsellskap|soci(e|é)t(y|é) b[ií]blica|sociedad b[ií]blica|lembaga alkitab|united bible societies|sirk[əe]ti|bible societies",
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the agent's ruling, not this script's"
},
{
"pattern": "artists for israel|k[äa]rnbibeln|sanskritbible|gilakmedia|tarifit|gSungrab|audiobiblia",
"reason": "doctrine not established from published material — left out rather than guessed"
}
],
"orgs": {
"wycliffe-ca": {
"name": "Wycliffe Bible Translators of Canada",
"url": "https://www.wycliffe.ca/",
"why": "Bible translation agency, Canadian office of the Wycliffe Global Alliance.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 2084732 bytes",
"match": [
{
"s": "wycliffe[^,]*\\bcanada\\b",
"f": "i"
}
]
},
"wycliffe-au": {
"name": "Wycliffe Bible Translators Australia",
"url": "https://wycliffe.org.au/",
"why": "Bible translation agency, Australian office of the Wycliffe Global Alliance.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 87857 bytes",
"match": [
{
"s": "wycliffe[^,]*australian?\\b",
"f": "i"
}
]
},
"wycliffe": {
"name": "Wycliffe Bible Translators",
"url": "https://www.wycliffe.org/",
"why": "Bible translation agency; translates the 66-book canon, no added scripture.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 367138 bytes",
"match": [
{
"s": "\\bwycliffe\\b",
"f": "i"
}
]
},
"twftw": {
"name": "The Word for the World",
"url": "https://www.twftw.org/",
"why": "Bible translation agency working through national translators.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 1145398 bytes",
"match": [
{
"s": "word for the world",
"f": "i"
},
{
"s": "\\bTWFTW\\b",
"f": ""
}
]
},
"biblica": {
"name": "Biblica",
"url": "https://www.biblica.com/",
"why": "Bible publisher and translator (NIV); 66-book canon.",
"state": "verified",
"method": "robots-txt",
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 292 bytes",
"match": [
{
"s": "\\bbiblica\\b",
"f": "i"
}
]
},
"pioneer": {
"name": "Pioneer Bible Translators",
"url": "https://pioneerbible.org/",
"why": "Bible translation agency working in languages with no Scripture.",
"state": "verified",
"method": "robots-txt",
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 120 bytes",
"match": [
{
"s": "pioneer bible translators",
"f": "i"
},
{
"s": "traducteurs pionniers de la bible",
"f": "i"
},
{
"s": "\\bPBT\\b",
"f": ""
}
]
},
"seed": {
"name": "The Seed Company",
"url": "https://seedcompany.com/",
"why": "Bible translation agency, affiliate of Wycliffe USA.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 371615 bytes",
"match": [
{
"s": "seed company",
"f": "i"
}
]
},
"sim": {
"name": "SIM",
"url": "https://www.sim.org/",
"why": "Evangelical mission agency; translation among its field work.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 190863 bytes",
"match": [
{
"s": "\\bSIM\\b",
"f": ""
},
{
"s": "society for international ministries",
"f": "i"
}
]
},
"bible-league": {
"name": "Bible League International",
"url": "https://www.bibleleague.org/",
"why": "Scripture publisher; absorbed the World Bible Translation Center, whose name the credits still carry.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 701341 bytes",
"match": [
{
"s": "world bible translation cent(er|re)",
"f": "i"
},
{
"s": "bible league",
"f": "i"
},
{
"s": "liga b[ií]blica",
"f": "i"
}
]
},
"unfoldingword": {
"name": "Door43 / unfoldingWord",
"url": "https://door43.org/",
"why": "Open-licensed Bible translation resources; the Door43 community platform.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 7465 bytes",
"match": [
{
"s": "door\\s?43",
"f": "i"
},
{
"s": "unfoldingword",
"f": "i"
}
]
},
"bridgeconn": {
"name": "Bridge Connectivity Solutions",
"url": "https://www.bridgeconn.com/",
"why": "Indian Bible-translation technology and translation services group.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 20334 bytes",
"match": [
{
"s": "bridge conn?ectivity",
"f": "i"
},
{
"s": "bridge communication systems",
"f": "i"
}
]
},
"btl-kenya": {
"name": "Bible Translation and Literacy",
"url": "https://www.btlkenya.org/",
"why": "Kenyan Bible translation agency, Wycliffe Global Alliance member.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 76225 bytes",
"match": [
{
"s": "bible translation and literacy",
"f": "i"
}
]
},
"kartidaya": {
"name": "Yayasan Kartidaya",
"url": "https://kartidaya.org/",
"why": "Indonesian Bible translation agency, Wycliffe Global Alliance member.",
"state": "verified",
"method": "robots-txt",
"detail": "homepage HTTP 429 (bot challenge from a datacentre IP), robots.txt HTTP 200, 319 bytes",
"match": [
{
"s": "kartidaya",
"f": "i"
}
]
},
"liebenzell": {
"name": "Liebenzell Mission",
"url": "https://www.liebenzell.org/",
"why": "German evangelical mission agency (Free Church tradition).",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 1281367 bytes",
"match": [
{
"s": "liebenzel+l? mission",
"f": "i"
}
]
},
"sil": {
"name": "SIL Global",
"url": "https://www.sil.org/",
"why": "Language development organisation; the linguistic partner behind much of this corpus.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 415008 bytes",
"match": [
{
"s": "\\bSIL\\b",
"f": ""
}
]
},
"lockman": {
"name": "The Lockman Foundation",
"url": "https://www.lockman.org/",
"why": "Scripture publisher (NASB, LBLA); 66-book canon.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 163046 bytes",
"match": [
{
"s": "lockman foundation",
"f": "i"
}
]
}
}
}

View file

@ -0,0 +1,82 @@
/**
* check-translator-contacts.mjs — the credit line now LINKS the translating organisation, and
* this is the gate on that link. Static, sub-second, no browser.
*
* WHAT IT WOULD CATCH. A contact URL is a link this map stands behind, so it has to clear the
* same bar as a resource link and one more besides:
* 1. Every cr.u in the pin index is on an organisation the DOCTRINE SCREEN lists. The screen's
* pattern is read out of internal/verify-gospel-map.mjs, never re-typed here — two copies of
* an allowlist drift, and the copy that drifts is the one nobody is looking at.
* 2. Every cr.u was actually FETCHED: it must appear in translator-org-links.json as `verified`.
* An org the verifier could not reach must stay a name.
* 3. No contact host is also serving a RESOURCE. wycliffe.org is a fine place to write to and
* is not a publisher this map reads text from; if one ever appears as a scripture or film
* link, that is a new publisher arriving sideways.
* 4. The leaf RENDERS it as a link. The data can carry 838 URLs while the template shows none.
* Self-test: `--poison` injects a JW contact, an unverified org, and a leaf with the anchor
* removed, and every arm must fail.
*/
import { readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = resolve(HERE, '..')
const POISON = process.argv.includes('--poison')
const idx = JSON.parse(readFileSync(resolve(REPO, 'data/tables/people-group-pins.json'), 'utf8'))
const orgs = JSON.parse(readFileSync(resolve(REPO, 'data/tables/translator-org-links.json'), 'utf8')).orgs || {}
const gate = readFileSync(resolve(REPO, 'internal/verify-gospel-map.mjs'), 'utf8')
let leaf = readFileSync(resolve(REPO, 'plugin-designs/gospel-resources/resources-map.vue'), 'utf8')
/* The screen, lifted from the gate rather than restated. */
const m = /^const ALLOWED_CONTACT = (\/.+\/i)$/m.exec(gate)
if (!m) { console.error('TRANSLATOR-CONTACTS: verify-gospel-map.mjs no longer declares ALLOWED_CONTACT — refusing to invent one.'); process.exit(1) }
const CONTACT = new RegExp(m[1].slice(1, -2), 'i')
const RESOURCE = /^const ALLOWED = (\/.+\/i)$/m.exec(gate)
const ALLOWED = new RegExp(RESOURCE[1].slice(1, -2), 'i')
const codes = { ...(idx.codes || {}) }
if (POISON) {
codes.__p1 = { cr: { o: 'Watchtower', u: 'https://www.jw.org/' } }
codes.__p2 = { cr: { o: 'Some Org', u: 'https://this-was-never-fetched.example/' } }
codes.__p3 = { s: 'https://www.wycliffe.org/', cr: { o: 'x' } }
leaf = leaf.replace(/class="gm-org"/g, 'class="gm-plain"')
}
const verified = new Set(Object.values(orgs).filter((o) => o.state === 'verified').map((o) => o.url))
const fails = []
let contacts = 0
const contactHosts = new Set()
for (const [c, v] of Object.entries(codes)) {
const u = v.cr && v.cr.u
if (u) {
contacts++
contactHosts.add(new URL(u).host.replace(/^www\./, ''))
if (!CONTACT.test(u)) fails.push(`${c}: contact ${u} is not on the screened organisation list`)
if (!verified.has(u)) fails.push(`${c}: contact ${u} is not recorded as verified in translator-org-links.json`)
}
}
for (const [c, v] of Object.entries(codes)) {
for (const u of [v.s, v.f, v.z, v.au && v.au.u, v.bv && v.bv.u, v.ga && v.ga.u, v.se && (v.se.u || v.se)]) {
if (!u) continue
if (CONTACT.test(u) && !ALLOWED.test(u)) fails.push(`${c}: ${u} is a CONTACT host serving a resource link`)
}
}
if (!contacts) fails.push('no contact URL in the index at all — this check would pass vacuously')
if (!/class="gm-org"/.test(leaf)) fails.push('resources-map.vue renders no .gm-org anchor — the data carries contacts the sheet never shows')
if (!/credit\.u/.test(leaf)) fails.push('resources-map.vue never reads credit.u')
if (POISON) {
const want = ['not on the screened organisation list', 'not recorded as verified', 'CONTACT host serving a resource link', 'renders no .gm-org anchor']
const missed = want.filter((w) => !fails.some((f) => f.includes(w)))
if (missed.length) { console.error(`TRANSLATOR-CONTACTS --poison: these arms did NOT fire: ${missed.join(' | ')}`); process.exit(1) }
console.log(`TRANSLATOR-CONTACTS --poison: PASS — all 4 arms fired (${fails.length} failure(s)).`)
process.exit(0)
}
if (fails.length) {
console.error(`TRANSLATOR-CONTACTS: FAIL — ${fails.length} problem(s)`)
for (const f of fails.slice(0, 12)) console.error(` ${f}`)
process.exit(1)
}
console.log(`TRANSLATOR-CONTACTS: PASS — ${contacts} credit(s) link an organisation, across ${contactHosts.size} screened host(s); each one verified by fetch and rendered as a link.`)

View file

@ -64,6 +64,15 @@ const GL = ['--use-gl=swiftshader', '--enable-unsafe-swiftshader', '--ignore-gpu
// nothing about the links nobody looks at.
const ALLOWED = /^(https:\/\/)?([a-z0-9-]+\.)*(ebible\.org|png\.bible|alkitab\.pw|git\.door43\.org|jesusfilm\.org|arc\.gt|zume\.training|bible\.is|knowgod\.com|sun\.bible|pray\.doxa\.life|fivefish\.org|scriptureearth\.org)(\/|$)/i
/* ★ A CONTACT IS NOT A PUBLISHER, AND THE TWO LISTS MUST NOT MERGE (2026-09-26). The credit line
now links the translating organisation, so cr.u is a FIFTH place a URL lives in the index — the
omission class this file already warns about, caught on the build that introduced it. These
hosts are screened as organisations a mobiliser may CONTACT, not as publishers whose text this
map serves, and they are spelled out here rather than read from the data file they came from:
a screen that trusts the file it is screening is not a screen. A contact host is therefore
still a failure if it ever appears as a resource link, because ALLOWED does not list it. */
const ALLOWED_CONTACT = /^https:\/\/([a-z0-9-]+\.)*(wycliffe\.org|wycliffe\.ca|wycliffe\.org\.au|twftw\.org|biblica\.com|pioneerbible\.org|seedcompany\.com|sim\.org|bibleleague\.org|door43\.org|bridgeconn\.com|btlkenya\.org|kartidaya\.org|liebenzell\.org|sil\.org|lockman\.org)\/?$/i
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = resolve(HERE, '..')
const EXPLICIT = !!(process.env.BASE_URL || process.argv[2])
@ -122,6 +131,7 @@ const PAGE_URL = `${base}/${TREE_NAME}/gospel-resources/resources-map/index.html
const fails = []
let linkCount = 0
let contactCount = 0
let exitCode = 0
const browser = await chromium.launch({ headless: true, args: GL })
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } })
@ -201,8 +211,8 @@ try {
if (!idxRes.ok) fails.push(`pin index not served (${idxRes.status})`)
else {
const idx = await idxRes.json()
const bad = []
let scanned = 0
const bad = [], contactBad = []
let scanned = 0, contacts = 0
for (const [c, v] of Object.entries(idx.codes || {})) {
/* ⚠ ga (Words of Life) and se (Scripture Earth) were absent from this list, so every link
from those two pools went unscreened — the doctrine boundary is the one thing that must
@ -213,6 +223,13 @@ try {
scanned++
if (!ALLOWED.test(u)) bad.push(`${c} -> ${u}`)
}
/* The translator contact, screened against the OTHER list. Counted apart so "every link is
on a screened publisher" keeps meaning exactly that. */
const cu = v.cr && v.cr.u
if (cu) {
contacts++
if (!ALLOWED_CONTACT.test(cu)) contactBad.push(`${c} -> ${cu}`)
}
}
/* ★ THE SUN BLOCK IS A THIRD PLACE URLS LIVE (2026-09-25). The Deaf bridge is carried as a
single top-level object rather than per-language under codes[], because SUN is one signed
@ -225,6 +242,8 @@ try {
}
if (!scanned) fails.push('pin index carries no links at all — this check would pass vacuously')
if (bad.length) fails.push(`${bad.length} link(s) on an unscreened publisher (e.g. ${bad[0]})`)
if (contactBad.length) fails.push(`${contactBad.length} translator contact(s) on an unscreened organisation (e.g. ${contactBad[0]})`)
contactCount = contacts
linkCount = scanned
// ★ THE ACCESS TABLE IS THE OTHER HALF, AND IT WAS NEVER SCREENED (2026-09-25).
@ -319,6 +338,7 @@ try {
} else {
console.log(`GOSPEL-MAP: PASS — ${st.pins} pins rendered; "${opened.pg}" (${opened.lang}) opens with`)
console.log(` a named language; all ${linkCount} links in the index are on screened publishers;`)
console.log(` ${contactCount} translator contact(s) are on screened organisations;`)
console.log(` the table link carries iso=${opened.iso}; the gap filter`)
console.log(` cuts ${filtered.before} features to ${filtered.after}, all tier 0.`)
}

View file

@ -86,6 +86,8 @@
"check:gallery-embed:maplibre": "node internal/check-research-gallery-embed.mjs --provider maplibre",
"check:gospel-all": "node internal/check-gospel-all.mjs",
"check:gospel-tables-staged": "node internal/check-gospel-tables-staged.mjs",
"check:translator-contacts": "node internal/check-translator-contacts.mjs",
"check:translator-contacts:poison": "node internal/check-translator-contacts.mjs --poison",
"check:heartbeat": "node internal/check-heartbeat-threshold.mjs",
"check:heartbeat:self-test": "node internal/check-heartbeat-threshold.mjs --self-test",
"check:host-locale-contract": "node internal/check-host-locale-contract.mjs",
@ -351,6 +353,7 @@
"check:shot-fidelity": "warns when THIS machine cannot RENDER what the shots must SHOW. `bun run shots` printed 13x OK while every emoji rendered as a tofu box — a glyph fallback loads perfectly (0 console errors, 0 HTTP>=400), so the generator cannot see it. Reports by default; the `check:shot-fidelity:strict` script to block once an emoji font is installed.",
"check:shot-fonts": "can THIS machine render the emoji the UI uses? Run BEFORE `bun run shots` — no font = tofu boxes in every committed PNG, which every other shot gate passes. Deliberately NOT in build:unlocked: it reports an ENVIRONMENT condition (this box has no emoji font), and a gate that always fails in the build makes the build useless.",
"check:shots": "a README screenshot that predates the bundle it shows is a picture telling a lie — and it looks IDENTICAL to a current one. Compares each shot's PNG against the BUILT BUNDLE it renders (not raw sources: an unbuilt edit is not yet in any leaf). Fix: node internal/readme-shots.mjs",
"check:translator-contacts": "the credit line LINKS the translating organisation now, and a contact URL is a link this map stands behind. Reads ALLOWED_CONTACT out of internal/verify-gospel-map.mjs rather than restating it (two copies of an allowlist drift, and the drifting copy is the one nobody reads), then asserts every cr.u is on that list, was recorded `verified` by a real fetch, is never also serving a RESOURCE (wycliffe.org is somewhere to write to, not a publisher this map reads text from), and is actually RENDERED as an anchor \u2014 the data can carry 838 URLs while the template shows none. Static, sub-second. Self-test: `bun run check:translator-contacts:poison`.",
"check:snapshot-guard": "every gospel-resources catalogue fetcher must write through scripts/_snapshot-guard.mjs, which refuses a write that loses more than a tenth of the previous snapshot. Written after a rate-limited FCBH run silently replaced 1,666 video scopes with 1,103. Static, sub-second. Self-test: `bun run check:snapshot-guard:poison` injects a raw write and the check must fail on it.",
"check:spa-tab-order": "the cap-5 SPA must open on Big Picture and keep the agent's order (Big Picture, UPG-100, Prayer Progress, Gospel Resources) — asserted as PRESENCE + tab-1 + RELATIVE order on the RENDERED rail, because the rail gained three sections and now renders 12 rows with others between them. ⚠ NOT WAVE-WIRED: it FAILS rather than skips when the SPA is unreachable, deliberately (\"a gate that cannot reach its subject has not passed, it has not run\") — correct standalone, but gates:post runs on checkouts with no server. Needs `bun run serve` on 4173.",
"check:stale-sweep-claim": "a caveat that has stopped being true is a wrong number. The gospel surfaces carry a paragraph saying which pools were searched; while the GRN sweep ran they truthfully said part of it was unread, the sweep finished, and the sentence stayed for four more commits. The DATA decides here: undecided gap codes mean the claim is allowed, all-decided means it must go, so it re-arms itself for the next slow pool. Does NOT flag a claim rendered under a v-if on the pending count — that form expires on its own. Self-test: `--poison`. Static, sub-second.",

View file

@ -2168,10 +2168,39 @@ function emitFilterState() {
// Report in the vocabulary a HOST speaks: the family NAME, not the legend's letter key. Same
// shared RELIGION_FAMILIES table the inbound direction reads, used the other way round.
const religionName = RELIGION_FAMILIES[uiStore?.religionFilter] || ''
// ★ LANGUAGE TRAVELS AS A CODE AS WELL AS A NAME, because the NAME moves with the locale.
// MEASURED against the live API: primary_language's LABEL is fully localized (864 names in en,
// 864 in ar, ZERO shared) while its VALUE is the stable ISO 639-3 code (864 shared, identical).
// Region already links on a slug and religion on a family letter; language was the one facet
// still linking on a display name, so the moment the grid's data followed the URL's language the
// link would have matched nothing in Arabic — silently, because an empty intersection looks like
// "no results" rather than a broken wire.
//
// This tree has paid for exactly this once before: deriveLanguageFamily in useMapData carries a
// comment about the 2026-06-22 locale colour bug, where deriving a family from the localized
// label collapsed every pin onto one bucket. Same trap, one facet over.
//
// The NAME still goes out because this map's own legend selects by name; the CODE goes out for
// anyone matching rows. Both halves get the form they can actually use.
const langName = mapStore?.selectedLanguage || ''
// Read the records here rather than assuming a `rows` in scope — check:no-free-identifiers caught
// exactly that on its first outing against this very edit, which is the class of bug it is for.
const langRows = mapData?.normalizedPeopleGroups?.value || []
const langCode = langName
? String(
(langRows.find((r) => {
const l = r && (r.language ?? r.primary_language)
const lab = l && typeof l === 'object' ? l.label : l
return String(lab || '').trim() === String(langName).trim()
}) || {})._raw?.primary_language?.value || ''
).trim()
: ''
const detail = {
region: mapStore?.selectedRegion || '',
religion: religionName,
language: mapStore?.selectedLanguage || '',
language: langName,
languageCode: langCode,
}
host.dispatchEvent(new CustomEvent('map-filtered', { detail, bubbles: true, composed: true }))
}

View file

@ -90,6 +90,12 @@ const TABS = [
// Ranked languages per country joined to resources — the bridge-language view.
{ id: 'languages', labelKey: 'gospel.tables.t_languages', file: 'languages-by-country' },
{ id: 'growth', labelKey: 'gospel.tables.t_growth', file: 'gospel-access-vs-growth' },
// SUB-NATIONAL, and the only tab at a grain smaller than a country. India and China hold
// most of the world's zero-access people between them, and "India" is not a place anyone is
// sent — a state is. Every row says placement is APPROXIMATE: a group is assigned to the
// province its map point falls in, and a point is one dot for a people that may live across
// several. It is the same file the map's province sheet reads, so the two cannot disagree.
{ id: 'province', labelKey: 'gospel.tables.t_province', file: 'gospel-by-province' },
]
// DEEP-LINK FILTER. The map and this table are separate PAGES, so "click a pin and the
// table follows" is a URL, not shared state. The map links here with ?tab=links&iso=<code>

View file

@ -1661,6 +1661,12 @@ useShadowStyles(
the way in is the footnote to it, not the headline. */
'.gm-pg-way{display:block;font-size:12.5px;color:#3b463d;opacity:.8;margin-top:2px}' +
'.gm-credit{font-size:12.5px;color:#6d766e;line-height:1.35}' +
/* The contact is a LINK inside a grey credit line, so it carries the ink colour and an
underline of its own — grey-on-grey would hide the one part of the sentence a
mobiliser is meant to act on. */
'.gm-org{color:#3b463d;text-decoration:underline;text-underline-offset:2px}' +
'.gm-org:hover{color:#2c352e}' +
'.is-dark .gm-org{color:#f3f3f1}' +
'.is-dark .gm-credit{color:#d1d4cd}' +
'.is-dark .gm-pg-way{color:#d1d4cd}' +
'.gm-prose-note{opacity:.9;font-weight:600}' +
@ -2374,7 +2380,9 @@ useShadowStyles(
{{ t('gospel.sheet.everyResource') }} {{ curatedBridgeRes.language }} →</a>
</div>
<p v-if="curatedBridgeRes && curatedBridgeRes.credit" class="gm-credit" dir="ltr" style="margin:4px 0 0">
The {{ curatedBridgeRes.language }} Scripture was translated by <b>{{ curatedBridgeRes.credit.o }}</b><template
The {{ curatedBridgeRes.language }} Scripture was translated by <a v-if="curatedBridgeRes.credit.u"
class="gm-org" :href="curatedBridgeRes.credit.u" target="_blank" rel="noopener noreferrer"><b>{{
curatedBridgeRes.credit.o }}</b></a><b v-else>{{ curatedBridgeRes.credit.o }}</b><template
v-if="curatedBridgeRes.credit.y">, {{ curatedBridgeRes.credit.y }}</template> — the likeliest people to ask
about this language next.
</p>
@ -2463,7 +2471,9 @@ useShadowStyles(
{{ t('gospel.sheet.everyResource') }} {{ candidateBridgeRes.language }} →</a>
</div>
<p v-if="candidateBridgeRes && candidateBridgeRes.credit" class="gm-credit" dir="ltr" style="margin:4px 0 0">
The {{ candidateBridgeRes.language }} Scripture was translated by <b>{{ candidateBridgeRes.credit.o }}</b><template
The {{ candidateBridgeRes.language }} Scripture was translated by <a v-if="candidateBridgeRes.credit.u"
class="gm-org" :href="candidateBridgeRes.credit.u" target="_blank" rel="noopener noreferrer"><b>{{
candidateBridgeRes.credit.o }}</b></a><b v-else>{{ candidateBridgeRes.credit.o }}</b><template
v-if="candidateBridgeRes.credit.y">, {{ candidateBridgeRes.credit.y }}</template> — the likeliest people to ask
about this language next.
</p>
@ -2475,12 +2485,15 @@ useShadowStyles(
<!-- ═══ WHO TO ASK, NOT JUST WHERE TO LOOK ══════════════════════════════════════
A link answers where the Scripture is. The organisation that produced it is
the one with the linguists, the relationships and very often a team already in
that country — which is what a mobiliser actually needs next. A NAME and not a
link: the catalogue carries the publisher's domain too, and shipping it would
put a host into this map that the doctrine screen has never seen. -->
that country — which is what a mobiliser actually needs next, so the name is a
LINK where one exists. Not eBible's own homeDomain field, which is still dropped:
the URL comes from data/tables/translator-org-links.json, a hand-screened list
whose every entry the build fetched. An org with no screened URL stays a name. -->
<template v-if="selectedGroup.credit">
<br><span class="gm-credit" dir="ltr">Translated by {{ selectedGroup.credit.o
}}<template v-if="selectedGroup.credit.y">, {{ selectedGroup.credit.y }}</template></span>
<br><span class="gm-credit" dir="ltr">Translated by <a v-if="selectedGroup.credit.u"
class="gm-org" :href="selectedGroup.credit.u" target="_blank" rel="noopener noreferrer">{{
selectedGroup.credit.o }}</a><template v-else>{{ selectedGroup.credit.o }}</template><template
v-if="selectedGroup.credit.y">, {{ selectedGroup.credit.y }}</template></span>
</template>
</p>

View file

@ -990,17 +990,47 @@ try {
} catch { /* no SUN index in this tree: the Deaf panel stays as it was */ }
/**
* WHO TRANSLATED IT. Attribution for Scripture already in the index — a name, never a new link,
* so no publisher can enter this map through a data refresh without passing the doctrine screen.
* It adds no resource and moves no tier; note it is attached AFTER tierOfCode is defined and
* BEFORE nothing, precisely because it must not participate in tiering.
* WHO TRANSLATED IT — AND HOW TO REACH THEM. Attribution for Scripture already in the index. The
* NAME still comes from eBible exactly as eBible wrote it; the CONTACT URL comes only from
* data/tables/translator-org-links.json, a hand-screened allowlist whose every entry this build
* fetched. eBible's own homeDomain field is still not used, so no publisher can enter this map
* through a data refresh without passing the doctrine screen.
* It adds no resource and moves no tier; it is attached AFTER tierOfCode is defined precisely
* because it must not participate in tiering.
*/
let credited = 0
let credited = 0, contactable = 0
let orgLinks = {}
try {
const t = JSON.parse(readFileSync(resolve(REPO, 'data/tables/translator-org-links.json'), 'utf8'))
for (const [id, o] of Object.entries(t.orgs || {})) {
/* Only what the verifier actually reached this run. `unknown` is not shipped and is not an
error either — the credit simply keeps naming the org without a link. */
if (o.state !== 'verified') continue
orgLinks[id] = { url: o.url, match: (o.match || []).map((m) => new RegExp(m.s, m.f || '')) }
}
} catch { /* no org table: credits stay names, which is what they were before */ }
/* THE EARLIEST NAME IN THE STRING WINS, not the first pattern in the file. "Pioneer Bible
Translators and The Word for the World International" is led by Pioneer, and a credit's lead
organisation is the one to ask; ordering by the allowlist instead would have answered TWFTW. */
const orgFor = (credit) => {
let best = null
for (const [id, o] of Object.entries(orgLinks)) {
for (const re of o.match) {
const m = re.exec(credit)
if (m && (!best || m.index < best.at)) best = { id, url: o.url, at: m.index }
}
}
return best
}
try {
const cr = JSON.parse(readFileSync(resolve(REPO, 'data/tables/translation-credits.json'), 'utf8')).byCode || {}
for (const [code, v] of Object.entries(cr)) {
if (!codes[code] || !v.org) continue
codes[code].cr = v.year ? { o: v.org, y: v.year } : { o: v.org }
const org = orgFor(v.org)
if (org) { codes[code].cr.u = org.url; codes[code].cr.g = org.id; contactable++ }
credited++
}
} catch { /* no credits table: the sheet simply does not name a translator */ }
@ -1041,7 +1071,7 @@ console.log(`arclight codes ${Object.keys(arc).length} (${arcOnly} new, ${upgr
console.log(`share urls ${Object.keys(share).length}`)
console.log(`fcbh codes ${audioCodes} (${audioNew} new codes)`)
console.log(`gospel tracts ${gospelCodes} (Cru/GodTools presentations; depth only, tier UNCHANGED)`)
console.log(`translators named ${credited}\t(attribution only; no new link, no tier change)`)
console.log(`translators named ${credited}\t(${contactable} reach a screened, fetched organisation homepage; no tier change)`)
console.log(`tiers published ${Object.keys(tiers).length}\t(one per code, so a language with no pin still has a tier)`)
console.log(`bridge candidates ${Object.keys(bridgeCandidates).length} (computed here so the map sheet and the access table cannot disagree)`)
console.log(`in-country bridge ${bridgeRows} (groups with NOTHING of their own; a widely-spoken language around them has something — never counted as theirs)`)

View file

@ -26,6 +26,7 @@
* country on no authority at all.
*/
import { writeFileSync } from 'node:fs'
import { writeSnapshot } from './_snapshot-guard.mjs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
@ -104,7 +105,9 @@ if (countries < 150) {
console.error(' A thin file would silently narrow every bridge recommendation that reads it.')
process.exitCode = 1
} else {
writeFileSync(OUT, `${JSON.stringify({
/* THROUGH THE SNAPSHOT GUARD. The 150-country floor above catches a collapse; it does not
catch the run that parses 240 countries and loses a fifth of them. */
const body = `${JSON.stringify({
meta: {
title: 'Languages spoken per country, with each share of the country',
sources: [CLDR, ISO],
@ -117,7 +120,8 @@ if (countries < 150) {
territories_dropped_no_alpha3: noAlpha3,
},
byCountry,
}, null, 1)}\n`)
}, null, 1)}\n`
writeSnapshot(OUT, body, (j) => Object.keys(j.byCountry || {}).length, 'country(ies)')
console.log(`country-languages: ${countries} countries (keyed by ISO3), ${dropped} CLDR tag(s) dropped for having no ISO 639-3, ${noAlpha3} territory(ies) with no alpha-3`)
console.log(`wrote ${OUT}`)
}

View file

@ -21,6 +21,7 @@
* It adds no resource and moves no tier. It is attribution for Scripture already in the index.
*/
import { writeFileSync, readFileSync } from 'node:fs'
import { writeSnapshot } from './_snapshot-guard.mjs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
@ -113,7 +114,9 @@ if (!joined) {
console.error('CREDITS: REFUSING TO WRITE — nothing joined; the URL shape or the catalogue changed.')
process.exitCode = 1
} else {
writeFileSync(OUT, `${JSON.stringify({
/* THROUGH THE SNAPSHOT GUARD. The refusal above fires only when NOTHING joins. A catalogue
fetch that half-succeeds joins some, and would take the translator's name off the rest. */
const body = `${JSON.stringify({
meta: {
title: 'Who translated it — attribution for the Scripture this map links to',
source: SRC,
@ -128,7 +131,8 @@ if (!joined) {
'doctrine screen has never seen. Adds no resource and moves no tier.',
},
byCode: used,
}, null, 1)}\n`)
}, null, 1)}\n`
writeSnapshot(OUT, body, (j) => Object.keys(j.byCode || {}).length, 'credited code(s)')
console.log(`translation-credits: ${joined} code(s) with a named translator, ${licenceOnly} whose copyright is a licence, ${noRow} with no catalogue row, from ${raw.length - 1} rows`)
console.log(`wrote ${OUT}`)
}

View file

@ -40,6 +40,7 @@
* asl.bible, 4.bible do not resolve.
*/
import { writeFileSync } from 'node:fs'
import { writeSnapshot } from './_snapshot-guard.mjs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
@ -116,7 +117,9 @@ if (verified.length < 3 || !projects.length) {
console.error(' Shipping a thin index would quietly shrink the only bridge the Deaf gap has.')
process.exitCode = 1
} else {
writeFileSync(OUT, `${JSON.stringify({
/* THROUGH THE SNAPSHOT GUARD. The floor above is 3 verified pages, which a rate-limited run
can clear while losing most of the projects — and this file is the Deaf gap's only bridge. */
const body = `${JSON.stringify({
meta: {
title: 'SUN — a universal signed language with Scripture, for Deaf groups with nothing',
source: 'sun.bible, fetched and verified by content',
@ -138,7 +141,8 @@ if (verified.length < 3 || !projects.length) {
},
resources: verified.map(({ key, url, label, title, bytes }) => ({ key, url, label, title, bytes })),
projects,
}, null, 1)}\n`)
}, null, 1)}\n`
writeSnapshot(OUT, body, (j) => (j.resources || []).length + (j.projects || []).length, 'SUN page(s) and project country(ies)')
console.log(`SUN: ${verified.length} verified page(s), ${projects.length} project country(ies)`)
console.log(`wrote ${OUT}`)
}

View file

@ -0,0 +1,157 @@
/**
* verify-translator-org-links.mjs — turn the translator CREDIT into someone a mobiliser can reach.
*
* WHY. fetch-ebible-credits.mjs already answers "who translated this" and the sheet already says
* those are "the likeliest people to ask" — but it names them and stops. A name is not a contact.
* The mobiliser reading a zero-access group is one step from the organisation that has linguists,
* relationships and very often a field team in that country, and the map makes them go and find it.
*
* ⚠ WHY THIS IS A HAND-WRITTEN ALLOWLIST AND NOT THE CATALOGUE'S OWN FIELD. eBible ships
* homeDomain and publicationURL on every row, and fetch-ebible-credits.mjs deliberately drops
* both: a refresh of somebody else's catalogue must never be able to put a new publisher on this
* map, because the doctrine screen is an allowlist for exactly that reason. So the URL comes from
* here, where every entry was screened by hand, or it does not ship at all.
*
* ⚠ WHAT IS DELIBERATELY ABSENT. National Bible Societies hold 89+ of the credits and NONE of
* them is linked here. Not doubt about the Societies — the brief's filter is "only the 66-book
* canon", and several UBS members publish editions that include the deuterocanonicals, so
* whether a Society CONTACT is inside or outside that line is the agent's ruling to make and not
* this script's. They stay named-without-a-link until it is made, and the count is reported so the
* gap is visible rather than quiet. Same for the orgs whose doctrine I could not establish: they
* are listed in `unscreened` with the reason, never linked.
*
* ⚠ VERIFIED MEANS FETCHED. Every URL below was fetched by this script on the run that wrote the
* table. Several of these hosts sit behind a bot challenge that answers a datacentre IP with 403
* or 429 while serving a real site to a browser; for those the check falls to robots.txt, which
* those same hosts answer, and the state records WHICH method proved it. Both controls run in band
* and the script refuses to write if either misbehaves.
*/
import { writeSnapshot } from './_snapshot-guard.mjs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = resolve(HERE, '../../..')
const OUT = resolve(REPO, 'data/tables/translator-org-links.json')
const UA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36'
/* ── THE ALLOWLIST ────────────────────────────────────────────────────────────────────────────
`match` is tested against the credit string as eBible wrote it, in order, first hit wins — so
the country arms of a family come BEFORE the family ("Wycliffe Bible Translators of Canada"
must not resolve to the US office). `why` is the doctrine note: what this organisation is, in
one line, so the screen can be re-read by a human without leaving the file. */
const ORGS = [
{ id: 'wycliffe-ca', name: 'Wycliffe Bible Translators of Canada', url: 'https://www.wycliffe.ca/',
match: [/wycliffe[^,]*\bcanada\b/i], why: 'Bible translation agency, Canadian office of the Wycliffe Global Alliance.' },
{ id: 'wycliffe-au', name: 'Wycliffe Bible Translators Australia', url: 'https://wycliffe.org.au/',
match: [/wycliffe[^,]*australian?\b/i], why: 'Bible translation agency, Australian office of the Wycliffe Global Alliance.' },
{ id: 'wycliffe', name: 'Wycliffe Bible Translators', url: 'https://www.wycliffe.org/',
match: [/\bwycliffe\b/i], why: 'Bible translation agency; translates the 66-book canon, no added scripture.' },
{ id: 'twftw', name: 'The Word for the World', url: 'https://www.twftw.org/',
match: [/word for the world/i, /\bTWFTW\b/], why: 'Bible translation agency working through national translators.' },
{ id: 'biblica', name: 'Biblica', url: 'https://www.biblica.com/',
match: [/\bbiblica\b/i], why: 'Bible publisher and translator (NIV); 66-book canon.' },
{ id: 'pioneer', name: 'Pioneer Bible Translators', url: 'https://pioneerbible.org/',
match: [/pioneer bible translators/i, /traducteurs pionniers de la bible/i, /\bPBT\b/],
why: 'Bible translation agency working in languages with no Scripture.' },
{ id: 'seed', name: 'The Seed Company', url: 'https://seedcompany.com/',
match: [/seed company/i], why: 'Bible translation agency, affiliate of Wycliffe USA.' },
{ id: 'sim', name: 'SIM', url: 'https://www.sim.org/',
match: [/\bSIM\b/, /society for international ministries/i], why: 'Evangelical mission agency; translation among its field work.' },
{ id: 'bible-league', name: 'Bible League International', url: 'https://www.bibleleague.org/',
match: [/world bible translation cent(er|re)/i, /bible league/i, /liga b[ií]blica/i],
why: 'Scripture publisher; absorbed the World Bible Translation Center, whose name the credits still carry.' },
/* The credit strings name Door43 itself ("Door43 World Missions Community"), and door43.org
answers a plain GET while unfoldingword.org rate-limits a datacentre IP — so the link goes
to the platform the credit actually names. */
{ id: 'unfoldingword', name: 'Door43 / unfoldingWord', url: 'https://door43.org/',
match: [/door\s?43/i, /unfoldingword/i], why: 'Open-licensed Bible translation resources; the Door43 community platform.' },
{ id: 'bridgeconn', name: 'Bridge Connectivity Solutions', url: 'https://www.bridgeconn.com/',
match: [/bridge conn?ectivity/i, /bridge communication systems/i],
why: 'Indian Bible-translation technology and translation services group.' },
{ id: 'btl-kenya', name: 'Bible Translation and Literacy', url: 'https://www.btlkenya.org/',
match: [/bible translation and literacy/i], why: 'Kenyan Bible translation agency, Wycliffe Global Alliance member.' },
{ id: 'kartidaya', name: 'Yayasan Kartidaya', url: 'https://kartidaya.org/',
match: [/kartidaya/i], why: 'Indonesian Bible translation agency, Wycliffe Global Alliance member.' },
{ id: 'liebenzell', name: 'Liebenzell Mission', url: 'https://www.liebenzell.org/',
match: [/liebenzel+l? mission/i], why: 'German evangelical mission agency (Free Church tradition).' },
{ id: 'sil', name: 'SIL Global', url: 'https://www.sil.org/',
match: [/\bSIL\b/], why: 'Language development organisation; the linguistic partner behind much of this corpus.' },
{ id: 'lockman', name: 'The Lockman Foundation', url: 'https://www.lockman.org/',
match: [/lockman foundation/i], why: 'Scripture publisher (NASB, LBLA); 66-book canon.' },
]
/* Credits that are NOT linked, and why. Counted in the table so the silence is legible. */
const HELD = [
{ pattern: /bible society|biblical society|bibelselskab|bibelsellskap|soci(e|é)t(y|é) b[ií]blica|sociedad b[ií]blica|lembaga alkitab|united bible societies|sirk[əe]ti|bible societies/i,
reason: 'national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the agent\'s ruling, not this script\'s' },
{ pattern: /artists for israel|k[äa]rnbibeln|sanskritbible|gilakmedia|tarifit|gSungrab|audiobiblia/i,
reason: 'doctrine not established from published material — left out rather than guessed' },
]
const fetchOnce = async (url) => {
try {
const r = await fetch(url, { headers: { 'user-agent': UA, accept: 'text/html,*/*' }, redirect: 'follow', signal: AbortSignal.timeout(25000) })
const body = await r.text()
return { status: r.status, final: r.url, bytes: body.length, body }
} catch (e) { return { status: 0, final: url, bytes: 0, body: '', error: String(e.message || e) } }
}
const isRobots = (b) => /(^|\n)\s*(user-agent|sitemap)\s*:/i.test(b) && b.length < 20000
/** Homepage first. If a bot challenge answers instead, robots.txt on the same host is the fallback. */
const verify = async (url) => {
const home = await fetchOnce(url)
if (home.status === 200 && home.bytes > 400) return { state: 'verified', method: 'homepage-get', detail: `HTTP 200, ${home.bytes} bytes`, final: home.final }
const robots = await fetchOnce(new URL('/robots.txt', url).href)
if (robots.status === 200 && isRobots(robots.body)) {
return { state: 'verified', method: 'robots-txt', detail: `homepage HTTP ${home.status} (bot challenge from a datacentre IP), robots.txt HTTP 200, ${robots.bytes} bytes`, final: home.final }
}
return { state: 'unknown', method: 'none', detail: `homepage HTTP ${home.status}${home.error ? ` (${home.error})` : ''}, robots.txt HTTP ${robots.status}`, final: home.final }
}
/* ── CONTROLS, IN BAND ─────────────────────────────────────────────────────────────────────── */
const posc = await verify('https://seedcompany.com/')
if (posc.state !== 'verified') { console.error(`ORG-LINKS: positive control failed (${posc.detail}) — a network fault would write every org off as unreachable. Refusing.`); process.exit(1) }
const negc = await verify('https://this-org-does-not-exist-doxa-maps.example/')
if (negc.state === 'verified') { console.error('ORG-LINKS: negative control PASSED verification — the check cannot fail, so it proves nothing. Refusing.'); process.exit(1) }
const robotsc = await fetchOnce('https://www.biblica.com/definitely-not-robots-xyzzy.txt')
if (robotsc.status === 200 && isRobots(robotsc.body)) { console.error('ORG-LINKS: the robots fallback accepts a path that is not robots.txt. Refusing.'); process.exit(1) }
console.log(`controls: positive ${posc.method}, negative "${negc.state}", robots-shape rejected non-robots path`)
const out = {}
let verified = 0, unknown = 0
for (const o of ORGS) {
const v = await verify(o.url)
if (v.state === 'verified') verified++; else unknown++
out[o.id] = { name: o.name, url: o.url, why: o.why, state: v.state, method: v.method, detail: v.detail,
/* source + flags as two fields, not a string with a suffix: a pattern is reconstructed by
the builder, and a suffix convention is one escaping bug away from a silently different
regex. */
match: o.match.map((r) => ({ s: r.source, f: r.flags })) }
console.log(` ${v.state === 'verified' ? 'ok ' : 'HELD'} ${o.id.padEnd(15)} ${v.method.padEnd(12)} ${o.url}`)
}
if (!verified) { console.error('ORG-LINKS: nothing verified — refusing to write.'); process.exitCode = 1 } else {
/* THROUGH THE SNAPSHOT GUARD, and this script is the reason it exists in miniature: three of
these hosts answered 429 on an earlier probe in the same hour. A rate-limited run would
verify four orgs instead of sixteen and quietly take the contact off ~600 credits. */
const body = `${JSON.stringify({
meta: {
title: 'Who to contact — screened, fetched homepages for the organisations named in the credits',
generated: new Date().toISOString().slice(0, 10),
grain: 'one entry per organisation, matched to a credit string by the regexes it carries',
verified_orgs: verified,
orgs_that_did_not_answer: unknown,
states: {
verified: 'this script fetched it on the run that wrote this file — homepage 200, or robots.txt 200 where a bot challenge answers a datacentre IP',
unknown: 'did not answer this run; NOT shipped, and never read as broken',
},
not_linked: HELD.map((h) => ({ reason: h.reason })),
note: 'Hand-screened allowlist. eBible\'s own homeDomain/publicationURL fields are deliberately NOT used: a refresh of someone else\'s catalogue must not be able to put a publisher on this map.',
},
held: HELD.map((h) => ({ pattern: h.pattern.source, reason: h.reason })),
orgs: out,
}, null, 1)}\n`
writeSnapshot(OUT, body, (j) => Object.values(j.orgs || {}).filter((o) => o.state === 'verified').length, 'verified organisation(s)')
console.log(`translator-org-links: ${verified} verified, ${unknown} did not answer`)
console.log(`wrote ${OUT}`)
}

View file

@ -46,7 +46,7 @@ import SearchBar from '@map/components/search/SearchBar.vue'
import FilterDropdown from '@map/components/search/FilterDropdown.vue'
import { resolveAssetsBase } from '@map/utils/embedBase.js'
import { applyPeopleGroupList } from '@map/lists/peopleGroupList.js'
import { tStatic } from '@map/i18n/index.js'
import { tStatic, localeFromUrl, RTL_LOCALES, getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
import { countrySlug } from './src/countries.js'
// ── DERIVED family axes (BUG-SEMANTIC-TREE-SIDEBAR-NOT-INTEROPERABLE-WITH-GRID) ──
// The Religion/Language trees' TOP TIERS emit DERIVED family names ("Hinduism",
@ -251,7 +251,36 @@ const filtersBackdrop = computed(() => {
}
})
const prayBaseUrl = computed(() => props.prayBaseUrl || cfg.value.prayBaseUrl || 'https://pray.doxa.life')
const languageCode = computed(() => props.languageCode || cfg.value.languageCode || '')
// ★ THE URL IS A SOURCE OF TRUTH FOR THE DATA TOO, not just for the chrome.
// MEASURED by rega at ?lang=ar and ?lang=es: this grid's CHROME translated and its DATA stayed
// English — country names, affinity names, religion labels — because the `&lang=` on the fetch was
// built from props and profile-config ONLY. A reader who asked for Arabic got Arabic buttons around
// English content, which reads as a half-finished translation rather than the wiring bug it is.
// localeFromUrl() is the library's own reader, imported rather than re-implemented, so the fetch and
// the chrome can never disagree about what the URL asked for. Same ladder regions uses
// (region-dashboards.vue:95), and a host that passes languageCode still wins.
const languageCode = computed(() =>
props.languageCode || cfg.value.languageCode || cfg.value.locale || cfg.value.lang || localeFromUrl() || '')
// ★ RTL IS A DIRECTION, NOT A TRANSLATION — and this grid had none.
// MEASURED in ar: the cards, the total and the filter bar all laid out left-to-right around Arabic
// words. createAppI18n reflects an RTL locale onto document.documentElement, which is right for a
// page it owns and useless for an element dropped into someone else's, which is this grid's whole
// job. Set on the HOST, like both composites: `dir` takes effect through the inherited CSS
// `direction`, and inheritance crosses shadow boundaries, so one attribute turns the element and
// everything it renders. Only ever SET, never cleared — a host page that declares its own dir owns
// that decision.
const textDir = computed(() => {
const l = languageCode.value || getRuntimeLocale() || detectLocale()
return RTL_LOCALES.has(String(l).split('-')[0].toLowerCase()) ? 'rtl' : 'ltr'
})
function reflectDir() {
try {
const host = hostEl()
if (host && host.setAttribute && textDir.value === 'rtl') host.setAttribute('dir', 'rtl')
} catch (_) { /* no host (SSR/tests) — nothing to reflect */ }
}
watch(textDir, reflectDir)
// ★ ABSOLUTE CANONICAL DEFAULT (the agent 2026-09-23, pasted from his own browser:
// http://localhost:5174/research/pitaguari/ -- "when I click view more information it does not
// send me to the proper doxa.life page"). The old default '/research/' was ROOT-RELATIVE, so it
@ -842,6 +871,9 @@ onMounted(() => {
const el = hostEl()
if (el) el.__vocabulary = vocabulary
if (typeof window !== 'undefined') window.__peopleGroupsGridVocabulary = vocabulary
// The watcher above only fires on a CHANGE, and the common case is a page that loads in Arabic
// and never changes — so reflect it once on mount as well.
reflectDir()
getUUPGs()
})
</script>

View file

@ -230,7 +230,13 @@ const bundleSrc = (bundle, file) =>
// composite that let the grid hold all three would put the two halves into a disagreement the map
// cannot resolve and the reader cannot see. So the composite mirrors the map's own exclusivity: a
// new facet replaces the previous one, exactly as clicking a second legend row does.
const linked = reactive({ region: { value: '', label: '' }, religion: '', language: '' })
// ★ LANGUAGE CARRIES TWO FORMS NOW, FOR THE SAME REASON REGION ALWAYS DID.
// MEASURED against the live API: primary_language's LABEL is fully localized (zero names shared
// between en and ar) while its VALUE is the stable ISO 639-3 code (all 864 identical). The map's
// legend selects by NAME; the grid matches rows by VALUE. Carrying one form would have meant the
// link matching nothing the moment the grid's data followed the URL's language — and an empty
// intersection reads as "no results", not as a broken wire.
const linked = reactive({ region: { value: '', label: '' }, religion: '', language: { value: '', label: '' } })
// ★ WHAT THE LIST IS SHOWING, so the map can go there (BUILD-GRID-SELECTION-FLIES-THE-MAP).
// The link ran one way: the map narrowed the grid, and the grid moved nothing. The grid already
@ -248,7 +254,7 @@ const gridSlugs = ref([])
function clearExcept(keep) {
if (keep !== 'region' && (linked.region.value || linked.region.label)) { linked.region.value = ''; linked.region.label = '' }
if (keep !== 'religion' && linked.religion) linked.religion = ''
if (keep !== 'language' && linked.language) linked.language = ''
if (keep !== 'language' && (linked.language.value || linked.language.label)) { linked.language.value = ''; linked.language.label = '' }
}
const slugify = (label) => String(label || '').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '')
@ -279,8 +285,14 @@ function adopt(next) {
if (next.religion !== linked.religion) { linked.religion = next.religion; clearExcept('religion') }
return
}
if (next.language) {
if (next.language !== linked.language) { linked.language = next.language; clearExcept('language') }
if (next.language && (next.language.value || next.language.label)) {
const v = next.language.value || ''
const l = next.language.label || ''
if (v !== linked.language.value || l !== linked.language.label) {
linked.language.value = v
linked.language.label = l
clearExcept('language')
}
return
}
// Nothing set anywhere in this report: the reader cleared whatever was active.
@ -306,7 +318,8 @@ function onGridFiltered(ev) {
adopt({
region: { value: (entry && (entry.value ?? entry)) || '', label: (entry && entry.label) || '' },
religion: nameOf(active.__religion_family),
language: nameOf(active.primary_language),
// The grid reports {value,label} per facet, which is exactly the two forms the halves need.
language: { value: (active.primary_language && active.primary_language.value) || '', label: (active.primary_language && active.primary_language.label) || '' },
})
}
function onMapFiltered(ev) {
@ -318,7 +331,8 @@ function onMapFiltered(ev) {
adopt({
region: { value: label ? slugify(label) : '', label },
religion: ev?.detail?.religion || '',
language: ev?.detail?.language || '',
// The map sends both: the name its legend selected by, and the locale-stable ISO code.
language: { value: ev?.detail?.languageCode || '', label: ev?.detail?.language || '' },
})
}
@ -339,14 +353,17 @@ function pushToChildren() {
if (map) {
let cfg = {}
try { cfg = JSON.parse(map.getAttribute('profile-config') || '{}') } catch (_) { cfg = {} }
cfg.externalFilters = { region: linked.region.value, religion: linked.religion, language: linked.language, fitSlugs: gridSlugs.value }
cfg.externalFilters = { region: linked.region.value, religion: linked.religion, language: linked.language.label, fitSlugs: gridSlugs.value }
map.setAttribute('profile-config', JSON.stringify(cfg))
}
const grid = rootEl.querySelector('people-groups-grid')
if (grid) {
grid.setAttribute('wagf-region', linked.region.value)
grid.setAttribute('religion-family', linked.religion)
grid.setAttribute('primary-language', linked.language)
// ★ THE CODE, NOT THE NAME. valueMatches compares the normalized value as well as the label,
// so a stable ISO code matches in every locale while a display name only matches in the one it
// was fetched in.
grid.setAttribute('primary-language', linked.language.value || linked.language.label)
}
}
watch(linked, pushToChildren, { deep: true })
@ -425,7 +442,7 @@ const sections = computed(() => [
...(hostLang.value ? { lang: hostLang.value } : {}),
...(hostList.value ? { list: hostList.value } : {}),
// The composite's filter state, in the map's inbound shape. Absent keys are ignored there.
externalFilters: { region: linked.region.value, religion: linked.religion, language: linked.language, fitSlugs: gridSlugs.value },
externalFilters: { region: linked.region.value, religion: linked.religion, language: linked.language.label, fitSlugs: gridSlugs.value },
assetsBase: '../../assets',
shareUrl: SHARE_URL
})
@ -452,7 +469,7 @@ const sections = computed(() => [
// reason); driving them from the composite is the same channel, not a new one.
'wagf-region': linked.region.value,
'religion-family': linked.religion,
'primary-language': linked.language,
'primary-language': linked.language.value || linked.language.label,
}
}
])

View file

@ -89,11 +89,17 @@ const lines = []
// engine — a MapLibre-only paint or layout mistake drops a whole layer silently, which would show
// up here as a link that delivers nothing while the Mapbox run stays green.
const PROVIDERS = ['mapbox', 'maplibre']
// ★ AND PER LOCALE, because the grid's DATA now follows the URL. The labels this link used to carry
// are fully localized — zero language names shared between en and ar — so a link proven only in
// English proves nothing about the language a reader actually asked for. en is the control; ar is
// the case that would have broken silently.
const LOCALES = (process.env.LINK_LOCALES || 'en,ar').split(',').map((s) => s.trim()).filter(Boolean)
const results = {}
try {
for (const locale of LOCALES) {
for (const engine of PROVIDERS) {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } })
await page.goto(`${LEAF}?provider=${engine}`, { waitUntil: 'domcontentloaded', timeout: 60000 })
await page.goto(`${LEAF}?provider=${engine}&lang=${locale}`, { waitUntil: 'domcontentloaded', timeout: 60000 })
// Everything below runs in ONE evaluate: the totals have to be read from the same live page, and
// a `groups-filtered` listener installed from Node would miss events fired between round trips.
@ -192,15 +198,52 @@ try {
// ── THEN THE REAL THING ───────────────────────────────────────────────────────────────────
const linked = await driveLinked(family)
return { unfiltered, family, letter, direct, linked, mutant, mapVocab, gridVocab: gridVocab.slice(0, 12) }
// ★ AND THE LANGUAGE FACET, WHICH IS THE ONE THAT MOVES WITH THE LOCALE.
// MEASURED against the live API: primary_language's LABEL is fully localized (zero names shared
// between en and ar) while its VALUE is the stable ISO 639-3 code (all identical). So the link
// must travel as the CODE. This proves it does, and the poison case proves the code is what is
// doing the work rather than a name that happens to match in the locale under test.
let lang = null
const langVocab = (grid.__vocabulary || window.__peopleGroupsGridVocabulary)?.('primary_language') || []
// Find a language whose own facet narrows the grid to a strict subset, and learn its CODE from
// the grid's rows rather than from a table.
for (const name of langVocab.slice(0, 40)) {
await clear()
await sleep(1500)
grid.setAttribute('primary-language', name)
await settle()
if (total > 0 && total < unfiltered) { lang = { name, direct: total }; break }
}
if (lang) {
// The map reports both forms on its own wire; drive that wire and read what the grid does.
await clear(); await sleep(1500)
const map = deep('doxa-research-map') || host
map.dispatchEvent(new CustomEvent('map-filtered', {
detail: { region: '', religion: '', language: lang.name, languageCode: '' },
bubbles: true, composed: true,
}))
await settle()
lang.byName = total
// POISON: a code that cannot exist. If the grid still narrows the same way, the wire is not
// carrying what this gate thinks it is.
await clear(); await sleep(1500)
map.dispatchEvent(new CustomEvent('map-filtered', {
detail: { region: '', religion: '', language: 'zz-not-a-language', languageCode: 'zzz' },
bubbles: true, composed: true,
}))
await settle()
lang.poison = total
}
return { unfiltered, family, letter, direct, linked, mutant, mapVocab, gridVocab: gridVocab.slice(0, 12), lang, langVocabCount: langVocab.length }
})
await page.close()
if (out.skip) { await browser.close(); cleanup(); skip(`${out.skip} (under ${engine})`) }
results[engine] = out
if (out.fail) { fails.push(`[${engine}] ${out.fail}`); continue }
if (out.skip) { await browser.close(); cleanup(); skip(`${out.skip} (under ${locale}/${engine})`) }
results[`${locale}/${engine}`] = out
if (out.fail) { fails.push(`[${locale}/${engine}] ${out.fail}`); continue }
lines.push(` ── ${engine}`)
lines.push(` ── ${locale} / ${engine}`)
lines.push(` vocabulary — the map names ${out.mapVocab.length} religion families, the grid names ${out.gridVocab.length}+; '${out.family}' is shared`)
lines.push(` direct — religion-family="${out.family}" set on the grid itself: ${out.direct} of ${out.unfiltered}`)
lines.push(` linked — the same family over the composite's wire: ${out.linked}`)
@ -212,12 +255,26 @@ try {
if (out.linked !== out.direct) {
fails.push(`[${engine}] the link does not deliver the facet — '${out.family}' set on the grid gives ${out.direct} rows, the same family over the composite's wire gives ${out.linked}.`)
}
if (out.lang) {
lines.push(` language — '${out.lang.name}' direct ${out.lang.direct}, over the wire ${out.lang.byName}, poison ${out.lang.poison} (of ${out.langVocabCount} names)`)
if (out.lang.byName !== out.lang.direct) {
fails.push(`[${engine}] the LANGUAGE link does not deliver the facet — '${out.lang.name}' set on the grid gives ${out.lang.direct} rows, the same language over the composite's wire gives ${out.lang.byName}.`)
}
if (out.lang.poison === out.lang.direct) {
fails.push(`[${engine}] NO TEETH on language — a language that does not exist narrowed the grid to the same ${out.lang.direct} rows. The wire is not carrying what this gate thinks it is.`)
}
} else {
lines.push(` language — SKIPPED (no single language narrows this grid to a strict subset)`)
}
}
}
// ★ AND THE TWO ENGINES MUST AGREE. Each run above only proves the link works within one engine;
// identical COUNTS is what proves the contract is the same product on both. A per-engine pass with
// different numbers would mean the two builds narrow to different peoples for the same choice.
const [a, b] = PROVIDERS
// ★ THE TWO ENGINES MUST AGREE, WITHIN EACH LOCALE. Each run above only proves the link works in
// one build; identical COUNTS is what proves the contract is the same product on both. Comparing
// ACROSS locales would compare two different datasets, since the labels themselves change — so the
// comparison is per locale, and a per-engine pass with different numbers still fails.
for (const locale of LOCALES) {
const [a, b] = PROVIDERS.map((e) => `${locale}/${e}`)
if (results[a] && results[b] && !results[a].fail && !results[b].fail) {
if (results[a].unfiltered !== results[b].unfiltered) {
fails.push(`the two engines start from different data — ${a} lists ${results[a].unfiltered} groups, ${b} lists ${results[b].unfiltered}.`)
@ -229,6 +286,7 @@ try {
fails.push(`the link delivers ${results[a].linked} rows under ${a} and ${results[b].linked} under ${b} — interoperability that holds on one provider and not the other is not interoperability.`)
}
}
}
} catch (err) {
fails.push(`the measurement itself failed: ${err && err.message}`)
} finally {

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,290 @@
{
"meta": {
"title": "Who to contact — screened, fetched homepages for the organisations named in the credits",
"generated": "2026-09-26",
"grain": "one entry per organisation, matched to a credit string by the regexes it carries",
"verified_orgs": 16,
"orgs_that_did_not_answer": 0,
"states": {
"verified": "this script fetched it on the run that wrote this file — homepage 200, or robots.txt 200 where a bot challenge answers a datacentre IP",
"unknown": "did not answer this run; NOT shipped, and never read as broken"
},
"not_linked": [
{
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the agent's ruling, not this script's"
},
{
"reason": "doctrine not established from published material — left out rather than guessed"
}
],
"note": "Hand-screened allowlist. eBible's own homeDomain/publicationURL fields are deliberately NOT used: a refresh of someone else's catalogue must not be able to put a publisher on this map."
},
"held": [
{
"pattern": "bible society|biblical society|bibelselskab|bibelsellskap|soci(e|é)t(y|é) b[ií]blica|sociedad b[ií]blica|lembaga alkitab|united bible societies|sirk[əe]ti|bible societies",
"reason": "national Bible Society — several UBS members publish editions containing the deuterocanonicals, so whether a Society contact sits inside the 66-book filter is the agent's ruling, not this script's"
},
{
"pattern": "artists for israel|k[äa]rnbibeln|sanskritbible|gilakmedia|tarifit|gSungrab|audiobiblia",
"reason": "doctrine not established from published material — left out rather than guessed"
}
],
"orgs": {
"wycliffe-ca": {
"name": "Wycliffe Bible Translators of Canada",
"url": "https://www.wycliffe.ca/",
"why": "Bible translation agency, Canadian office of the Wycliffe Global Alliance.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 2084732 bytes",
"match": [
{
"s": "wycliffe[^,]*\\bcanada\\b",
"f": "i"
}
]
},
"wycliffe-au": {
"name": "Wycliffe Bible Translators Australia",
"url": "https://wycliffe.org.au/",
"why": "Bible translation agency, Australian office of the Wycliffe Global Alliance.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 87857 bytes",
"match": [
{
"s": "wycliffe[^,]*australian?\\b",
"f": "i"
}
]
},
"wycliffe": {
"name": "Wycliffe Bible Translators",
"url": "https://www.wycliffe.org/",
"why": "Bible translation agency; translates the 66-book canon, no added scripture.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 367138 bytes",
"match": [
{
"s": "\\bwycliffe\\b",
"f": "i"
}
]
},
"twftw": {
"name": "The Word for the World",
"url": "https://www.twftw.org/",
"why": "Bible translation agency working through national translators.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 1145398 bytes",
"match": [
{
"s": "word for the world",
"f": "i"
},
{
"s": "\\bTWFTW\\b",
"f": ""
}
]
},
"biblica": {
"name": "Biblica",
"url": "https://www.biblica.com/",
"why": "Bible publisher and translator (NIV); 66-book canon.",
"state": "verified",
"method": "robots-txt",
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 292 bytes",
"match": [
{
"s": "\\bbiblica\\b",
"f": "i"
}
]
},
"pioneer": {
"name": "Pioneer Bible Translators",
"url": "https://pioneerbible.org/",
"why": "Bible translation agency working in languages with no Scripture.",
"state": "verified",
"method": "robots-txt",
"detail": "homepage HTTP 403 (bot challenge from a datacentre IP), robots.txt HTTP 200, 120 bytes",
"match": [
{
"s": "pioneer bible translators",
"f": "i"
},
{
"s": "traducteurs pionniers de la bible",
"f": "i"
},
{
"s": "\\bPBT\\b",
"f": ""
}
]
},
"seed": {
"name": "The Seed Company",
"url": "https://seedcompany.com/",
"why": "Bible translation agency, affiliate of Wycliffe USA.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 371615 bytes",
"match": [
{
"s": "seed company",
"f": "i"
}
]
},
"sim": {
"name": "SIM",
"url": "https://www.sim.org/",
"why": "Evangelical mission agency; translation among its field work.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 190863 bytes",
"match": [
{
"s": "\\bSIM\\b",
"f": ""
},
{
"s": "society for international ministries",
"f": "i"
}
]
},
"bible-league": {
"name": "Bible League International",
"url": "https://www.bibleleague.org/",
"why": "Scripture publisher; absorbed the World Bible Translation Center, whose name the credits still carry.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 701341 bytes",
"match": [
{
"s": "world bible translation cent(er|re)",
"f": "i"
},
{
"s": "bible league",
"f": "i"
},
{
"s": "liga b[ií]blica",
"f": "i"
}
]
},
"unfoldingword": {
"name": "Door43 / unfoldingWord",
"url": "https://door43.org/",
"why": "Open-licensed Bible translation resources; the Door43 community platform.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 7465 bytes",
"match": [
{
"s": "door\\s?43",
"f": "i"
},
{
"s": "unfoldingword",
"f": "i"
}
]
},
"bridgeconn": {
"name": "Bridge Connectivity Solutions",
"url": "https://www.bridgeconn.com/",
"why": "Indian Bible-translation technology and translation services group.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 20334 bytes",
"match": [
{
"s": "bridge conn?ectivity",
"f": "i"
},
{
"s": "bridge communication systems",
"f": "i"
}
]
},
"btl-kenya": {
"name": "Bible Translation and Literacy",
"url": "https://www.btlkenya.org/",
"why": "Kenyan Bible translation agency, Wycliffe Global Alliance member.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 76225 bytes",
"match": [
{
"s": "bible translation and literacy",
"f": "i"
}
]
},
"kartidaya": {
"name": "Yayasan Kartidaya",
"url": "https://kartidaya.org/",
"why": "Indonesian Bible translation agency, Wycliffe Global Alliance member.",
"state": "verified",
"method": "robots-txt",
"detail": "homepage HTTP 429 (bot challenge from a datacentre IP), robots.txt HTTP 200, 319 bytes",
"match": [
{
"s": "kartidaya",
"f": "i"
}
]
},
"liebenzell": {
"name": "Liebenzell Mission",
"url": "https://www.liebenzell.org/",
"why": "German evangelical mission agency (Free Church tradition).",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 1281367 bytes",
"match": [
{
"s": "liebenzel+l? mission",
"f": "i"
}
]
},
"sil": {
"name": "SIL Global",
"url": "https://www.sil.org/",
"why": "Language development organisation; the linguistic partner behind much of this corpus.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 415008 bytes",
"match": [
{
"s": "\\bSIL\\b",
"f": ""
}
]
},
"lockman": {
"name": "The Lockman Foundation",
"url": "https://www.lockman.org/",
"why": "Scripture publisher (NASB, LBLA); 66-book canon.",
"state": "verified",
"method": "homepage-get",
"detail": "HTTP 200, 163046 bytes",
"match": [
{
"s": "lockman foundation",
"f": "i"
}
]
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -43,7 +43,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-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki" crossorigin="anonymous"></script>
<script src="../../big-picture.js" integrity="sha384-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -43,7 +43,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-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki" crossorigin="anonymous"></script>
<script src="../../big-picture.js" integrity="sha384-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -43,7 +43,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-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki" crossorigin="anonymous"></script>
<script src="../big-picture.js" integrity="sha384-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU" 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

@ -47,7 +47,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-dmlUkR/A54stQ9oHUOIrUbiGInFwp/pn8O6fJhlPKG7gfreqLjIQFTP8xkB1FOz9" crossorigin="anonymous"></script>
<script src="../../dashboard-plus-grid.js" integrity="sha384-XHViLblyRnVqeh/EO/H+OfVcrRrhVjvtLKORCDZ5agT580qLVPxYUiPF4L1IEvic" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -47,7 +47,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-dmlUkR/A54stQ9oHUOIrUbiGInFwp/pn8O6fJhlPKG7gfreqLjIQFTP8xkB1FOz9" crossorigin="anonymous"></script>
<script src="../dashboard-plus-grid.js" integrity="sha384-XHViLblyRnVqeh/EO/H+OfVcrRrhVjvtLKORCDZ5agT580qLVPxYUiPF4L1IEvic" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -38,7 +38,7 @@
"bundle": "big-picture",
"scriptUrl": "./big-picture/big-picture.js",
"output": "dx-maps/big-picture/big-picture.js",
"integrity": "sha384-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki",
"integrity": "sha384-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU",
"elements": [
"bp-big-picture",
"bp-research-header",
@ -60,7 +60,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-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki\" 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-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU\" 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": [
{
@ -214,7 +214,7 @@
"bundle": "dashboard-plus-grid",
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"output": "dx-maps/dashboard-plus-grid/dashboard-plus-grid.js",
"integrity": "sha384-dmlUkR/A54stQ9oHUOIrUbiGInFwp/pn8O6fJhlPKG7gfreqLjIQFTP8xkB1FOz9",
"integrity": "sha384-XHViLblyRnVqeh/EO/H+OfVcrRrhVjvtLKORCDZ5agT580qLVPxYUiPF4L1IEvic",
"elements": [
"dashboard-plus-grid"
],
@ -234,7 +234,7 @@
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-dmlUkR/A54stQ9oHUOIrUbiGInFwp/pn8O6fJhlPKG7gfreqLjIQFTP8xkB1FOz9\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-XHViLblyRnVqeh/EO/H+OfVcrRrhVjvtLKORCDZ5agT580qLVPxYUiPF4L1IEvic\" 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": [
{
@ -353,7 +353,7 @@
"bundle": "doxa-research-map",
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"output": "dx-maps/doxa-research-map/doxa-research-map.js",
"integrity": "sha384-IE7GadcBdYFEn58dO/atf9zTDaFcWkpRWY242m9noDv/Hqh2OiAEZvib3AyvpUbA",
"integrity": "sha384-kZbs+xXC3w7pbJ/AHdTHPg7UrEFhkfBAPyE0VImdySNNdtF0Ax5V1B0CCWf5do39",
"elements": [
"doxa-research-map"
],
@ -369,7 +369,7 @@
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-IE7GadcBdYFEn58dO/atf9zTDaFcWkpRWY242m9noDv/Hqh2OiAEZvib3AyvpUbA\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-kZbs+xXC3w7pbJ/AHdTHPg7UrEFhkfBAPyE0VImdySNNdtF0Ax5V1B0CCWf5do39\" 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": [
{
@ -578,7 +578,7 @@
"bundle": "gospel-resources",
"scriptUrl": "./gospel-resources/gospel-resources.js",
"output": "dx-maps/gospel-resources/gospel-resources.js",
"integrity": "sha384-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm",
"integrity": "sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi",
"elements": [
"gospel-resources-map"
],
@ -598,7 +598,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
},
{
@ -612,7 +612,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
},
{
@ -626,7 +626,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/gospel-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -640,7 +640,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
},
{
@ -654,7 +654,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": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
}
]
@ -720,7 +720,7 @@
"bundle": "people-groups-grid",
"scriptUrl": "./people-groups-grid/people-groups-grid.js",
"output": "dx-maps/people-groups-grid/people-groups-grid.js",
"integrity": "sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14",
"integrity": "sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg",
"elements": [
"people-groups-grid"
],
@ -736,7 +736,7 @@
"scriptUrl": "./people-groups-grid/people-groups-grid.js",
"embed": {
"iframe": "<iframe src=\"./people-groups-grid/people-groups-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=\"./people-groups-grid/people-groups-grid.js\" integrity=\"sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14\" crossorigin=\"anonymous\"></script>\n<people-groups-grid profile-config='{\"profile\":\"people-groups-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></people-groups-grid>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./people-groups-grid/people-groups-grid.js\" integrity=\"sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg\" crossorigin=\"anonymous\"></script>\n<people-groups-grid profile-config='{\"profile\":\"people-groups-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></people-groups-grid>"
},
"instances": [
{
@ -970,7 +970,7 @@
"bundle": "research-map-plus-grid",
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"output": "dx-maps/research-map-plus-grid/research-map-plus-grid.js",
"integrity": "sha384-LYvmsSdjhpLm3hreqplnP4/i8BbdfQ2vAvL0wwk4lUfzbzShFuHEP9ycp0lhtf+F",
"integrity": "sha384-mUijovmr70lmhkp4ZJfJzpc9NLNc0xDADtgiatvxLAhQqOCW2xOYsH+5siFigPKa",
"elements": [
"research-map-plus-grid"
],
@ -990,7 +990,7 @@
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./research-map-plus-grid/research-map-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=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-LYvmsSdjhpLm3hreqplnP4/i8BbdfQ2vAvL0wwk4lUfzbzShFuHEP9ycp0lhtf+F\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-mUijovmr70lmhkp4ZJfJzpc9NLNc0xDADtgiatvxLAhQqOCW2xOYsH+5siFigPKa\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -36,7 +36,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-IE7GadcBdYFEn58dO/atf9zTDaFcWkpRWY242m9noDv/Hqh2OiAEZvib3AyvpUbA" crossorigin="anonymous"></script>
<script src="../doxa-research-map.js" integrity="sha384-kZbs+xXC3w7pbJ/AHdTHPg7UrEFhkfBAPyE0VImdySNNdtF0Ax5V1B0CCWf5do39" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -33,7 +33,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-IE7GadcBdYFEn58dO/atf9zTDaFcWkpRWY242m9noDv/Hqh2OiAEZvib3AyvpUbA" crossorigin="anonymous"></script>
<script src="../../doxa-research-map.js" integrity="sha384-kZbs+xXC3w7pbJ/AHdTHPg7UrEFhkfBAPyE0VImdySNNdtF0Ax5V1B0CCWf5do39" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -32,7 +32,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -32,7 +32,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -47,7 +47,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi" 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

@ -32,7 +32,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -36,7 +36,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -38,7 +38,7 @@
"bundle": "big-picture",
"scriptUrl": "./big-picture/big-picture.js",
"output": "dx-maps/big-picture/big-picture.js",
"integrity": "sha384-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki",
"integrity": "sha384-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU",
"elements": [
"bp-big-picture",
"bp-research-header",
@ -60,7 +60,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-f0QwbiCCDjTDqiQVxWY5KwZIGQ/eEcHdrSPLR8GqCZfc4r5PkfObe4GMN4lxK8ki\" 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-kmlhRu6ZAc3s0YfPwCjzpI7bhkh0VtchUf4CGKT6SudSe0hTZ9nUq58WWBH6rpOU\" 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": [
{
@ -214,7 +214,7 @@
"bundle": "dashboard-plus-grid",
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"output": "dx-maps/dashboard-plus-grid/dashboard-plus-grid.js",
"integrity": "sha384-dmlUkR/A54stQ9oHUOIrUbiGInFwp/pn8O6fJhlPKG7gfreqLjIQFTP8xkB1FOz9",
"integrity": "sha384-XHViLblyRnVqeh/EO/H+OfVcrRrhVjvtLKORCDZ5agT580qLVPxYUiPF4L1IEvic",
"elements": [
"dashboard-plus-grid"
],
@ -234,7 +234,7 @@
"scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-dmlUkR/A54stQ9oHUOIrUbiGInFwp/pn8O6fJhlPKG7gfreqLjIQFTP8xkB1FOz9\" crossorigin=\"anonymous\"></script>\n<dashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dashboard-plus-grid>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-XHViLblyRnVqeh/EO/H+OfVcrRrhVjvtLKORCDZ5agT580qLVPxYUiPF4L1IEvic\" 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": [
{
@ -353,7 +353,7 @@
"bundle": "doxa-research-map",
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"output": "dx-maps/doxa-research-map/doxa-research-map.js",
"integrity": "sha384-IE7GadcBdYFEn58dO/atf9zTDaFcWkpRWY242m9noDv/Hqh2OiAEZvib3AyvpUbA",
"integrity": "sha384-kZbs+xXC3w7pbJ/AHdTHPg7UrEFhkfBAPyE0VImdySNNdtF0Ax5V1B0CCWf5do39",
"elements": [
"doxa-research-map"
],
@ -369,7 +369,7 @@
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
"embed": {
"iframe": "<iframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-IE7GadcBdYFEn58dO/atf9zTDaFcWkpRWY242m9noDv/Hqh2OiAEZvib3AyvpUbA\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-kZbs+xXC3w7pbJ/AHdTHPg7UrEFhkfBAPyE0VImdySNNdtF0Ax5V1B0CCWf5do39\" 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": [
{
@ -578,7 +578,7 @@
"bundle": "gospel-resources",
"scriptUrl": "./gospel-resources/gospel-resources.js",
"output": "dx-maps/gospel-resources/gospel-resources.js",
"integrity": "sha384-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm",
"integrity": "sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi",
"elements": [
"gospel-resources-map"
],
@ -598,7 +598,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
},
{
@ -612,7 +612,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
},
{
@ -626,7 +626,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/gospel-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -640,7 +640,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-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
},
{
@ -654,7 +654,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": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-GcCK+eUTnl5RY4lsaZGRcDUKJJl20UL6UsKbUNEBJGsRmNGJcToH5y/jDKqnTOTm\" 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": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-aWRZtfKdtk+LWOYZYT8lBWYxIIvJn59/N6ANUcDw5e5wk3rjPHlZ/ItBR3CfJbhi\" 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>"
}
}
]
@ -720,7 +720,7 @@
"bundle": "people-groups-grid",
"scriptUrl": "./people-groups-grid/people-groups-grid.js",
"output": "dx-maps/people-groups-grid/people-groups-grid.js",
"integrity": "sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14",
"integrity": "sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg",
"elements": [
"people-groups-grid"
],
@ -736,7 +736,7 @@
"scriptUrl": "./people-groups-grid/people-groups-grid.js",
"embed": {
"iframe": "<iframe src=\"./people-groups-grid/people-groups-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=\"./people-groups-grid/people-groups-grid.js\" integrity=\"sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14\" crossorigin=\"anonymous\"></script>\n<people-groups-grid profile-config='{\"profile\":\"people-groups-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></people-groups-grid>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./people-groups-grid/people-groups-grid.js\" integrity=\"sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg\" crossorigin=\"anonymous\"></script>\n<people-groups-grid profile-config='{\"profile\":\"people-groups-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></people-groups-grid>"
},
"instances": [
{
@ -970,7 +970,7 @@
"bundle": "research-map-plus-grid",
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"output": "dx-maps/research-map-plus-grid/research-map-plus-grid.js",
"integrity": "sha384-LYvmsSdjhpLm3hreqplnP4/i8BbdfQ2vAvL0wwk4lUfzbzShFuHEP9ycp0lhtf+F",
"integrity": "sha384-mUijovmr70lmhkp4ZJfJzpc9NLNc0xDADtgiatvxLAhQqOCW2xOYsH+5siFigPKa",
"elements": [
"research-map-plus-grid"
],
@ -990,7 +990,7 @@
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./research-map-plus-grid/research-map-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=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-LYvmsSdjhpLm3hreqplnP4/i8BbdfQ2vAvL0wwk4lUfzbzShFuHEP9ycp0lhtf+F\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-mUijovmr70lmhkp4ZJfJzpc9NLNc0xDADtgiatvxLAhQqOCW2xOYsH+5siFigPKa\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -32,7 +32,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <people-groups-grid> custom element. -->
<script src="../../people-groups-grid.js" integrity="sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14" crossorigin="anonymous"></script>
<script src="../../people-groups-grid.js" integrity="sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -32,7 +32,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <people-groups-grid> custom element. -->
<script src="../people-groups-grid.js" integrity="sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14" crossorigin="anonymous"></script>
<script src="../people-groups-grid.js" integrity="sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -32,7 +32,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <people-groups-grid> custom element. -->
<script src="../../people-groups-grid.js" integrity="sha384-h7sjTAqFa4ozk9m+T2iqiJtX1eigKiO56zKQ4ji3OJO9RBvmMZCbs1xb0hATHx14" crossorigin="anonymous"></script>
<script src="../../people-groups-grid.js" integrity="sha384-zDk7LzYk0D8pNW6T7Fstzd69i2u3OTn39oO5Pmrr7TckfHH3ikpNe5O11+78mbHg" 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

@ -43,7 +43,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <research-map-plus-grid> custom element. -->
<script src="../../research-map-plus-grid.js" integrity="sha384-LYvmsSdjhpLm3hreqplnP4/i8BbdfQ2vAvL0wwk4lUfzbzShFuHEP9ycp0lhtf+F" crossorigin="anonymous"></script>
<script src="../../research-map-plus-grid.js" integrity="sha384-mUijovmr70lmhkp4ZJfJzpc9NLNc0xDADtgiatvxLAhQqOCW2xOYsH+5siFigPKa" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -43,7 +43,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <research-map-plus-grid> custom element. -->
<script src="../research-map-plus-grid.js" integrity="sha384-LYvmsSdjhpLm3hreqplnP4/i8BbdfQ2vAvL0wwk4lUfzbzShFuHEP9ycp0lhtf+F" crossorigin="anonymous"></script>
<script src="../research-map-plus-grid.js" integrity="sha384-mUijovmr70lmhkp4ZJfJzpc9NLNc0xDADtgiatvxLAhQqOCW2xOYsH+5siFigPKa" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: