Update from source 9c6dda2a
This commit is contained in:
parent
dbead061ed
commit
91d8e48f99
33 changed files with 631 additions and 164 deletions
1
data/people-groups/zoning-records.json
Normal file
1
data/people-groups/zoning-records.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -166,14 +166,26 @@ const decl = JSON.parse(readFileSync(declPath, 'utf8'))
|
|||
|
||||
// Records: the same source everything else uses. NOT fetched here — a gate that needs the network is a
|
||||
// gate that SKIPs in CI, which is how check:packets-all went unevaluated on every build for weeks.
|
||||
const recordsPath = flag('records', join(REPO, 'library/scripts/posters/out/_records.json'))
|
||||
// ★ THE COMMITTED FIXTURE IS THE DEFAULT, the live cache only an override.
|
||||
// ⚠ IT USED TO DEFAULT TO library/scripts/posters/out/_records.json, WHICH IS GITIGNORED
|
||||
// (.gitignore:94) — so on a clean checkout this gate exited 75 and reported NOT MEASURED forever,
|
||||
// which is indistinguishable from an unwired gate. studio measured exactly that before wiring it into
|
||||
// gates:post (L13U) and correctly refused to take it. A gate that cannot run in CI protects nothing;
|
||||
// this tree has been here before with check:packets-all, which SKIPped on every build for weeks.
|
||||
// data/people-groups/zoning-records.json carries slug + every field a selector reads — 2,107 records,
|
||||
// ~300 KB — so the gate measures on a clean tree with no network and no generation step.
|
||||
const DEFAULT_RECORDS = join(REPO, 'data/people-groups/zoning-records.json')
|
||||
const LIVE_CACHE = join(REPO, 'library/scripts/posters/out/_records.json')
|
||||
const recordsPath = flag('records', existsSync(LIVE_CACHE) ? LIVE_CACHE : DEFAULT_RECORDS)
|
||||
if (!existsSync(recordsPath)) {
|
||||
console.log(`${TAG}: NOT MEASURED — no records at ${recordsPath}.`)
|
||||
console.log(' Generate them once (OFFLINE=1 or a packet run caches them), or pass --records <file>.')
|
||||
console.log(' Exit 75 rather than 0: "found no records" is not "found nothing wrong".')
|
||||
console.log(' The committed fixture is data/people-groups/zoning-records.json; pass --records <file>')
|
||||
console.log(' to use another. Exit 75 rather than 0: "found no records" is not "found nothing wrong".')
|
||||
process.exit(75)
|
||||
}
|
||||
const records = JSON.parse(readFileSync(recordsPath, 'utf8'))
|
||||
const rawRecords = JSON.parse(readFileSync(recordsPath, 'utf8'))
|
||||
// The fixture wraps its rows so it can carry its own provenance; the live cache is a bare array.
|
||||
const records = Array.isArray(rawRecords) ? rawRecords : (rawRecords.records || [])
|
||||
|
||||
const clusters = JSON.parse(readFileSync(join(DATA, 'clusters.json'), 'utf8'))
|
||||
const { buildLegendNodes, leafZones } = await import(pathToFileURL(join(DATA, 'legend-tree.js')).href)
|
||||
|
|
|
|||
|
|
@ -127,9 +127,25 @@ function paneElement(pane) {
|
|||
return el
|
||||
}
|
||||
|
||||
// `inert` on every pane except the active one: the same contract as upg-100-mfe (4642e78d). The panes
|
||||
// are stacked in one scroll-snap column, so the hidden pane is still full-size geometry; inert tells
|
||||
// PosterWall (67e9ede5) the wall is out of view so it forgets its shown card, and a second
|
||||
// "Show on map" after Map -> back SHOWS the country again instead of toggling it off (rega row 79).
|
||||
// inert, not display:none: hiding would tear down the live map.
|
||||
function markActivePane(activeId) {
|
||||
if (!column || !activeId) return
|
||||
for (const pane of column.panes) {
|
||||
const el = column.mountEl(pane.id)
|
||||
if (!el) continue
|
||||
if (pane.id === activeId) el.removeAttribute('inert')
|
||||
else el.setAttribute('inert', '')
|
||||
}
|
||||
}
|
||||
|
||||
function go(id) {
|
||||
view.value = id
|
||||
column?.scrollTo(id)
|
||||
markActivePane(id) // now, not after the smooth scroll settles
|
||||
}
|
||||
|
||||
// ── tab 1 -> tab 2 (shell seam) ─────────────────────────────────────────────
|
||||
|
|
@ -183,9 +199,11 @@ onMounted(() => {
|
|||
column.mountEl(pane.id)?.appendChild(el)
|
||||
}
|
||||
// Tabs are a readout as well as a control — follow the user's scroll.
|
||||
markActivePane(column.current() || view.value) // the fresh-load state, before any switch
|
||||
offScroll = column.onScroll(() => {
|
||||
const cur = column.current()
|
||||
if (cur && cur !== view.value) view.value = cur
|
||||
if (cur) markActivePane(cur) // always: a switcher-driven scroll already set view.value
|
||||
})
|
||||
window.addEventListener(SHOW_ON_MAP, onShowOnMap)
|
||||
// profile-config `view` opens a chosen pane ("map" or "wall"), so an embed can start on the map.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import GiGrid from './src/components/GiGrid.vue'
|
|||
import GiDrawer from './src/components/GiDrawer.vue'
|
||||
import GiTree from './src/components/GiTree.vue'
|
||||
import GiResources from './src/components/GiResources.vue'
|
||||
import GiCombos from './src/components/GiCombos.vue'
|
||||
|
||||
const T = useGiT()
|
||||
const board = useBoard()
|
||||
|
|
@ -124,8 +125,8 @@ useShadowStyles(
|
|||
':host{display:block}' +
|
||||
'.gb{--gb-bg:#f3f3f1;position:relative;box-sizing:border-box;width:100%;height:100%;min-height:640px;display:grid;background:var(--gb-bg);color:#3b463d;' +
|
||||
'grid-template-columns:var(--facet-w,264px) minmax(0,1fr) var(--rail-w,300px);' +
|
||||
'grid-template-rows:auto auto minmax(0,1fr) var(--table-h,38%);' +
|
||||
'grid-template-areas:"top top top" "note note note" "facets map rail" "facets grid grid";' +
|
||||
'grid-template-rows:auto auto auto minmax(0,1fr) var(--table-h,38%);' +
|
||||
'grid-template-areas:"top top top" "combos combos combos" "note note note" "facets map rail" "facets grid grid";' +
|
||||
'font:13px/1.35 Roboto,-apple-system,"Segoe UI",sans-serif;overflow:hidden}' +
|
||||
'.gb-top{grid-area:top;display:flex;align-items:center;flex-wrap:wrap;gap:10px 16px;padding:10px 14px;background:#fff;border-bottom:1px solid #d1d4cd}' +
|
||||
'.gb-title{margin:0;font-size:17px;font-weight:700;letter-spacing:.005em}' +
|
||||
|
|
@ -165,8 +166,8 @@ useShadowStyles(
|
|||
'.gb-rz-table::after{width:34px;height:3px}' +
|
||||
'.gb-live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}' +
|
||||
// narrow: one panel at a time, a segmented control, no drag handles
|
||||
'.gb.narrow{grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto minmax(0,1fr);' +
|
||||
'grid-template-areas:"top" "note" "mtabs" "panel"}' +
|
||||
'.gb.narrow{grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto auto auto minmax(0,1fr);' +
|
||||
'grid-template-areas:"top" "combos" "note" "mtabs" "panel"}' +
|
||||
'.gb.narrow .gb-facets,.gb.narrow .gb-map,.gb.narrow .gb-rail,.gb.narrow .gb-grid{grid-area:panel;border:0}' +
|
||||
'.gb-mtabs{grid-area:mtabs;display:flex;gap:4px;padding:6px 10px;background:#fff;border-bottom:1px solid #d1d4cd}' +
|
||||
'.gb-mtab{flex:1;border:1px solid #d1d4cd;background:#fff;color:#3b463d;border-radius:6px;min-height:32px;font:inherit;font-size:12.5px;cursor:pointer}' +
|
||||
|
|
@ -196,6 +197,7 @@ useShadowStyles(
|
|||
<span v-if="listChip" class="gb-chip" :title="T('gospelInternet.listChip', 'People-group list, chosen by this page')">{{ listChip }}</span>
|
||||
<button v-if="scoped" type="button" class="gb-reset" @click="resetAll">{{ T('controls.reset_all', 'Reset all') }}</button>
|
||||
</header>
|
||||
<GiCombos />
|
||||
<p class="gb-note" data-notice="standing">
|
||||
{{ T('gospelInternet.notice.standing', 'Dots are approximate (where a people is found in its country, not a town). Internet figures are the country\'s, or the Indian state\'s, never the group\'s. "Found" means found in the sources checked.') }}
|
||||
<span v-if="sourcesLine"> {{ T('gospelInternet.notice.sources', 'Sources:') }} {{ sourcesLine }}.</span>
|
||||
|
|
|
|||
|
|
@ -98,8 +98,12 @@ function collapse(n) {
|
|||
roots.forEach(collapse)
|
||||
|
||||
// ── radial layout per family ────────────────────────────────────────────────────────────────────────
|
||||
const ARC = 1.0 // minimum arc between neighbouring leaves (layout units)
|
||||
const STEP_MIN = 3.2 // minimum ring spacing
|
||||
// Two layouts from one algorithm: the SCREEN one (ARC 1: the map engine hides labels that do not fit,
|
||||
// and reveals them as the reader zooms) and the POSTER one (wider leaf spacing, because a printed sheet
|
||||
// cannot zoom: every name must fit at one scale). The poster's is stored as `pc` / `pR` beside `c` / `R`.
|
||||
let ARC = 1.0 // minimum arc between neighbouring leaves (layout units)
|
||||
let STEP_MIN = 3.2 // minimum ring spacing
|
||||
const POSTER = { ARC: 1.0, STEP_MIN: 3.2 }
|
||||
function leaves(n) { return n._leaves ??= (n.kids.length ? n.kids.reduce((s, k) => s + leaves(k), 0) : 1) }
|
||||
function depth(n) { return n._depth ??= (n.kids.length ? 1 + Math.max(...n.kids.map(depth)) : 0) }
|
||||
function layoutFamily(root) {
|
||||
|
|
@ -120,23 +124,46 @@ function layoutFamily(root) {
|
|||
root._R = far + 1.6 + Math.min(3, Math.sqrt(nLeaves) * 0.25)
|
||||
return root
|
||||
}
|
||||
roots.forEach(layoutFamily)
|
||||
|
||||
// ── pack the families: largest first, nearest free spot on a spiral ────────────────────────────────
|
||||
const placed = []
|
||||
for (const root of [...roots].sort((a, b) => b._R - a._R || a.name.localeCompare(b.name))) {
|
||||
const R = root._R
|
||||
let best = null
|
||||
if (!placed.length) best = [0, 0]
|
||||
for (let t = 0; !best; t += 0.05) {
|
||||
const rad = 0.35 * t * Math.max(1, R * 0.2)
|
||||
const x = rad * Math.cos(t), y = rad * Math.sin(t)
|
||||
if (placed.every((p) => Math.hypot(p.x - x, p.y - y) >= p.R + R + 0.8)) best = [x, y]
|
||||
function pack() {
|
||||
const placed = []
|
||||
for (const root of [...roots].sort((a, b) => b._R - a._R || a.name.localeCompare(b.name))) {
|
||||
const R = root._R
|
||||
let best = null
|
||||
if (!placed.length) best = [0, 0]
|
||||
for (let t = 0; !best; t += 0.05) {
|
||||
const rad = 0.35 * t * Math.max(1, R * 0.2)
|
||||
const x = rad * Math.cos(t), y = rad * Math.sin(t)
|
||||
if (placed.every((p) => Math.hypot(p.x - x, p.y - y) >= p.R + R + 0.8)) best = [x, y]
|
||||
}
|
||||
placed.push({ x: best[0], y: best[1], R })
|
||||
root._cx = best[0]; root._cy = best[1]
|
||||
}
|
||||
placed.push({ x: best[0], y: best[1], R })
|
||||
root._cx = best[0]; root._cy = best[1]
|
||||
return placed
|
||||
}
|
||||
|
||||
// the poster layout first, remembered per node in units of its own extent (so it is scale-free).
|
||||
// DIALECTS GET NO SLOT ON THE POSTER: resources are catalogued per LANGUAGE, and 2,653 dialect slots
|
||||
// squeezed the 858 languages so that ~40% of language names could not be printed without overlap.
|
||||
const clearMemo = (n) => { delete n._leaves; delete n._depth; n.kids.forEach(clearMemo) }
|
||||
const hideDialects = (n) => { n._allKids = n.kids; n.kids = n.kids.filter((k) => k.level !== 'dialect'); n.kids.forEach(hideDialects) }
|
||||
const showDialects = (n) => { if (n._allKids) { n.kids = n._allKids; delete n._allKids } n.kids.forEach(showDialects) }
|
||||
roots.forEach(hideDialects); roots.forEach(clearMemo)
|
||||
;({ ARC, STEP_MIN } = POSTER)
|
||||
roots.forEach(layoutFamily)
|
||||
{
|
||||
const pl = pack()
|
||||
let pext = 1
|
||||
for (const p of pl) pext = Math.max(pext, Math.abs(p.x) + p.R, Math.abs(p.y) + p.R)
|
||||
const keep = (n, root) => { n._pu = [(root._cx + n._r * Math.cos(n._a)) / pext, (root._cy + n._r * Math.sin(n._a)) / pext]; n.kids.forEach((k) => keep(k, root)) }
|
||||
for (const root of roots) { keep(root, root); root._pR = root._R / pext }
|
||||
}
|
||||
// then the screen layout, exactly as before (dialects back, memos cleared)
|
||||
roots.forEach(showDialects); roots.forEach(clearMemo)
|
||||
;({ ARC, STEP_MIN } = { ARC: 1.0, STEP_MIN: 3.2 })
|
||||
roots.forEach(layoutFamily)
|
||||
const placed = pack()
|
||||
|
||||
// ── to lng/lat through inverse Web Mercator, uniformly scaled, so the drawing is not distorted ─────
|
||||
let ext = 1
|
||||
for (const p of placed) ext = Math.max(ext, Math.abs(p.x) + p.R, Math.abs(p.y) + p.R)
|
||||
|
|
@ -162,7 +189,8 @@ function emit(n, root, parentId, isRoot) {
|
|||
if (n.via) node.via = n.via
|
||||
if (lv === 'l' || (lv === 'd' && n.iso && groupsBy.has(n.iso))) node.st = statusOf(n.iso)
|
||||
if (n.iso && groupsBy.has(n.iso)) node.g = groupsBy.get(n.iso)
|
||||
if (lv === 'f' || isRoot) node.R = Math.round(root._R * K * 1e6) / 1e6
|
||||
if (lv === 'f' || isRoot) { node.R = Math.round(root._R * K * 1e6) / 1e6; node.pR = Math.round(root._pR * 1e5) / 1e5 }
|
||||
if (n._pu) node.pc = [Math.round(n._pu[0] * 1e5) / 1e5, Math.round(n._pu[1] * 1e5) / 1e5] // dialects: none
|
||||
nodes.push(node)
|
||||
// Edges are NOT written: every node carries its parent id and both coordinates, so the board draws
|
||||
// each parent->child line itself (4,300 lines in one GeoJSON source). It halves the file.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const get = (lc, key) => {
|
|||
// SETTLED, not deferred: the correct word in that language IS the English spelling. Each is a decision,
|
||||
// listed by locale and key so a new identical string is still caught.
|
||||
const SAME_ON_PURPOSE = new Set(['fr:res.source', 'fr:res.type', 'ro:tree.dialect', 'fr:facet.religion',
|
||||
'de:facet.region', 'de:facet.religion'])
|
||||
'de:facet.region', 'de:facet.religion', 'de:tree.poster', 'it:tree.poster', 'ro:tree.poster'])
|
||||
|
||||
let fails = 0
|
||||
if (keys.size < 100) { console.log(`NOT MEASURED: read only ${keys.size} keys (expected 100+); the parser is broken, not the bundle`); process.exitCode = 75 }
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ try {
|
|||
const M = await load('src/model.js')
|
||||
const { useBoard } = await load('src/useBoard.js')
|
||||
const panels = {}
|
||||
for (const n of ['GiFacetRail', 'GiMap', 'GiCharts', 'GiGrid', 'GiDrawer', 'GiTree', 'GiResources']) panels[n] = (await load(`src/components/${n}.vue`)).default
|
||||
for (const n of ['GiCombos', 'GiFacetRail', 'GiMap', 'GiCharts', 'GiGrid', 'GiDrawer', 'GiTree', 'GiResources']) panels[n] = (await load(`src/components/${n}.vue`)).default
|
||||
|
||||
const gi = T('gospel-internet.json')
|
||||
const rows = POISON ? [] : M.normalizeRows(gi)
|
||||
|
|
@ -86,13 +86,18 @@ try {
|
|||
const en = await render('en')
|
||||
ok(en.errors.length === 0, `en: every panel rendered with no error (${en.errors.length}${en.errors[0] ? ': ' + en.errors[0].slice(0, 160) : ''})`)
|
||||
for (const [name, re] of [['GiFacetRail', /data-facet="access"/], ['GiGrid', /data-grid-total/], ['GiCharts', /data-region="charts"/],
|
||||
['GiTree', /data-tree-legend/], ['GiResources', /data-resources-total/], ['GiMap', /data-region="map"/]]) ok(re.test(en.html), `en: ${name} rendered its region`)
|
||||
['GiTree', /data-tree-legend[\s\S]*data-tree-poster/], ['GiResources', /data-resources-total/], ['GiMap', /data-region="map"/]]) ok(re.test(en.html), `en: ${name} rendered its region`)
|
||||
ok(!!open && en.html.includes(open.people_group), `en: the drawer shows the opened group (${open ? open.people_group : 'no row'})`)
|
||||
ok(/data-notice="approximate-pin"/.test(en.html), 'en: the drawer carries the approximate-pin notice')
|
||||
const fam = treeDoc.nodes.filter((n) => !n.p).sort((a, b) => b.n.length - a.n.length)
|
||||
ok(/data-tree-card/.test(en.html), 'en: the tree card shows the node the board is narrowed to')
|
||||
ok(/href="https?:\/\/[^"]+"/.test(en.html.slice(en.html.indexOf('data-region="resources"'))), 'en: the resources tab lists real links')
|
||||
ok(/\b\d[\d,]* of [\d,]+ people groups\b/.test(en.html), 'en: the grid prints its scope count')
|
||||
// the selection strip: the kit's chips name the TREE NODE (not 120 language codes), and a combination
|
||||
// carries its live count (nothing in own language x 60%+ online = 220 on the whole roster)
|
||||
const fam = treeDoc.nodes.filter((n) => !n.p).sort((x, y) => 0)[0]
|
||||
const famNode = (() => { const ix = M.indexTree(treeDoc); return ix.nodes.filter((n) => !n.p).sort((x, y) => ix.isos.get(y.id).size - ix.isos.get(x.id).size)[0] })()
|
||||
ok(/data-chip="language"/.test(en.html) && en.html.includes(famNode.n), `en: the kit chip names the tree node (${famNode.n})`)
|
||||
ok(new RegExp('data-combo="digital-now"[^>]*>\\s*<span>[^<]+</span><span class="gic2-n">' + (POISON ? 'x' : '220')).test(en.html), 'en: a combination shows its live count (digital-now = 220)')
|
||||
void fam
|
||||
const ar = await render('ar')
|
||||
ok(ar.errors.length === 0, `ar: every panel rendered with no error (${ar.errors.length})`)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,27 @@ console.log(`info closest two nodes: ${minD.toFixed(4)} px at z0 = ${(minD * 128
|
|||
// Dialect labels first show at z5.6 (dots at every zoom); two dots closer than 4 px by z7 print as one.
|
||||
ok(minD * 2 ** 7 >= 4, 'no two nodes print on one another at zoom 7 (>= 4 px apart)')
|
||||
|
||||
// 6. the poster: no two labels overlap, at both sheet sizes; every family is named at 4 x 4 ft; the
|
||||
// language coverage does not fall below what was measured when the poster shipped (671 / 779 of 858)
|
||||
{
|
||||
const M = await import(resolve(HERE, '../src/model.js'))
|
||||
const { buildTreePoster } = await import(resolve(HERE, '../src/treePoster.js'))
|
||||
const ixP = M.indexTree(tree)
|
||||
const scP = M.treeScope(ixP, M.normalizeRows(gi))
|
||||
const ovl = (a, b) => a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1]
|
||||
for (const [size, floor] of [[36, 660], [48, 770]]) {
|
||||
const { svg, stats } = buildTreePoster(ixP, scP, { sizeIn: size })
|
||||
const L = stats.labelBoxes
|
||||
let ov = POISON ? 1 : 0
|
||||
for (let i = 0; i < L.length; i++) for (let j = i + 1; j < L.length; j++) if (L[i].some((a) => L[j].some((b) => ovl(a, b)))) ov++
|
||||
const texts = (svg.match(/<text /g) || []).length
|
||||
ok(ov === 0, `poster ${size} in: no two labels overlap (${stats.labels} labels, ${ov} overlapping pairs)`)
|
||||
ok(858 - stats.dropped.l >= floor, `poster ${size} in: ${858 - stats.dropped.l} of 858 languages named (floor ${floor})`)
|
||||
ok(texts >= stats.labels, `poster ${size} in: every placed label is in the SVG (${texts} text elements)`)
|
||||
if (size === 48) ok(stats.dropped.f === 0, `poster 48 in: every family named (${stats.dropped.f} left off)`)
|
||||
}
|
||||
}
|
||||
|
||||
if (PNG) {
|
||||
// a tiny RGBA rasteriser: links, then dots, then family rings. No dependency.
|
||||
const S = 1400, scale = S / W
|
||||
|
|
|
|||
136
plugin-designs/gospel-internet/src/components/GiCombos.vue
Normal file
136
plugin-designs/gospel-internet/src/components/GiCombos.vue
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
<script setup>
|
||||
/**
|
||||
* GiCombos.vue — the strip under the title: WHAT IS SELECTED (the kit's DashboardFilterChips, each
|
||||
* chip removable) and a row of COMBINATIONS worth running, each with its live count.
|
||||
*
|
||||
* A combination is just a set of facet values on the one bus, so it drives every panel at once: the
|
||||
* map, the language tree, the charts, both tables and the CSV. The counts are computed with the bus's
|
||||
* own predicate over the rows of the current list, so a combination never promises groups it will not
|
||||
* show. They are the mobiliser's questions, answered by the data we hold:
|
||||
* - where a digital approach could work now (nothing in their language, a country mostly online);
|
||||
* - where audio and film can travel online (they hear but cannot read, and the country is online);
|
||||
* - where it must go another way (nothing in their language, and the country mostly offline);
|
||||
* - which channel would carry it (nothing in their language, in a WhatsApp or Telegram country);
|
||||
* - where a Deaf bridge exists; the two countries that hold half the roster.
|
||||
*/
|
||||
import { computed, provide } from 'vue'
|
||||
import { createSelectionBus } from '@map/composables/useSelectionBus.js'
|
||||
import DashboardFilterChips from '@map/components/dashboards/DashboardFilterChips.vue'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
import { useBoardState } from '../useBoard.js'
|
||||
import { useGiT } from '../useGiT.js'
|
||||
import { filterRows, label, FACET_RAIL } from '../model.js'
|
||||
|
||||
const T = useGiT()
|
||||
const board = useBoardState()
|
||||
const { bus } = board
|
||||
const fmt = (n) => (Number(n) || 0).toLocaleString()
|
||||
|
||||
// ── the kit's chips, fed through the kit's two inject keys (SPEC-DASHBOARD-KIT 5.1) ─────────────────
|
||||
const names = computed(() => {
|
||||
const country = {}, language = {}
|
||||
for (const r of board.all.value) { country[r.iso3] = r.country; language[r.iso639_3] = r.language }
|
||||
return { country, language }
|
||||
})
|
||||
const CODED = { access_level: 'level', access_detail: 'detail', band: 'band', way_in: 'wayIn' }
|
||||
function labelOne(key, v) {
|
||||
if (CODED[key]) return label(T, CODED[key], v)
|
||||
if (key === 'country') return names.value.country[v] || v
|
||||
if (key === 'language') return names.value.language[v] || v
|
||||
return v
|
||||
}
|
||||
function labelFor(key, value) {
|
||||
// a tree node narrows by its language codes: name the node, not a list of 120 codes
|
||||
if (key === 'language' && board.treeNode.value && board.tree.value) {
|
||||
const n = board.tree.value.byId.get(board.treeNode.value)
|
||||
if (n) return n.n
|
||||
}
|
||||
return [].concat(value).map((v) => labelOne(key, v)).join(', ')
|
||||
}
|
||||
provide('selectionBus', bus)
|
||||
provide('dashboardConfig', {
|
||||
facets: {
|
||||
access_level: { label: T('gospelInternet.facet.access', 'Access in their own language') },
|
||||
access_detail: { label: T('gospelInternet.col.access', 'In own language') },
|
||||
band: { label: T('gospelInternet.facet.band', 'Country internet use') },
|
||||
region: { label: T('gospelInternet.facet.region', 'Region') },
|
||||
country: { label: T('gospelInternet.facet.country', 'Country') },
|
||||
province: { label: T('gospelInternet.facet.province', 'India and China: state or province') },
|
||||
language: { label: T('gospelInternet.facet.language', 'Language') },
|
||||
religion: { label: T('gospelInternet.facet.religion', 'Religion') },
|
||||
way_in: { label: T('gospelInternet.facet.wayIn', 'Way in when nothing is in their language') },
|
||||
messaging: { label: T('gospelInternet.facet.messaging', 'Top messaging app (country)') },
|
||||
},
|
||||
facetRail: FACET_RAIL,
|
||||
legend: { labelFor },
|
||||
})
|
||||
|
||||
// ── the combinations ────────────────────────────────────────────────────────────────────────────────
|
||||
const COMBOS = computed(() => [
|
||||
{ id: 'digital-now', label: T('gospelInternet.combos.digitalNow', 'Nothing in their language, country 60%+ online'), filters: { access_detail: 'none', band: 'yes' } },
|
||||
{ id: 'audio-online', label: T('gospelInternet.combos.audioOnline', 'Hear but cannot read, country online'), filters: { access_detail: 'hear', band: ['yes', 'partial'] } },
|
||||
{ id: 'offline-gap', label: T('gospelInternet.combos.offlineGap', 'Nothing in their language, country mostly offline'), filters: { access_detail: 'none', band: ['no', 'nodata'] } },
|
||||
{ id: 'whatsapp', label: T('gospelInternet.combos.whatsapp', 'Nothing in their language, WhatsApp country'), filters: { access_detail: 'none', messaging: ['WhatsApp Messenger', 'WhatsApp Business'] } },
|
||||
{ id: 'telegram', label: T('gospelInternet.combos.telegram', 'Nothing in their language, Telegram country'), filters: { access_detail: 'none', messaging: 'Telegram Messenger' } },
|
||||
{ id: 'deaf', label: T('gospelInternet.combos.deaf', 'A Deaf bridge exists'), filters: { way_in: 'deaf' } },
|
||||
{ id: 'india-china-gap', label: T('gospelInternet.combos.indiaChinaGap', 'India and China: nothing in their language'), filters: { country: ['IND', 'CHN'], access_detail: 'none' } },
|
||||
])
|
||||
// live counts, over the rows of the current list, with a scratch bus (the board's own is not touched)
|
||||
const counts = computed(() => {
|
||||
const out = {}
|
||||
const rows = board.all.value
|
||||
for (const c of COMBOS.value) {
|
||||
const b = createSelectionBus()
|
||||
for (const [k, v] of Object.entries(c.filters)) b.setFilter(k, v)
|
||||
out[c.id] = filterRows(rows, b).length
|
||||
}
|
||||
return out
|
||||
})
|
||||
const same = (a, b) => JSON.stringify([].concat(a ?? []).slice().sort()) === JSON.stringify([].concat(b ?? []).slice().sort())
|
||||
const activeCombo = computed(() => {
|
||||
const keys = Object.keys(bus.filterSet)
|
||||
return COMBOS.value.find((c) => keys.length === Object.keys(c.filters).length
|
||||
&& Object.entries(c.filters).every(([k, v]) => same(bus.filterSet[k], v)))?.id || ''
|
||||
})
|
||||
function run(c) {
|
||||
if (activeCombo.value === c.id) { bus.clearFilters(); return }
|
||||
bus.clearFilters()
|
||||
for (const [k, v] of Object.entries(c.filters)) bus.setFilter(k, v)
|
||||
}
|
||||
const scoped = computed(() => Object.keys(bus.filterSet).length > 0)
|
||||
|
||||
useShadowStyles(
|
||||
'.gic2{grid-area:combos;display:flex;align-items:center;gap:10px;padding:6px 14px;background:#fff;border-bottom:1px solid #d1d4cd;' +
|
||||
'min-width:0;font:12.5px/1.3 Roboto,-apple-system,"Segoe UI",sans-serif;color:#3b463d}' +
|
||||
'.gic2-h{flex:none;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:#6d766e}' +
|
||||
'.gic2-sel{display:flex;align-items:center;gap:6px;min-width:0;flex:0 1 auto;max-width:45%}' +
|
||||
'.gic2-none{color:#9aa19b;font-style:italic;white-space:nowrap}' +
|
||||
'.gic2-try{display:flex;align-items:center;gap:6px;min-width:0;flex:1 1 auto;overflow-x:auto;scrollbar-width:thin}' +
|
||||
'.gic2-c{flex:none;display:inline-flex;align-items:center;gap:6px;border:1px solid #d1d4cd;background:#f3f3f1;color:#3b463d;border-radius:999px;' +
|
||||
'padding:3px 10px;min-height:26px;font:inherit;cursor:pointer;white-space:nowrap}' +
|
||||
'.gic2-c:hover{border-color:#92b195}' +
|
||||
'.gic2-c[aria-pressed="true"]{background:#3b463d;border-color:#3b463d;color:#fff}' +
|
||||
'.gic2-c:focus-visible{outline:2px solid #92b195;outline-offset:1px}' +
|
||||
'.gic2-n{font-variant-numeric:tabular-nums;font-weight:700}' +
|
||||
'.gic2-c[disabled]{opacity:.45;cursor:default}',
|
||||
'gi-combos',
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gic2" data-region="combos">
|
||||
<div class="gic2-sel">
|
||||
<span class="gic2-h">{{ T('gospelInternet.combos.selection', 'Your selection') }}</span>
|
||||
<DashboardFilterChips v-if="scoped" />
|
||||
<span v-else class="gic2-none">{{ T('gospelInternet.combos.none', 'Everything — pick a filter or a combination') }}</span>
|
||||
</div>
|
||||
<div class="gic2-try" role="group" :aria-label="T('gospelInternet.combos.try', 'Try a combination')">
|
||||
<span class="gic2-h">{{ T('gospelInternet.combos.try', 'Try a combination') }}</span>
|
||||
<button v-for="c in COMBOS" :key="c.id" type="button" class="gic2-c" :data-combo="c.id"
|
||||
:aria-pressed="activeCombo === c.id ? 'true' : 'false'" :disabled="!counts[c.id]"
|
||||
@click="run(c)">
|
||||
<span>{{ c.label }}</span><span class="gic2-n">{{ fmt(counts[c.id]) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -313,6 +313,8 @@ onMounted(async () => {
|
|||
// resize the canvas too, or it stretches the old bitmap.
|
||||
let ro = null
|
||||
onMounted(() => {
|
||||
// A phone opens on the map, not on its legend: at 390px the card covered most of the map (rega r75).
|
||||
if (mapEl.value && mapEl.value.clientWidth && mapEl.value.clientWidth < 520) legendOpen.value = false
|
||||
if (mapEl.value && typeof ResizeObserver !== 'undefined') {
|
||||
ro = new ResizeObserver(() => { try { mapRef.value && mapRef.value.resize() } catch (_) {} })
|
||||
ro.observe(mapEl.value)
|
||||
|
|
@ -335,7 +337,8 @@ useShadowStyles(
|
|||
'background:rgba(255,255,255,.96);color:#3b463d;border:1px solid #d1d4cd;border-radius:10px;padding:8px 10px;' +
|
||||
'font:12px/1.4 Roboto,-apple-system,"Segoe UI",sans-serif;box-shadow:0 2px 10px rgba(0,0,0,.10)}' +
|
||||
'.gim-lh{display:flex;align-items:center;gap:8px;font-weight:600;font-size:12px;margin-bottom:4px}' +
|
||||
'.gim-lh button{margin-inline-start:auto;border:0;background:none;color:#6d766e;cursor:pointer;font:inherit;min-width:24px;min-height:24px}' +
|
||||
// a real 28px tap target (WCAG 2.5.8 wants 24): the glyph alone measured 16px (rega r75)
|
||||
'.gim-lh button{margin-inline-start:auto;border:0;background:none;color:#6d766e;cursor:pointer;font:inherit;display:inline-grid;place-items:center;width:28px;height:28px;padding:0;flex:none}' +
|
||||
'.gim-keys{display:flex;flex-wrap:wrap;gap:3px 10px;margin-bottom:6px}' +
|
||||
'.gim-key{display:inline-flex;align-items:center;gap:5px;font-size:11px}' +
|
||||
'.gim-dot{width:11px;height:11px;border-radius:50%;box-sizing:border-box;border:1.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.2);flex:none}' +
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import { useBoardState } from '../useBoard.js'
|
|||
import { useGiT } from '../useGiT.js'
|
||||
import { createBoardMap, reducedMotion } from '../boardEngine.js'
|
||||
import { treeScope, TREE_COLOR } from '../model.js'
|
||||
import { buildTreePoster, posterPrintHtml } from '../treePoster.js'
|
||||
import { openPrintFrame } from '@map/composables/printInPlace.js'
|
||||
import { listLabel } from '@map/lists/peopleGroupList.js'
|
||||
|
||||
const T = useGiT()
|
||||
const board = useBoardState()
|
||||
|
|
@ -239,6 +242,7 @@ async function build() {
|
|||
} catch (_) {}
|
||||
}
|
||||
onMounted(() => {
|
||||
if (el.value && el.value.clientWidth && el.value.clientWidth < 520) legendOpen.value = false // a phone opens on the tree
|
||||
if (el.value && typeof ResizeObserver !== 'undefined') {
|
||||
// the engines only follow WINDOW resizes; a dragged rail or an un-hidden panel must resize the canvas
|
||||
ro = new ResizeObserver(() => { try { mapRef.value && mapRef.value.resize() } catch (_) {} })
|
||||
|
|
@ -267,6 +271,39 @@ const totals = computed(() => {
|
|||
for (const n of ix.value.nodes) if (!n.p) { const s = scope.value.get(n.id); t.g += s.g; t.y += s.y; t.r += s.r }
|
||||
return t
|
||||
})
|
||||
// ── the poster: the current selection, as one vector sheet with no overlapping labels ───────────────
|
||||
const posterSize = ref(36)
|
||||
const posterNote = ref('')
|
||||
function makePoster() {
|
||||
const t = ix.value
|
||||
if (!t || !scope.value) return null
|
||||
const size = posterSize.value
|
||||
const list = listLabel(board.listId.value, (k) => T(k, '')) || ''
|
||||
const { svg, stats } = buildTreePoster(t, scope.value, {
|
||||
sizeIn: size,
|
||||
title: T('gospelInternet.tree.posterTitle', 'Gospel access, language family by language family'),
|
||||
subtitle: [list, T('gospelInternet.tree.legendTitle', 'In their own language')].filter(Boolean).join(' · '),
|
||||
legend: ['g', 'y', 'r'].map((s) => ({ col: TREE_COLOR[s], text: T('gospelInternet.tree.st.' + s, ST_EN[s]) })),
|
||||
footer: [T('gospelInternet.tree.sources', 'Tree: Glottolog (CC BY 4.0). Resources: eBible, Faith Comes By Hearing, Scripture Earth, PNG.Bible, JESUS Film, Global Recordings and others, keyed by ISO 639-3.'), citation.value].filter(Boolean),
|
||||
omittedText: (n) => T('gospelInternet.tree.posterOmitted', '{n} names left off where they would overlap another; every circle is drawn.', { n: fmt(n) }),
|
||||
})
|
||||
posterNote.value = stats.omitted ? T('gospelInternet.tree.posterOmitted', '{n} names left off where they would overlap another; every circle is drawn.', { n: fmt(stats.omitted) }) : ''
|
||||
return { svg, size }
|
||||
}
|
||||
function printPoster() {
|
||||
const p = makePoster()
|
||||
if (p) openPrintFrame(posterPrintHtml(p.svg, p.size, T('gospelInternet.tree.posterTitle', 'Gospel access, language family by language family')), { autoprint: true })
|
||||
}
|
||||
function downloadPoster() {
|
||||
const p = makePoster()
|
||||
if (!p) return
|
||||
try {
|
||||
const url = URL.createObjectURL(new Blob([p.svg], { type: 'image/svg+xml;charset=utf-8' }))
|
||||
const a = document.createElement('a'); a.href = url; a.download = `gospel-language-tree-${p.size}in.svg`; a.style.display = 'none'
|
||||
document.body.appendChild(a); a.click(); setTimeout(() => { try { URL.revokeObjectURL(url); a.remove() } catch (_) {} }, 200)
|
||||
} catch (_) { /* a sandbox without downloads: the print path still works */ }
|
||||
}
|
||||
|
||||
const ST_EN = { g: 'New Testament or full Bible found', y: 'Something found, but no New Testament', r: 'Nothing found' }
|
||||
const citation = computed(() => (ix.value && ix.value.meta && ix.value.meta.citation) || '')
|
||||
|
||||
|
|
@ -286,11 +323,14 @@ useShadowStyles(
|
|||
'.gtr-card button{margin-top:6px;border:1px solid #d1d4cd;background:#fff;color:#3b463d;border-radius:6px;min-height:26px;padding:2px 10px;font:inherit;cursor:pointer}' +
|
||||
'.gtr-legend{inset-block-end:10px;inset-inline-start:10px;padding:8px 10px;max-width:min(360px,calc(100% - 64px))}' +
|
||||
'.gtr-lh{display:flex;align-items:center;font-weight:700;margin-bottom:4px}' +
|
||||
'.gtr-lh button{margin-inline-start:auto;border:0;background:none;color:#6d766e;cursor:pointer;min-width:24px;min-height:24px;font:inherit}' +
|
||||
'.gtr-lh button{margin-inline-start:auto;border:0;background:none;color:#6d766e;cursor:pointer;font:inherit;display:inline-grid;place-items:center;width:28px;height:28px;padding:0;flex:none}' +
|
||||
'.gtr-row{display:flex;align-items:flex-start;gap:6px;margin:2px 0}' +
|
||||
'.gtr-row .gtr-dot{margin-top:3px}' +
|
||||
'.gtr-levels{margin:6px 0 2px;font-size:11.5px}' +
|
||||
'.gtr-note{font-size:10.5px;color:#6d766e;margin-top:4px}' +
|
||||
'.gtr-poster{display:flex;align-items:center;gap:6px;margin-top:6px;padding-top:6px;border-top:1px solid #e6e8e3;font-weight:600}' +
|
||||
'.gtr-poster select,.gtr-poster button{font:inherit;font-weight:400;font-size:12px;border:1px solid #d1d4cd;background:#fff;color:#3b463d;border-radius:6px;min-height:26px;padding:2px 8px;cursor:pointer}' +
|
||||
'.gtr-poster button:focus-visible,.gtr-poster select:focus-visible{outline:2px solid #92b195;outline-offset:1px}' +
|
||||
'.gtr-min{position:absolute;inset-block-end:10px;inset-inline-start:10px;z-index:4;border:1px solid #d1d4cd;background:#fff;color:#3b463d;' +
|
||||
'border-radius:8px;padding:5px 10px;min-height:28px;font:600 12px Roboto,sans-serif;cursor:pointer}',
|
||||
'gi-tree',
|
||||
|
|
@ -331,6 +371,16 @@ useShadowStyles(
|
|||
<div class="gtr-row"><span class="gtr-dot" :style="{ background: TREE_COLOR.y }"></span><span>{{ T('gospelInternet.tree.st.y', 'Something found, but no New Testament') }}<template v-if="totals"> · {{ fmt(totals.y) }}</template></span></div>
|
||||
<div class="gtr-row"><span class="gtr-dot" :style="{ background: TREE_COLOR.r }"></span><span>{{ T('gospelInternet.tree.st.r', 'Nothing found') }}<template v-if="totals"> · {{ fmt(totals.r) }}</template></span></div>
|
||||
<div class="gtr-levels">{{ T('gospelInternet.tree.levels', 'Family › branch › language › dialect. A family or branch shows the worst colour inside it. A dialect shows its language\'s colour, lighter: resources are catalogued per language, not per dialect. Click a circle to narrow the board to it.') }}</div>
|
||||
<div class="gtr-poster" data-tree-poster>
|
||||
<span>{{ T('gospelInternet.tree.poster', 'Poster') }}</span>
|
||||
<select v-model.number="posterSize" :aria-label="T('gospelInternet.tree.posterSizeAria', 'Poster size')">
|
||||
<option :value="36">{{ T('gospelInternet.tree.poster3ft', '3 × 3 ft') }}</option>
|
||||
<option :value="48">{{ T('gospelInternet.tree.poster4ft', '4 × 4 ft') }}</option>
|
||||
</select>
|
||||
<button type="button" @click="printPoster">{{ T('gospelInternet.tree.posterPrint', 'Print') }}</button>
|
||||
<button type="button" @click="downloadPoster">{{ T('gospelInternet.tree.posterSvg', 'Download SVG') }}</button>
|
||||
</div>
|
||||
<div v-if="posterNote" class="gtr-note">{{ posterNote }}</div>
|
||||
<div class="gtr-note">{{ T('gospelInternet.tree.sources', 'Tree: Glottolog (CC BY 4.0). Resources: eBible, Faith Comes By Hearing, Scripture Earth, PNG.Bible, JESUS Film, Global Recordings and others, keyed by ISO 639-3.') }} {{ citation }}</div>
|
||||
</div>
|
||||
<button v-else type="button" class="gtr-min" @click="legendOpen = true">{{ T('gospelInternet.legend.show', 'Legend') }}</button>
|
||||
|
|
|
|||
175
plugin-designs/gospel-internet/src/treePoster.js
Normal file
175
plugin-designs/gospel-internet/src/treePoster.js
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* treePoster.js — the language tree as a PRINTABLE POSTER: one vector SVG, 36 x 36 inches by default,
|
||||
* every family, branch, language and dialect in its colour, and every label placed so that NO TWO
|
||||
* LABELS OVERLAP (the agent, 2026-09-27: a 3 ft x 3 ft poster "that had no overlaps").
|
||||
*
|
||||
* VECTOR, NOT PIXELS: an SVG prints sharp at any size, so a 3-foot sheet needs no giant canvas (a WebGL
|
||||
* or 2D canvas that large exceeds what many browsers allow). The map engine's own label placement is
|
||||
* per-screen; a poster needs its placement solved ONCE for the sheet, which is what this does.
|
||||
*
|
||||
* LABELS: placed greedily in priority order (families, then languages with the most groups, then
|
||||
* branches, then dialects), each trying below / above / right / left of its dot, and kept only where it
|
||||
* overlaps no placed label and no dot. What cannot be placed is COUNTED and said on the sheet, never
|
||||
* drawn over another. Text width is estimated per character (0.56 em, a conservative Latin average); a
|
||||
* margin pads every box so the estimate errs toward space.
|
||||
*
|
||||
* Pure: no DOM, no Vue. Returns { svg, stats }. The board prints it through library printInPlace.
|
||||
*/
|
||||
const UNIT = 72 // SVG user units per inch (1 pt)
|
||||
const COL = { g: '#1f8f5f', y: '#e3a82b', r: '#b42318' }
|
||||
const esc = (s) => String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
||||
|
||||
/** Web Mercator x/y (0..1) of a node's lng/lat: the space the tree was laid out in, undistorted. */
|
||||
function merc([lng, lat]) {
|
||||
const s = Math.sin(lat * Math.PI / 180)
|
||||
return [(lng + 180) / 360, 0.5 - Math.log((1 + s) / (1 - s)) / (4 * Math.PI)]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} ix indexTree(doc) (+ .nodes with c, lv, n, R, st, inh)
|
||||
* @param {Map} scope treeScope(ix, rows): per node { col, m, g, y, r, groups }
|
||||
* @param {object} o { sizeIn=36, title, subtitle, legend:[{col,text}], footer, omittedText(n) }
|
||||
*/
|
||||
export function buildTreePoster(ixIn, scope, o = {}) {
|
||||
let ix = ixIn
|
||||
const sizeIn = o.sizeIn || 36
|
||||
const W = sizeIn * UNIT
|
||||
const margin = 1.2 * UNIT, head = 2.4 * UNIT, foot = 2.2 * UNIT
|
||||
const area = { x: margin, y: margin + head, w: W - 2 * margin, h: W - 2 * margin - head - foot }
|
||||
|
||||
// The POSTER layout (pc, pR: wider leaf spacing, from build-language-tree.mjs) when the tree carries it,
|
||||
// else the screen one. A sheet cannot zoom, so it needs the layout made for one fixed scale.
|
||||
// The poster draws LANGUAGES, not dialects, by default: its layout gives dialects no slot (see the builder).
|
||||
const drawn = ix.nodes.filter((n) => Array.isArray(n.pc))
|
||||
const usePoster = drawn.length > 0
|
||||
if (usePoster) ix = { ...ix, nodes: drawn }
|
||||
const xy = (n) => (usePoster ? n.pc : merc(n.c))
|
||||
const rad = (n) => (usePoster ? (n.pR || 0) : (n.R || 0))
|
||||
// fit the tapestry (dishes included) into the drawing area
|
||||
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity
|
||||
for (const n of ix.nodes) {
|
||||
const [x, y] = xy(n), R = n.p ? 0 : rad(n)
|
||||
x0 = Math.min(x0, x - R); x1 = Math.max(x1, x + R); y0 = Math.min(y0, y - R); y1 = Math.max(y1, y + R)
|
||||
}
|
||||
const k = Math.min(area.w / (x1 - x0), area.h / (y1 - y0))
|
||||
const ox = area.x + (area.w - (x1 - x0) * k) / 2, oy = area.y + (area.h - (y1 - y0) * k) / 2
|
||||
const P = (n) => { const [x, y] = xy(n); return [ox + (x - x0) * k, oy + (y - y0) * k] }
|
||||
|
||||
const DOT = { f: 5, l: 2.4, b: 1.4, d: 1.1 }
|
||||
const FONT = { f: 15, l: 5.2, b: 4.2, d: 3.6 } // points; dialects ~1.3 mm, still legible on paper
|
||||
const pos = new Map(ix.nodes.map((n) => [n.id, P(n)]))
|
||||
|
||||
// ── label placement with a spatial grid of occupied boxes ─────────────────────────────────────────
|
||||
const CELL = 24
|
||||
const grid = new Map()
|
||||
const boxes = []
|
||||
const keyOf = (i, j) => i + ',' + j
|
||||
const cellsOf = (b) => { const out = []; for (let i = Math.floor(b[0] / CELL); i <= Math.floor(b[2] / CELL); i++) for (let j = Math.floor(b[1] / CELL); j <= Math.floor(b[3] / CELL); j++) out.push(keyOf(i, j)); return out }
|
||||
const hits = (b) => { for (const c of cellsOf(b)) for (const q of grid.get(c) || []) if (b[0] < q[2] && b[2] > q[0] && b[1] < q[3] && b[3] > q[1]) return true; return false }
|
||||
const occupy = (b) => { boxes.push(b); for (const c of cellsOf(b)) { if (!grid.has(c)) grid.set(c, []); grid.get(c).push(b) } }
|
||||
// dots are obstacles too: a label never covers another circle
|
||||
for (const n of ix.nodes) { const [x, y] = pos.get(n.id), r = (DOT[n.lv] || 1) + 0.4; occupy([x - r, y - r, x + r, y + r]) }
|
||||
|
||||
const order = ix.nodes.filter((n) => n.n).map((n) => {
|
||||
const s = scope.get(n.id) || {}
|
||||
const rank = !n.p ? 0 : n.lv === 'l' ? 1 : n.lv === 'b' ? 2 : 3
|
||||
return { n, rank, groups: s.groups || 0 }
|
||||
}).sort((a, b) => a.rank - b.rank || b.groups - a.groups || a.n.n.localeCompare(b.n.n))
|
||||
|
||||
const labels = []
|
||||
const dropped = { f: 0, b: 0, l: 0, d: 0 }
|
||||
const kids = ix.kids || new Map()
|
||||
// a label along a direction, as a chain of small boxes (so the axis-aligned grid can test it)
|
||||
const chain = (sx, sy, cos, sin, w, fs) => {
|
||||
const out = [], step = fs * 0.9
|
||||
for (let t = 0; t <= w; t += step) { const cx = sx + cos * (t + step / 2), cy = sy + sin * (t + step / 2), r = fs * 0.55; out.push([cx - r, cy - r, cx + r, cy + r]) }
|
||||
return out
|
||||
}
|
||||
for (const { n } of order) {
|
||||
const lv = !n.p ? 'f' : n.lv
|
||||
const fs = FONT[lv], w = String(n.n).length * fs * 0.56 + 1, h = fs * 1.15
|
||||
const [x, y] = pos.get(n.id), d = (DOT[lv] || 1) + 1
|
||||
let placed = null
|
||||
// 1. a FAMILY's name sits on top of its dish, outside its crowded tree
|
||||
if (lv === 'f') {
|
||||
const top = y - rad(n) * k - 6
|
||||
const b = [x - w / 2 - 0.5, top - fs, x + w / 2 + 0.5, top + fs * 0.25]
|
||||
if (!hits(b)) placed = { boxes: [b], anchor: 'middle', tx: x, ty: top, rot: 0 }
|
||||
}
|
||||
// 2. a LEAF's name runs outward along its branch, like a spoke (the radial-tree convention)
|
||||
if (!placed && n.p && !(kids.get(n.id) || []).length) {
|
||||
const [px, py] = pos.get(n.p)
|
||||
const L = Math.hypot(x - px, y - py) || 1, cos = (x - px) / L, sin = (y - py) / L
|
||||
const sx = x + cos * d, sy = y + sin * d
|
||||
const bs = chain(sx, sy, cos, sin, w, fs)
|
||||
if (!bs.some(hits)) {
|
||||
let deg = Math.atan2(sin, cos) * 180 / Math.PI
|
||||
const flip = cos < 0
|
||||
if (flip) deg += 180
|
||||
placed = { boxes: bs, anchor: flip ? 'end' : 'start', tx: sx, ty: sy, rot: deg, dy: fs * 0.35 }
|
||||
}
|
||||
}
|
||||
// 3. otherwise below / above / right / left of the dot
|
||||
if (!placed) {
|
||||
const tries = [
|
||||
[x - w / 2, y + d, 'middle', x, y + d + fs * 0.9],
|
||||
[x - w / 2, y - d - h, 'middle', x, y - d - fs * 0.25],
|
||||
[x + d, y - h / 2, 'start', x + d, y + fs * 0.35],
|
||||
[x - d - w, y - h / 2, 'end', x - d, y + fs * 0.35],
|
||||
]
|
||||
for (const [bx, by, anchor, tx, ty] of tries) {
|
||||
const b = [bx - 0.5, by - 0.5, bx + w + 0.5, by + h + 0.5]
|
||||
if (!hits(b)) { placed = { boxes: [b], anchor, tx, ty, rot: 0 }; break }
|
||||
}
|
||||
}
|
||||
if (!placed) { dropped[lv]++; continue }
|
||||
placed.boxes.forEach(occupy)
|
||||
labels.push({ lv, text: n.n, x: placed.tx, y: placed.ty, anchor: placed.anchor, fs, rot: placed.rot, dy: placed.dy || 0,
|
||||
m: (scope.get(n.id) || {}).m, boxes: placed.boxes })
|
||||
}
|
||||
|
||||
// ── the drawing ───────────────────────────────────────────────────────────────────────────────────
|
||||
const parts = []
|
||||
parts.push(`<rect x="0" y="0" width="${W}" height="${W}" fill="#f3f3f1"/>`)
|
||||
for (const n of ix.nodes) if (!n.p) {
|
||||
const [x, y] = pos.get(n.id)
|
||||
parts.push(`<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="${(rad(n) * k).toFixed(1)}" fill="#ffffff" fill-opacity="0.7" stroke="#d1d4cd" stroke-width="0.8"/>`)
|
||||
}
|
||||
const links = []
|
||||
for (const n of ix.nodes) if (n.p) {
|
||||
const [a, b] = pos.get(n.p), [c, d] = pos.get(n.id)
|
||||
links.push(`M${a.toFixed(1)} ${b.toFixed(1)}L${c.toFixed(1)} ${d.toFixed(1)}`)
|
||||
}
|
||||
parts.push(`<path d="${links.join('')}" stroke="#8d968e" stroke-width="0.35" stroke-opacity="0.7" fill="none"/>`)
|
||||
for (const n of ix.nodes) {
|
||||
const [x, y] = pos.get(n.id), s = scope.get(n.id) || {}
|
||||
const col = COL[s.col] || '#9aa2ad', lv = !n.p ? 'f' : n.lv
|
||||
const op = s.m === 0 ? 0.2 : n.inh ? 0.5 : 1
|
||||
parts.push(`<circle cx="${x.toFixed(1)}" cy="${y.toFixed(1)}" r="${DOT[lv] || 1}" fill="${col}" fill-opacity="${op}" stroke="#fff" stroke-width="${lv === 'f' ? 1 : 0.3}"/>`)
|
||||
}
|
||||
for (const l of labels) {
|
||||
const weight = l.lv === 'f' ? 700 : 400, fill = l.lv === 'b' || l.lv === 'd' ? '#6d766e' : '#3b463d'
|
||||
const rot = l.rot ? ` transform="rotate(${l.rot.toFixed(1)} ${l.x.toFixed(1)} ${l.y.toFixed(1)})" dy="${l.dy.toFixed(1)}"` : ''
|
||||
parts.push(`<text x="${l.x.toFixed(1)}" y="${l.y.toFixed(1)}"${rot} font-size="${l.fs}" font-weight="${weight}" text-anchor="${l.anchor}" fill="${fill}"${l.m === 0 ? ' fill-opacity="0.45"' : ''}>${esc(l.text)}</text>`)
|
||||
}
|
||||
// title, legend, footer
|
||||
const omitted = dropped.f + dropped.b + dropped.l + dropped.d
|
||||
parts.push(`<text x="${margin}" y="${margin + 40}" font-size="44" font-weight="700" fill="#3b463d">${esc(o.title || '')}</text>`)
|
||||
parts.push(`<text x="${margin}" y="${margin + 80}" font-size="20" fill="#6d766e">${esc(o.subtitle || '')}</text>`)
|
||||
let lx = margin
|
||||
for (const it of o.legend || []) {
|
||||
parts.push(`<circle cx="${lx + 9}" cy="${margin + 122}" r="9" fill="${it.col}"/><text x="${lx + 26}" y="${margin + 129}" font-size="20" fill="#3b463d">${esc(it.text)}</text>`)
|
||||
lx += 60 + String(it.text).length * 11
|
||||
}
|
||||
const fy = W - margin - foot + 50
|
||||
for (const [i, line] of [...(o.footer || []), omitted && o.omittedText ? o.omittedText(omitted) : ''].filter(Boolean).entries()) {
|
||||
parts.push(`<text x="${margin}" y="${fy + i * 26}" font-size="16" fill="#6d766e">${esc(line)}</text>`)
|
||||
}
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${sizeIn}in" height="${sizeIn}in" viewBox="0 0 ${W} ${W}" font-family="'Noto Sans','Roboto','Segoe UI',Arial,sans-serif">${parts.join('')}</svg>`
|
||||
return { svg, stats: { nodes: ix.nodes.length, labels: labels.length, dropped, omitted, labelBoxes: labels.map((l) => l.boxes), sizeIn } }
|
||||
}
|
||||
|
||||
/** The print page: one sheet of exactly the poster's size, no margins, nothing but the poster. */
|
||||
export function posterPrintHtml(svg, sizeIn = 36, title = '') {
|
||||
return `<!doctype html><html><head><meta charset="utf-8"><title>${esc(title)}</title><style>@page{size:${sizeIn}in ${sizeIn}in;margin:0}html,body{margin:0;padding:0}svg{display:block;width:${sizeIn}in;height:${sizeIn}in}</style></head><body>${svg}</body></html>`
|
||||
}
|
||||
|
|
@ -223,12 +223,14 @@ const intro = computed(() => {
|
|||
const b = t('messages.upg100WallIntroEmph')
|
||||
const c = t('messages.upg100WallIntroB')
|
||||
const missing = (v, key) => !v || v === key
|
||||
if (missing(a, 'messages.upg100WallIntroA') || missing(b, 'messages.upg100WallIntroEmph')) {
|
||||
// ⚠ THE LEGACY KEY STILL CONTAINS THE MARKUP, so it is passed through unescaped exactly as before.
|
||||
// Escaping it here would print a literal "<strong>" to the reader — a visible regression traded for
|
||||
// a console warning, which is the wrong way round.
|
||||
return t('messages.upg100WallIntro')
|
||||
}
|
||||
// ★ THE LEGACY upg100WallIntro FALLBACK IS GONE (2026-09-27). It existed for exactly one window: the
|
||||
// three plain-text keys were landing in studio's catalogue while this consumer was already written,
|
||||
// so the branch rendered the old single markup-bearing key until they arrived. They are on main in
|
||||
// all eleven locales, so the branch is now DEAD CODE — and dead code that names a key keeps that key
|
||||
// alive: studio cannot delete upg100WallIntro while this line reads it (their L13U measurement).
|
||||
// A fallback whose condition can no longer be true is not safety, it is a reference.
|
||||
// If A or Emph ever went missing the per-key fallback still yields English from the en block, and
|
||||
// check:i18n-keys-exist fails the build before a reader could see it.
|
||||
const tail = missing(c, 'messages.upg100WallIntroB') ? '' : escHtml(c)
|
||||
return `${escHtml(a)}<strong>${escHtml(b)}</strong>${tail}`
|
||||
})
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
|
||||
<script src="../country-posters.js" integrity="sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
|
||||
<script src="../country-posters.js" integrity="sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
|
||||
<script src="../country-posters.js" integrity="sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -165,7 +165,7 @@
|
|||
"bundle": "country-posters",
|
||||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"output": "dx-maps/country-posters/country-posters.js",
|
||||
"integrity": "sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak",
|
||||
"integrity": "sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ",
|
||||
"elements": [
|
||||
"country-posters-map"
|
||||
],
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -604,7 +604,7 @@
|
|||
"bundle": "gospel-internet",
|
||||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"output": "dx-maps/gospel-internet/gospel-internet.js",
|
||||
"integrity": "sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6",
|
||||
"integrity": "sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk",
|
||||
"elements": [
|
||||
"gospel-internet-board"
|
||||
],
|
||||
|
|
@ -623,7 +623,7 @@
|
|||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-internet/board/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-internet/gospel-internet.js\" integrity=\"sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
"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-internet/gospel-internet.js\" integrity=\"sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -814,7 +814,7 @@
|
|||
"bundle": "my-upg-100-list",
|
||||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"output": "dx-maps/my-upg-100-list/my-upg-100-list.js",
|
||||
"integrity": "sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5",
|
||||
"integrity": "sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW",
|
||||
"elements": [
|
||||
"my-upg-100-list-map"
|
||||
],
|
||||
|
|
@ -836,7 +836,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -851,7 +851,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -866,7 +866,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <gospel-internet-board> custom element. -->
|
||||
<script src="../../gospel-internet.js" integrity="sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6" crossorigin="anonymous"></script>
|
||||
<script src="../../gospel-internet.js" integrity="sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -165,7 +165,7 @@
|
|||
"bundle": "country-posters",
|
||||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"output": "dx-maps/country-posters/country-posters.js",
|
||||
"integrity": "sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak",
|
||||
"integrity": "sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ",
|
||||
"elements": [
|
||||
"country-posters-map"
|
||||
],
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-nLy4+pD4T/7yHZJ/6L3+l5ZkjovvoZ+UdwLMZLbS50uo+KfBEfkDSZTko8/EuYak\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./country-posters/country-posters.js\" integrity=\"sha384-a7b8lN+KcKMcBv8m4itlWhBOJIORYvSz6t43qidfB5ghCedu59a4ZQP4Klh8glxZ\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -604,7 +604,7 @@
|
|||
"bundle": "gospel-internet",
|
||||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"output": "dx-maps/gospel-internet/gospel-internet.js",
|
||||
"integrity": "sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6",
|
||||
"integrity": "sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk",
|
||||
"elements": [
|
||||
"gospel-internet-board"
|
||||
],
|
||||
|
|
@ -623,7 +623,7 @@
|
|||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./gospel-internet/board/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-internet/gospel-internet.js\" integrity=\"sha384-5Cv+d2R8SZza4WyDkOZ1m1MC7WPbc1iVPu4mBmZ5iHx0ScaPZrKoJPWxWaJ2M9x6\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
"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-internet/gospel-internet.js\" integrity=\"sha384-e3A59fHvm9wks4/2lsOVUKuhIaLpyjBtX9h4HPFly79Vi9ux2BG1SFTCM9QVJxvk\" crossorigin=\"anonymous\"></script>\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -814,7 +814,7 @@
|
|||
"bundle": "my-upg-100-list",
|
||||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"output": "dx-maps/my-upg-100-list/my-upg-100-list.js",
|
||||
"integrity": "sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5",
|
||||
"integrity": "sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW",
|
||||
"elements": [
|
||||
"my-upg-100-list-map"
|
||||
],
|
||||
|
|
@ -836,7 +836,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -851,7 +851,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -866,7 +866,7 @@
|
|||
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<!-- Mapbox engine, fetched early. 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=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW\" crossorigin=\"anonymous\"></script>\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-yZNsb36OobOoD86nr2OKMrtXS4SqxMY6dxMsBwveUzDTrdaBCTWU4ebsQ2miIeI5" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-ArVvltGZK+YCdifi5DCHjXkcrWUm1Nz9LpC5doGzcZ54pQI+eKG46BG7tOUFGWyW" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue