Merge the pilot (6d89e6b8c): India tab + regions
Takes the pilot's side for both conflicted files (countryPosterType.js = the India tab's withheld API; check-poster-section.mjs = the pilot's) so no combined resolution is hidden in the merge. The sort tie-break (PosterWall.vue) and its gate arm (check-poster-wall-host-seams) auto-merge unchanged. The phone-limit and the renamed section-gate arm follow as separate commits on top.
This commit is contained in:
commit
464c1c729a
159 changed files with 6696 additions and 1664 deletions
|
|
@ -13,7 +13,7 @@
|
|||
> bundle `parameterized` (cap-2) no matter how light it looks, and a `meta.json` `type`
|
||||
> declares tiers 3/4/5.
|
||||
|
||||
Generated from 28 bundle(s).
|
||||
Generated from 29 bundle(s).
|
||||
|
||||
### One canonical exemplar per tier (generated — quote these)
|
||||
|
||||
|
|
@ -56,6 +56,7 @@ Generated from 28 bundle(s).
|
|||
| `poster-design` | `poster-sizes` | `single` |
|
||||
| `poster-design` | `poster-styles` | `single` |
|
||||
| `prayer-walk-planner` | `prayer-walk-planner` | `single` |
|
||||
| `regions-dashboard` | `country-poster-sections` | `single` |
|
||||
| `regions-dashboard` | `region-dashboards` | `single` |
|
||||
| `regions-dashboard` | `regions-list` | `single` |
|
||||
|
||||
|
|
@ -67,6 +68,7 @@ Generated from 28 bundle(s).
|
|||
| `doxa-simple-map` | `doxa-simple-map` | `parameterized` |
|
||||
| `doxa-vision-card` | `doxa-vision-card` | `parameterized` |
|
||||
| `people-groups-grid` | `people-groups-grid` | `parameterized` |
|
||||
| `poster-sections` | `poster-sections` | `parameterized` |
|
||||
|
||||
## Capability 3 — multi-view-element (monolithic)
|
||||
|
||||
|
|
|
|||
139
internal/check-host-floor.mjs
Normal file
139
internal/check-host-floor.mjs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-host-floor.mjs — the profile floor is decided against the host WITHOUT disturbing live content.
|
||||
*
|
||||
* library/elements/hostFloor.js (used by ProfileLoader.vue) probes the host at mount, and after that re-probes ONLY
|
||||
* when the host may have changed kind, saving and restoring every scrolled descendant. The regression this pins:
|
||||
* the probe ran on EVERY host resize, collapsing the loader under live content; a scrolling child's scrollTop was
|
||||
* clamped to 0 and the embedded UPG-100 app flipped from Map back to its first view whenever a phone's URL bar
|
||||
* resized the host.
|
||||
*
|
||||
* A stub DOM models the layout that matters: the host's height from its mode and the loader's min-height, the
|
||||
* floor (100svh) as a number the test moves like a window resize, and a scrolled CHILD whose scrollTop clamps to 0
|
||||
* whenever the loader collapses.
|
||||
* A. after mount, a content-sized host following a window resize, and a sized host that keeps its size, never
|
||||
* write the loader's min-height nor move the child's scrollTop;
|
||||
* B. a sized host (523 px) is filled: the floor goes off at mount;
|
||||
* C. a host styled SHORTER than the floor after mount flips to sized ONCE, then stays;
|
||||
* D. a host capped by max-height (600 < floor) is found at mount: 0 flips and 0 re-probes over 20 resizes;
|
||||
* E. a max-height cap that arrives AFTER mount: at most 1 flip and 1 re-probe over 20 resizes, ends content-sized;
|
||||
* F. a sized host restyled to height:auto with NO content gets the floor back;
|
||||
* H. a sized host restyled to height:auto WITH in-flow content (420) gets the floor back: at most 1 flip over 20
|
||||
* resizes, and the scrolled child keeps its scrollTop through the re-probe;
|
||||
* G. an inline host is left alone after mount.
|
||||
* `--self-test`: the old per-resize probe (A), a missing capped latch (D/E re-probe in a loop), a missing re-check
|
||||
* of a sized host (H stays sized) and a missing scroll restore (H loses the child's scrollTop) must each FAIL.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const SRC = join(ROOT, 'library/elements/hostFloor.js')
|
||||
const asModule = (src) => import('data:text/javascript;base64,' + Buffer.from(src).toString('base64'))
|
||||
|
||||
// mode: 'content' (height = max(content, min-height)), 'sized' (a fixed height), 'capped' (content-sized up to a cap)
|
||||
function world({ mode = 'content', fixed = 523, cap = 600, content = 0, floor = 844, display = 'block' } = {}) {
|
||||
const w = { mode, fixed, cap, content, floor, display, writes: [], child: { scrollTop: 787 } }
|
||||
const attrs = new Set()
|
||||
const minPx = () => {
|
||||
const v = loader.style._min
|
||||
if (v === '' || v === undefined) return w.floor
|
||||
return parseFloat(v)
|
||||
}
|
||||
w.hostH = () => {
|
||||
if (w.mode === 'sized') return w.fixed
|
||||
const natural = Math.max(w.content, minPx())
|
||||
return w.mode === 'capped' ? Math.min(natural, w.cap) : natural
|
||||
}
|
||||
const loader = {
|
||||
style: {
|
||||
_min: '',
|
||||
get minHeight() { return this._min },
|
||||
set minHeight(v) {
|
||||
this._min = v; w.writes.push(v)
|
||||
if (v === '0px' && w.mode !== 'sized') w.child.scrollTop = 0 // the loader collapsed: the child clamps
|
||||
},
|
||||
},
|
||||
hasAttribute: (n) => attrs.has(n), setAttribute: (n) => attrs.add(n), removeAttribute: (n) => attrs.delete(n),
|
||||
}
|
||||
const host = { getBoundingClientRect: () => ({ height: w.hostH() }) }
|
||||
w.loader = loader; w.host = host
|
||||
w.style = (n) => (n === host ? { display: w.display, minHeight: '0px' } : { display: 'block', minHeight: (loader.style._min === '' ? w.floor : parseFloat(loader.style._min)) + 'px' })
|
||||
w.sized = () => attrs.has('data-host-sized')
|
||||
w.probes = () => w.writes.filter((v) => v === '100000px').length
|
||||
return w
|
||||
}
|
||||
|
||||
async function audit(src) {
|
||||
const { createHostFloor } = await asModule(src)
|
||||
const fails = []
|
||||
let t = 0
|
||||
const make = (opts) => { const w = world(opts); w.f = createHostFloor(w.loader, w.host, { style: w.style, scrolled: () => (w.child.scrollTop ? [w.child] : []) }); return w }
|
||||
|
||||
// A: content-sized host, a window resize moves the floor and the host with it
|
||||
{ const w = make({ mode: 'content', content: 0 }); w.f.probeOnce(); w.child.scrollTop = 787; w.writes.length = 0
|
||||
for (const fl of [830, 760, 700, 844]) { w.floor = fl; w.f.follow() }
|
||||
if (w.child.scrollTop !== 787) fails.push(`A: a host resize after mount clamped a scrolled child (scrollTop 787 → ${w.child.scrollTop})`)
|
||||
if (w.writes.length) fails.push(`A: a host resize after mount wrote the loader's min-height (${w.writes.join(', ')})`)
|
||||
if (w.sized()) fails.push('A: a content-sized host was flipped to sized by a window resize') }
|
||||
{ const w = make({ mode: 'sized', fixed: 523 }); w.f.probeOnce(); w.writes.length = 0; w.child.scrollTop = 300
|
||||
for (let i = 0; i < 4; i++) w.f.follow()
|
||||
if (w.writes.length || w.child.scrollTop !== 300) fails.push('A: a sized host\'s resize callbacks touched the loader') }
|
||||
// B
|
||||
{ const w = make({ mode: 'sized', fixed: 523 }); w.f.probeOnce()
|
||||
if (!w.sized() || w.loader.style.minHeight !== '0px') fails.push('B: a sized host (523) is not filled (the floor stayed on)') }
|
||||
// C
|
||||
{ const w = make({ mode: 'content' }); w.f.probeOnce(); let flips = 0, was = w.sized()
|
||||
w.mode = 'sized'; w.fixed = 600
|
||||
for (let i = 0; i < 10; i++) { t += 100; w.f.follow(); if (w.sized() !== was) { flips++; was = w.sized() } }
|
||||
if (!w.sized() || flips !== 1) fails.push(`C: a host sized after mount should flip to sized exactly once (flips ${flips}, sized ${w.sized()})`) }
|
||||
// D
|
||||
{ const w = make({ mode: 'capped', cap: 600, content: 0 }); w.f.probeOnce(); let flips = 0, was = w.sized()
|
||||
for (let i = 0; i < 20; i++) { t += 50; w.f.follow(); if (w.sized() !== was) { flips++; was = w.sized() } }
|
||||
const probes = w.probes() - 1
|
||||
if (flips !== 0 || w.sized() || probes > 0) fails.push(`D: a max-height host flipped ${flips} time(s) / was re-probed ${probes} time(s) (it must be found at mount and left alone)`) }
|
||||
// E
|
||||
{ const w = make({ mode: 'content', content: 0 }); w.f.probeOnce(); let flips = 0, was = w.sized(); const p0 = w.probes()
|
||||
w.mode = 'capped'; w.cap = 600
|
||||
for (let i = 0; i < 20; i++) { t += 50; w.f.follow(); if (w.sized() !== was) { flips++; was = w.sized() } }
|
||||
const probes = w.probes() - p0
|
||||
if (flips > 1 || w.sized() || probes > 1) fails.push(`E: a max-height cap that arrived after mount LOOPS or stays sized (flips ${flips}, re-probes ${probes}, sized ${w.sized()})`) }
|
||||
// H: sized → height:auto with in-flow content
|
||||
{ const w = make({ mode: 'sized', fixed: 523 }); w.f.probeOnce(); w.child.scrollTop = 300; let flips = 0, was = w.sized()
|
||||
w.mode = 'content'; w.content = 420
|
||||
for (let i = 0; i < 20; i++) { t += 50; w.f.follow(); if (w.sized() !== was) { flips++; was = w.sized() } }
|
||||
if (w.sized()) fails.push('H: a sized host restyled to height:auto with content (420) stayed sized (the floor did not come back)')
|
||||
if (flips > 1) fails.push(`H: the sized → auto host flipped ${flips} times`)
|
||||
if (w.child.scrollTop !== 300) fails.push(`H: the re-probe moved a scrolled child (scrollTop 300 → ${w.child.scrollTop})`) }
|
||||
// F
|
||||
{ const w = make({ mode: 'sized', fixed: 523 }); w.f.probeOnce(); w.mode = 'content'; w.content = 0; w.f.follow()
|
||||
// with the floor off and no content the host is 0 high; follow() must bring the floor back
|
||||
if (w.sized()) fails.push('F: a sized host that collapsed to 0 did not get the floor back') }
|
||||
// G
|
||||
{ const w = make({ mode: 'content', display: 'inline' }); w.f.probeOnce(); w.writes.length = 0; w.floor = 700; w.f.follow()
|
||||
if (w.writes.length) fails.push('G: an inline host was acted on after mount') }
|
||||
return fails
|
||||
}
|
||||
|
||||
const src = readFileSync(SRC, 'utf8')
|
||||
if (process.argv.includes('--self-test')) {
|
||||
const poisons = [
|
||||
['the old per-resize probe (follow() re-measures, no restore)', src.replace(/function follow\(\) \{/, 'function follow() { decide(); return;'), /^A: /],
|
||||
['no latch for a capped host', src.replace('if (capped) { if (h + 1 >= f) capped = false }\n else if', 'if'), /^[DE]: /],
|
||||
['no re-check of a sized host', src.replace('if (lastH === null || Math.abs(h - lastH) >= 1) reprobe()', '/* no re-check */'), /^H: .*stayed sized/],
|
||||
['no scroll restore in the re-probe', src.replace("for (const [e, top, left] of kept) { if (e.scrollTop !== top) e.scrollTop = top; if (e.scrollLeft !== left) e.scrollLeft = left }", ''), /^H: the re-probe moved/],
|
||||
]
|
||||
let missed = 0
|
||||
for (const [name, poisoned, rx] of poisons) {
|
||||
if (poisoned === src) { console.log(`NOT MEASURED: the poison anchor for "${name}" moved`); process.exitCode = 75; continue }
|
||||
const f = await audit(poisoned); const hit = f.find((x) => rx.test(x))
|
||||
console.log(hit ? `✓ caught: ${name} -> ${hit}` : `✗ MISSED: ${name}`)
|
||||
if (!hit) missed++
|
||||
}
|
||||
if (missed) process.exitCode = 1
|
||||
} else {
|
||||
const f = await audit(src)
|
||||
if (f.length) { for (const x of f) console.log('✗ ' + x); console.log(`FAIL check:host-floor (${f.length})`); process.exitCode = 1 }
|
||||
else console.log('PASS check:host-floor — probed at mount; after it, re-probed only on a change of kind and without moving a scrolled child; a window resize writes nothing; a sized host fills; late sizing flips once; a max-height cap never loops; a sized host restyled to auto (with or without content) gets the floor back; inline hosts are left alone')
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@
|
|||
* widget { corner } for a corner widget the Plugin Studio mounts once, fixed
|
||||
* visibility "team" | "public" default team — see VISIBILITY below
|
||||
* mobile "desktop-only" | "larger-screens" absent = phone-ready — see MOBILE below
|
||||
* posterDesign { family, name, size, mobileFriendly, labelKey?, instance? } or an array of them: the poster
|
||||
* design(s) this profile shows, listed by Poster Design's Styles. Carried as written, ONLY when present
|
||||
* (every other manifest keeps its bytes); check:poster-design-catalogue validates it.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
|
|
@ -69,8 +72,9 @@ export function readProfileMeta(dir, profileName) {
|
|||
? null
|
||||
: String(meta.visibility),
|
||||
mobile: MOBILE.includes(meta?.mobile) ? meta.mobile : null,
|
||||
posterDesign: meta?.posterDesign && typeof meta.posterDesign === 'object' ? meta.posterDesign : null,
|
||||
}
|
||||
} catch { return { stage: 'experimental', heavy: false, widget: null, label: '', targets: null, visibility: 'team', visibilityInvalid: null, mobile: null } }
|
||||
} catch { return { stage: 'experimental', heavy: false, widget: null, label: '', targets: null, visibility: 'team', visibilityInvalid: null, mobile: null, posterDesign: null } }
|
||||
}
|
||||
|
||||
/** The optional manifest keys for one profile (stage always; heavy / widget only when set). */
|
||||
|
|
@ -78,7 +82,7 @@ export function profileMetaFields(dir, profileName) {
|
|||
const m = readProfileMeta(dir, profileName)
|
||||
// visibility is emitted ONLY when it is "public": absent means team, so every manifest in the
|
||||
// tree today keeps the bytes it has and nothing becomes visible by upgrading the generator.
|
||||
return { stage: m.stage, ...(m.visibility === 'public' ? { visibility: 'public' } : {}), ...(m.label ? { label: m.label } : {}), ...(m.targets ? { targets: m.targets } : {}), ...(m.heavy ? { heavy: true } : {}), ...(m.widget ? { widget: m.widget } : {}), ...(m.mobile ? { mobile: m.mobile } : {}) }
|
||||
return { stage: m.stage, ...(m.visibility === 'public' ? { visibility: 'public' } : {}), ...(m.label ? { label: m.label } : {}), ...(m.targets ? { targets: m.targets } : {}), ...(m.heavy ? { heavy: true } : {}), ...(m.widget ? { widget: m.widget } : {}), ...(m.mobile ? { mobile: m.mobile } : {}), ...(m.posterDesign ? { posterDesign: m.posterDesign } : {}) }
|
||||
}
|
||||
|
||||
/** A bundle is production if ANY of its profiles says so. */
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const QaCorner = __DOXA_QA__
|
|||
import PinHint from './components/hints/PinHint.vue'
|
||||
import LargerScreenNotice from './components/LargerScreenNotice.vue'
|
||||
import { useScreenGate } from './composables/useScreenGate.js'
|
||||
import { createHostFloor } from './elements/hostFloor.js'
|
||||
import { useUIStore } from './stores/uiStore.js'
|
||||
import { useMapStore } from './stores/mapStore.js'
|
||||
import { useDataStore } from './stores/dataStore.js'
|
||||
|
|
@ -345,36 +346,20 @@ provide('uiStore', uiStore)
|
|||
provide('mapStore', mapStore)
|
||||
provide('dataStore', dataStore)
|
||||
|
||||
// ★ THE FLOOR NEVER EXCEEDS A SIZED HOST (measured 2026-10-02 on a phone, DX Life's Home/Pray/Research). The
|
||||
// viewport floor below (min-height: 100svh, see the style block) exists for a host whose height comes from its
|
||||
// CONTENT (the scroll-page leaf, BUG-UPG100). A host that is already SIZED (DX Life's slot: a definite height,
|
||||
// overflow hidden; a tab pane) must be filled exactly: there the floor made the map taller than its host, the
|
||||
// host clipped its bottom, and the legend footer was cut off. It also tied the map's height to the window's,
|
||||
// which is what moved the map as a phone's URL bar came and went.
|
||||
// Which kind of host this is cannot be read from CSS, so it is MEASURED: with the floor at 0 and then at a huge
|
||||
// value, a sized host keeps its height and a content-sized one grows. Sized → the floor is switched off
|
||||
// (height:100% fills the host); content-sized → the floor stays. Both probes are undone before the frame paints.
|
||||
// Re-measured whenever the host's size changes (a ResizeObserver on the HOST, never a window resize).
|
||||
// ★ THE FLOOR NEVER EXCEEDS A SIZED HOST, and it is decided WITHOUT disturbing live content: measured once at mount,
|
||||
// then only followed (library/elements/hostFloor.js, which says why, and check:host-floor, which pins it). The
|
||||
// viewport floor below (min-height: 100svh) is for a host whose height comes from its CONTENT; a host SIZED from
|
||||
// outside (DX Life's slot) is filled exactly, so its bottom (a map's legend) is never clipped.
|
||||
const loaderEl = ref(null)
|
||||
let hostRO = null
|
||||
function fitFloorToHost() {
|
||||
const el = loaderEl.value
|
||||
const host = el && el.getRootNode && el.getRootNode().host
|
||||
if (!el || !host) return
|
||||
const keep = el.style.minHeight
|
||||
el.style.minHeight = '0px'
|
||||
const h0 = host.getBoundingClientRect().height
|
||||
el.style.minHeight = '100000px'
|
||||
const h1 = host.getBoundingClientRect().height
|
||||
const sized = h0 > 0 && Math.abs(h1 - h0) < 1
|
||||
el.style.minHeight = sized ? '0px' : (keep === '0px' ? '' : keep)
|
||||
el.toggleAttribute('data-host-sized', sized)
|
||||
}
|
||||
onMounted(() => {
|
||||
fitFloorToHost()
|
||||
const host = loaderEl.value && loaderEl.value.getRootNode && loaderEl.value.getRootNode().host
|
||||
if (host && typeof ResizeObserver !== 'undefined') {
|
||||
hostRO = new ResizeObserver(() => fitFloorToHost())
|
||||
const el = loaderEl.value
|
||||
const host = el && el.getRootNode ? el.getRootNode().host : null
|
||||
if (!el || !host) return
|
||||
const floor = createHostFloor(el, host)
|
||||
floor.probeOnce()
|
||||
if (typeof ResizeObserver !== 'undefined') {
|
||||
hostRO = new ResizeObserver(() => floor.follow())
|
||||
hostRO.observe(host)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ import { getApiBaseUrl } from '../utils/apiBaseUrl.js';
|
|||
const { t, locale } = useI18n();
|
||||
|
||||
useShadowStyles(`
|
||||
.people-group-detail{display:flex;flex-direction:column;height:100%;overflow-y:auto;}
|
||||
.people-group-detail{display:flex;flex-direction:column;height:100%;overflow-y:auto;overscroll-behavior:contain;}
|
||||
.people-group-detail.dark-mode{background:#1a1a2e;color:#eee;}
|
||||
.detail-header{display:flex;justify-content:space-between;align-items:flex-start;padding:16px;border-bottom:1px solid #e0e0e0;background:white;position:sticky;top:0;z-index:10;}
|
||||
.dark-mode .detail-header{background:#16213e;border-bottom-color:#2d2d4e;}
|
||||
|
|
@ -133,7 +133,7 @@ useShadowStyles(`
|
|||
.dark-mode .close-button{color:#aaa;}
|
||||
.close-button:hover{color:#333;}
|
||||
.dark-mode .close-button:hover{color:#fff;}
|
||||
.detail-content{padding:6px 12px 12px;flex:1;overflow-y:auto;}
|
||||
.detail-content{padding:6px 12px 12px;flex:1;overflow-y:auto;overscroll-behavior:contain;}
|
||||
.dark-mode .detail-content{background:#1a1a2e;}
|
||||
.quick-stats{display:flex;flex-direction:column;gap:4px;margin-bottom:8px;}
|
||||
.stat-item{display:flex;justify-content:space-between;align-items:baseline;font-size:13px;}
|
||||
|
|
@ -467,6 +467,8 @@ function handleImageError(event) {
|
|||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
/* a scroll that reaches this panel's end never chains to the page (the mobile sheet's detail view) */
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* Header Section */
|
||||
|
|
@ -540,6 +542,7 @@ function handleImageError(event) {
|
|||
padding: 6px 12px 12px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* Quick Stats */
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ useShadowStyles(`
|
|||
mobile chrome (pull tab, collapse/expand caret, touch drag). ── */
|
||||
/* bottom = the sheet's height (it shows only while the sheet is open, whose height fits its content)
|
||||
+ a 16px gap. */
|
||||
.floating-mini-image{position:absolute;inset-inline-start:16px;bottom:calc(var(--legend-img-seat,var(--legend-sheet-h,30%)) + 16px);z-index:999;pointer-events:auto;cursor:pointer;}
|
||||
.floating-mini-image{position:absolute;inset-inline-start:16px;bottom:calc(var(--legend-img-seat,var(--legend-sheet-h,30%)) + 16px);z-index:999;pointer-events:auto;cursor:pointer;touch-action:none;}
|
||||
/* The image SITS on --legend-img-seat, a copy of the sheet's height that moves only while the image shows (the
|
||||
sheet open, no finger on it; script: IMG_SEAT_VAR). Dragged up, and through the snap to full open, the sheet
|
||||
(z 1000) slides over the still image (z 999), so it goes INTO the legend instead of riding its top edge; dragged
|
||||
|
|
@ -118,7 +118,7 @@ useShadowStyles(`
|
|||
max-height reads --legend-sheet-top-reserve, set on this element from the
|
||||
TOP_RESERVE_PX constant in the script — the SAME number the touch-drag clamp
|
||||
uses, so CSS and JS can never disagree about how tall the sheet may get. */
|
||||
.legend-mobile-sheet{position:absolute;bottom:0;inset-inline-start:0;inset-inline-end:0;z-index:1000;background:white;border-top-left-radius:var(--map-radius,16px);border-top-right-radius:var(--map-radius,16px);box-shadow:0 -4px 20px rgba(0,0,0,0.2);transition:height 0.3s ease-out;display:flex;flex-direction:column;overflow:hidden;max-height:calc(100% - var(--legend-sheet-top-reserve,80px));}
|
||||
.legend-mobile-sheet{overscroll-behavior:contain;position:absolute;bottom:0;inset-inline-start:0;inset-inline-end:0;z-index:1000;background:white;border-top-left-radius:var(--map-radius,16px);border-top-right-radius:var(--map-radius,16px);box-shadow:0 -4px 20px rgba(0,0,0,0.2);transition:height 0.3s ease-out;display:flex;flex-direction:column;overflow:hidden;max-height:calc(100% - var(--legend-sheet-top-reserve,80px));}
|
||||
/* While a finger is resizing the sheet the height must be applied INSTANTLY —
|
||||
with the 0.3s transition left on, the measured height lagged the finger by
|
||||
14-30px (mobile-sheet-probe baseline 2026-09-16) and the release logic read a
|
||||
|
|
@ -964,12 +964,20 @@ onBeforeUnmount(() => { if (_creditTimer) { clearTimeout(_creditTimer); _creditT
|
|||
beside the legend, so it shows in no resting state (collapsed, or open with nothing selected). A
|
||||
tapped dot's detail view keeps its note inside the sheet, where it explains shared dots; the tree's
|
||||
own copy stays off (:pin-note="false" below). Desktop is unchanged. -->
|
||||
<!-- Floating mini-image: 16px above the open sheet's SEAT (it shows only in that state; see .floating-mini-image). -->
|
||||
<!-- Floating mini-image: 16px above the open sheet's SEAT (it shows only in that state; see .floating-mini-image).
|
||||
★ IT IS A HANDLE TOO (measured 2026-10-02: a drag up from the picture scrolled the PAGE, scrollY 0 → 135; it sits
|
||||
outside the sheet and the lip, so it had none of their handlers). It takes the sheet's own capture handlers
|
||||
and touch-action:none: a drag from it resizes the sheet (the grab-zone test holds above the sheet, as for the
|
||||
lip), and a tap is handed back as a click, which reaches @click below as before. -->
|
||||
<Transition name="lm-img">
|
||||
<div
|
||||
v-if="legendMode === 'detail' && selectedPeopleGroup && legendState === 'open' && miniImageUrl"
|
||||
class="floating-mini-image"
|
||||
@click="uiStore.fullyOpenLegend()"
|
||||
@touchstart.capture="onSheetTouchStart"
|
||||
@touchmove.capture="onSheetTouchMove"
|
||||
@touchend.capture="onSheetTouchEnd"
|
||||
@touchcancel.capture="onSheetTouchEnd"
|
||||
>
|
||||
<img
|
||||
:src="miniImageUrl"
|
||||
|
|
@ -984,12 +992,15 @@ onBeforeUnmount(() => { if (_creditTimer) { clearTimeout(_creditTimer); _creditT
|
|||
v-if="legendState !== 'collapsed'"
|
||||
class="sheet-grab-lip"
|
||||
aria-hidden="true"
|
||||
@touchstart="onSheetTouchStart"
|
||||
@touchmove="onSheetTouchMove"
|
||||
@touchend="onSheetTouchEnd"
|
||||
@touchcancel="onSheetTouchEnd"
|
||||
@touchstart.capture="onSheetTouchStart"
|
||||
@touchmove.capture="onSheetTouchMove"
|
||||
@touchend.capture="onSheetTouchEnd"
|
||||
@touchcancel.capture="onSheetTouchEnd"
|
||||
></div>
|
||||
<!-- Touch resize handlers sit on the SHEET: a drag may start anywhere in the
|
||||
<!-- (.capture: the drag sees every touch FIRST, so no child's handler can stop one reaching it; and the sheet
|
||||
contains overscroll, so a list inside it can never chain a scroll to the page: 2026-10-02, a phone shrinking the
|
||||
detail sheet past its content scrolled the page.)
|
||||
Touch resize handlers sit on the SHEET: a drag may start anywhere in the
|
||||
top GRAB_ZONE_PX (strip + title row) — and, in detail mode, on the content
|
||||
when it can't scroll that way. See onSheetTouchStart for the model. -->
|
||||
<div
|
||||
|
|
@ -997,10 +1008,10 @@ onBeforeUnmount(() => { if (_creditTimer) { clearTimeout(_creditTimer); _creditT
|
|||
class="legend-mobile-sheet"
|
||||
:class="[legendState, { 'sheet-dark': isDark, dragging: isDragging, fit: fitOpen }]"
|
||||
:style="{ height: fitOpen ? 'auto' : legendHeight, '--legend-sheet-top-reserve': TOP_RESERVE_PX + 'px' }"
|
||||
@touchstart="onSheetTouchStart"
|
||||
@touchmove="onSheetTouchMove"
|
||||
@touchend="onSheetTouchEnd"
|
||||
@touchcancel="onSheetTouchEnd"
|
||||
@touchstart.capture="onSheetTouchStart"
|
||||
@touchmove.capture="onSheetTouchMove"
|
||||
@touchend.capture="onSheetTouchEnd"
|
||||
@touchcancel.capture="onSheetTouchEnd"
|
||||
>
|
||||
<!-- Thin drag strip at the very top: pull-tab visual + tap-to-toggle. -->
|
||||
<div class="sheet-drag-strip" @click="handleCaretClick">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
/**
|
||||
* PosterSectionView.vue — ONE poster section on its own: the profile behind <dxm-poster-section>.
|
||||
* It reads its settings from profile-config (type, scope, collapsible, collapsed, locale, assetsBase, columns), finds the
|
||||
* It reads its settings from profile-config (type, scope, collapsible, collapsed, locale, assetsBase, columns, minCardPx, showHeader, cardAction), finds the
|
||||
* poster TYPE in posterTypes.js, and draws a PosterWall in section-only mode: the same section a wall draws (the
|
||||
* same PosterSection, the same print actions and overlay), with no wall head around it.
|
||||
*/
|
||||
|
|
@ -38,11 +38,30 @@ const title = computed(() => (adapter.value ? adapter.value.title(cfg.value.scop
|
|||
const actions = computed(() => (adapter.value
|
||||
? adapter.value.actions({ assetsBase: () => assetsBase.value, apiBase: () => cfg.value.apiBaseUrl || getApiBaseUrl(), lang, profileConfig, t })
|
||||
: null))
|
||||
const collapsible = computed(() => cfg.value.collapsible !== false)
|
||||
const startCollapsed = computed(() => cfg.value.collapsed === true)
|
||||
// show-header="false" hides the section's head so a host's own layout is its header. The head is also the collapse
|
||||
// toggle, so a headless section can neither collapse nor start shut (it would have no way back open).
|
||||
const showHeader = computed(() => cfg.value.showHeader !== false)
|
||||
const collapsible = computed(() => cfg.value.collapsible !== false && showHeader.value)
|
||||
const startCollapsed = computed(() => cfg.value.collapsed === true && showHeader.value)
|
||||
const groupBy = () => title.value
|
||||
const collapsedBy = () => startCollapsed.value
|
||||
const instanceId = computed(() => `poster-section-${typeName.value}-${String(cfg.value.scope || 'all')}`)
|
||||
// card-action="dashboard": the card's sticker reads "Show dashboard" (the wall's headerAction seam, as Poster Design's
|
||||
// "Show full details") and a click dispatches dxm-poster-dashboard from this element: bubbling, composed (it leaves the
|
||||
// shadow root, so e.target is the <dxm-poster-section>) and cancelable. The HOST builds its own URL; the element never does.
|
||||
// name is the data's English name (the type's items in 'en'), never the card's localised label. The two strings live in
|
||||
// their own catalogue area (posterCard), referenced only here, so only the bundles that carry this element include it.
|
||||
const DASHBOARD_EVENT = 'dxm-poster-dashboard'
|
||||
const headerAction = computed(() => (cfg.value.cardAction === 'dashboard'
|
||||
? { labelKey: 'posterCard.showDashboard', ariaKey: 'posterCard.showDashboardAria' } : null))
|
||||
const wallRef = ref(null)
|
||||
function onHeaderAction(item) {
|
||||
if (cfg.value.cardAction !== 'dashboard' || !item) return
|
||||
const en = adapter.value ? adapter.value.items(cfg.value.scope, 'en').find((x) => x.id === item.id) : null
|
||||
const detail = { type: typeName.value, id: item.id, iso3: item.iso3, name: (en || item).label, scope: cfg.value.scope }
|
||||
const from = wallRef.value && wallRef.value.$el
|
||||
if (from) from.dispatchEvent(new CustomEvent(DASHBOARD_EVENT, { bubbles: true, composed: true, cancelable: true, detail }))
|
||||
}
|
||||
// Nothing to show still SAYS so (an unknown type or scope never renders as a blank).
|
||||
const noneLine = computed(() => t('poster.sectionNone', { scope: String(cfg.value.scope || '') }))
|
||||
</script>
|
||||
|
|
@ -52,15 +71,20 @@ const noneLine = computed(() => t('poster.sectionNone', { scope: String(cfg.valu
|
|||
<!-- embedded: an in-flow block the PAGE scrolls (a wall alone is a full-pane scroller), so sections stack on a
|
||||
page and sit inline under a host's own heading. -->
|
||||
<PosterWall v-else-if="adapter && actions"
|
||||
ref="wallRef"
|
||||
section-only
|
||||
embedded
|
||||
:items="items"
|
||||
:group-by="groupBy"
|
||||
:section-heads="showHeader"
|
||||
:header-action="headerAction"
|
||||
@header-action="onHeaderAction"
|
||||
:collapsible="collapsible"
|
||||
:collapsed-by="collapsedBy"
|
||||
:instance-id="instanceId"
|
||||
:is-dark="cfg.isDark === true"
|
||||
:columns="cfg.columns || 0"
|
||||
:min-card-px="cfg.minCardPx || 0"
|
||||
:assets-base="assetsBase"
|
||||
:event-name="actions.eventName"
|
||||
:unit="actions.unit"
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@
|
|||
* a country with no region is 'other'.
|
||||
*/
|
||||
import data from '@map/data/country-posters.json'
|
||||
import { makeCountrySheet, sheetWithheld, largeSheet } from '@map/packet/countrySheet.js'
|
||||
import { isPhonePrint } from '@map/composables/printInPlace.js'
|
||||
import { makeCountrySheet, largeSheet, posterWithheld, rowsWithheld } from '@map/packet/countrySheet.js'
|
||||
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
|
||||
import { keyedError } from '@map/i18n/keyedError.js'
|
||||
import { countryName } from '@map/utils/countryName.js'
|
||||
|
|
@ -19,14 +18,10 @@ export const regionKey = (s) => String(s || '').normalize('NFD').replace(/[\u030
|
|||
.replace(/['’.]/g, '').replace(/&/g, ' and ').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'other'
|
||||
const TOO_MANY = 'Too many people groups to fit on one poster legibly — a solution is in progress.'
|
||||
const inScope = (scope) => (c) => regionKey(c.region) === regionKey(scope)
|
||||
// A phone cannot draw a desktop-only large sheet (same rule as the Country Posters wall, country-poster-wall.vue).
|
||||
// Evaluated at call time so the type is correct in whatever runtime context a host mounts it.
|
||||
const phoneBlocked = (c) => isPhonePrint() && Boolean(largeSheet(c)?.desktopOnly)
|
||||
|
||||
export default {
|
||||
scopes: () => [...new Set(data.countries.map((c) => regionKey(c.region)))],
|
||||
items(scope, locale) {
|
||||
// Order is applied once in PosterWall (count desc + stable name tie-break), so this returns unordered rows.
|
||||
const rows = data.countries.filter(inScope(scope))
|
||||
if (/^en\b/i.test(locale || 'en')) return rows
|
||||
return rows.map((c) => ({ ...c, label: countryName(c.iso3, locale) || c.label }))
|
||||
|
|
@ -43,13 +38,11 @@ export default {
|
|||
const sheet = makeCountrySheet({ records: recordsFor, assetsBase, countries: data.countries })
|
||||
const byId = new Map(data.countries.map((c) => [c.id, c]))
|
||||
return {
|
||||
// a withheld whole-country poster withholds its list, packet and cards too (countrySheet.js WITHHELD)
|
||||
records: (c) => (sheetWithheld(c) ? Promise.reject(keyedError('messages.countryPosterTooMany', TOO_MANY))
|
||||
: phoneBlocked(c) ? Promise.reject(keyedError('poster.phoneLimitAll', 'This needs a desktop or laptop computer.'))
|
||||
: recordsFor({ type: 'country', values: [c.iso3] })),
|
||||
// a whole country's withheld rows (countrySheet.js WITHHELD: India's list, packet and cards) say so, unfetched
|
||||
records: (c) => (rowsWithheld(c) ? Promise.reject(keyedError('messages.countryPosterTooMany', TOO_MANY)) : recordsFor({ type: 'country', values: [c.iso3] })),
|
||||
// the sheet gets the data's own record (English, the series index by id), as on the wall
|
||||
sheet: (card, format) => sheet(byId.get(card.id) || card, format),
|
||||
unsupported: (c) => (sheetWithheld(c) ? t('messages.countryPosterTooMany') : phoneBlocked(c) ? t('poster.phoneLimitAll') : c.unsupportedReason ? t('messages.countryPosterTooMany') : null),
|
||||
unsupported: (c) => (posterWithheld(c) || (c.unsupportedReason && !largeSheet(c)) ? t('messages.countryPosterTooMany') : null),
|
||||
unit: 'UPG', itemCountKey: 'regions.countryCount', eventName: 'doxa-country-posters:show-on-map', downloadPrefix: 'doxa-country', cardStocks: true,
|
||||
}
|
||||
},
|
||||
|
|
|
|||
82
library/elements/hostFloor.js
Normal file
82
library/elements/hostFloor.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* hostFloor.js — whether a profile's viewport floor applies, decided against its HOST (library/ProfileLoader.vue).
|
||||
*
|
||||
* The loader floors every profile at min-height:100svh, for a host whose height comes from its CONTENT (the
|
||||
* scroll-page leaf; BUG-UPG100). A host that is SIZED from outside (DX Life's map slot: a definite height, overflow
|
||||
* hidden) must be filled exactly: there the floor made the map taller than its host and clipped its legend.
|
||||
*
|
||||
* ★ PROBED AT MOUNT; AFTER THAT, ONLY WHEN THE HOST CHANGES KIND, AND WITHOUT A TRACE. Which kind of host this is
|
||||
* cannot be read from CSS, so it is MEASURED: the floor at 0, then at a huge value; a sized host keeps its height,
|
||||
* a content-sized one grows (and one that stops growing below the floor is CAPPED, by max-height). At mount the
|
||||
* profile has not rendered, so that probe touches no reader state.
|
||||
* The regression this replaces: re-probing on EVERY host resize collapsed the loader under live content, clamped a
|
||||
* scrolling child's scrollTop, and the embedded UPG-100 app (its view follows its column's scroll) flipped from Map
|
||||
* back to its first view whenever a phone's URL bar resized the host. So after mount follow() re-probes ONLY when
|
||||
* the reading says the kind may have changed, and that re-probe saves every scrolled descendant and puts it back
|
||||
* before the frame paints:
|
||||
* - floor on, host SHORTER than the floor: sized from outside (styled after mount), or capped. Re-probe decides;
|
||||
* a capped host is latched until it reaches the floor again, so it is never re-probed in a loop;
|
||||
* - floor off (sized), host CHANGED size: it may have stopped holding a size (restyled to height:auto, now
|
||||
* following its content). Re-probe decides, and the floor comes back if so;
|
||||
* - a window-height change on a content-sized host moves the host WITH the floor: no re-probe, nothing written.
|
||||
* check:host-floor pins all of this, with the old per-resize probe, a missing latch, a missing sized re-check and a
|
||||
* missing scroll restore as poisons.
|
||||
*/
|
||||
const scrolledUnder = (root) => {
|
||||
const out = []
|
||||
const walk = (r) => {
|
||||
for (const e of r.querySelectorAll ? r.querySelectorAll('*') : []) {
|
||||
if (e.scrollTop || e.scrollLeft) out.push(e)
|
||||
if (e.shadowRoot) walk(e.shadowRoot)
|
||||
}
|
||||
}
|
||||
walk(root)
|
||||
return out
|
||||
}
|
||||
|
||||
export function createHostFloor(loader, host, { style = (n) => getComputedStyle(n), scrolled = () => scrolledUnder(loader) } = {}) {
|
||||
let capped = false, lastH = null
|
||||
const isSized = () => loader.hasAttribute('data-host-sized')
|
||||
const floorPx = () => parseFloat(style(loader).minHeight) || 0
|
||||
const hostH = () => host.getBoundingClientRect().height
|
||||
function set(sized) {
|
||||
loader.style.minHeight = sized ? '0px' : ''
|
||||
if (sized) loader.setAttribute('data-host-sized', '')
|
||||
else loader.removeAttribute('data-host-sized')
|
||||
}
|
||||
function decide() {
|
||||
loader.style.minHeight = '0px'
|
||||
const h0 = hostH()
|
||||
loader.style.minHeight = '100000px'
|
||||
const h1 = hostH()
|
||||
const sized = h0 > 0 && Math.abs(h1 - h0) < 1
|
||||
set(sized)
|
||||
const f = sized ? 0 : floorPx()
|
||||
capped = !sized && f > 0 && h1 + 1 < f // grows with its content, but stops below the floor
|
||||
}
|
||||
/** At mount only, before the profile renders. */
|
||||
function probeOnce() { decide(); lastH = hostH() }
|
||||
/** After mount: the same decision, leaving every scrolled descendant where it was. */
|
||||
function reprobe() {
|
||||
const kept = scrolled().map((e) => [e, e.scrollTop, e.scrollLeft])
|
||||
decide()
|
||||
for (const [e, top, left] of kept) { if (e.scrollTop !== top) e.scrollTop = top; if (e.scrollLeft !== left) e.scrollLeft = left }
|
||||
}
|
||||
/** On every host resize: reads, and re-probes only when the host may have changed kind. */
|
||||
function follow() {
|
||||
const display = style(host).display
|
||||
if (display === 'inline' || display === 'contents') return // no box of its own to read
|
||||
const h = hostH()
|
||||
if (isSized()) {
|
||||
if (lastH === null || Math.abs(h - lastH) >= 1) reprobe()
|
||||
} else {
|
||||
const f = floorPx()
|
||||
if (capped) { if (h + 1 >= f) capped = false }
|
||||
else if (f > 0 && h + 1 < f) reprobe()
|
||||
}
|
||||
lastH = hostH()
|
||||
}
|
||||
return { probeOnce, follow }
|
||||
}
|
||||
|
||||
export default createHostFloor
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// <dxm-poster-section>'s attributes → its profile-config. Pure (no Vue), so it is testable on its own.
|
||||
export const ATTRS = ['type', 'scope', 'collapsible', 'collapsed', 'locale', 'assets-base', 'api-base-url', 'is-dark', 'columns']
|
||||
export const ATTRS = ['type', 'scope', 'collapsible', 'collapsed', 'locale', 'assets-base', 'api-base-url', 'is-dark', 'columns', 'min-card-px', 'show-header', 'card-action']
|
||||
export const TAG = 'dxm-poster-section'
|
||||
|
||||
export function posterSectionConfig(el) {
|
||||
|
|
@ -12,6 +12,16 @@ export function posterSectionConfig(el) {
|
|||
// posters per row (1-5); absent = the wall's own default. A reader's saved choice still wins (PosterWall).
|
||||
const cols = Number(a('columns'))
|
||||
if (el.hasAttribute('columns') && Number.isInteger(cols) && cols >= 1 && cols <= 5) cfg.columns = cols
|
||||
// the narrowest a card may get before a row holds one fewer (PosterWall's minCardPx); absent = the wall's own default.
|
||||
// A host that wants two posters a row on a phone sets it low enough to fit (the Regions list: 150).
|
||||
const minCard = Number(a('min-card-px'))
|
||||
if (el.hasAttribute('min-card-px') && Number.isInteger(minCard) && minCard >= 80 && minCard <= 600) cfg.minCardPx = minCard
|
||||
// show-header="false": no section head, so the HOST's own layout is the header (a key only then: every other host's
|
||||
// config is unchanged). The head is also the collapse toggle, so a headless section never collapses (PosterSectionView).
|
||||
if (flag('show-header', true) === false) cfg.showHeader = false
|
||||
// card-action="dashboard": each card's sticker reads "Show dashboard" and asks the HOST to open its own page
|
||||
// (PosterSectionView dispatches dxm-poster-dashboard). Only a known value sets a key; absent = "Show on map".
|
||||
if (a('card-action') === 'dashboard') cfg.cardAction = 'dashboard'
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@
|
|||
*
|
||||
* Attributes: type (default "country"), scope (a region / continent / list key), collapsible (default on;
|
||||
* collapsible="false" turns it off), collapsed (start shut), locale, assets-base, api-base-url, is-dark, columns
|
||||
* (posters per row, 1-5).
|
||||
* (posters per row, 1-5), min-card-px (the narrowest a card gets before a row holds one fewer), show-header (default
|
||||
* on; show-header="false" hides the section's head so the host's layout is its header), card-action ("dashboard": each
|
||||
* card reads "Show dashboard" and dispatches a bubbling, composed dxm-poster-dashboard { type, id, iso3, name, scope } for
|
||||
* the host to open its own page; absent = "Show on map").
|
||||
* They are mapped into the standard element's profile-config, so the element keeps defineMapElement's per-instance
|
||||
* i18n and store, and the profile (PosterSectionView.vue) reads them like every other profile reads its config.
|
||||
* Any bundle can call registerPosterSectionElement(); a second call is a no-op.
|
||||
|
|
|
|||
4
library/i18n/locales/ar/posterCard.json
Normal file
4
library/i18n/locales/ar/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "عرض لوحة المعلومات",
|
||||
"showDashboardAria": "عرض لوحة معلومات {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "حزمة الصلاة",
|
||||
"cards": "بطاقات الصلاة",
|
||||
"wall": "ألواح الجدار",
|
||||
"mapPrint": "طباعة الخريطة"
|
||||
"mapPrint": "طباعة الخريطة",
|
||||
"plugins": "من إضافات أخرى"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "صفحة خريطة الدولة",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "الأوراق وشرائط اللصق ومواضع صفوف البطاقات مأخوذة من هندسة الألواح المشتركة؛ وتُعرض البطاقات كتخطيط، لأن جدار البركة يرسم بطاقاته في إضافته الخاصة.",
|
||||
"mapPrint": "يطبع هذا القالب الخريطة الحية التي ينظر إليها القارئ، لذا ليست له صورة نموذجية."
|
||||
"mapPrint": "يطبع هذا القالب الخريطة الحية التي ينظر إليها القارئ، لذا ليست له صورة نموذجية.",
|
||||
"noCountryAtSize": "لا تُطبع أي دولة بهذا الحجم اليوم: الهند، الدولة الوحيدة التي كانت تُطبع به، تُطبع الآن بحجم 5 × 8 أقدام فقط. نعرضه هنا بدولة نموذجية لإظهار القالب."
|
||||
},
|
||||
"details": {
|
||||
"configName": "اسم الإعداد",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "بيانات نموذجية",
|
||||
"renderer": "المولّد",
|
||||
"legacy": "مقاس قديم: ما زال يُطبع، لكن أداة اختيار الطباعة لم تعد تعرضه.",
|
||||
"forAgents": "لاستخدام هذا القالب، قل للوكيل: استخدم الإعداد {name}."
|
||||
"forAgents": "لاستخدام هذا القالب، قل للوكيل: استخدم الإعداد {name}.",
|
||||
"openPage": "افتح صفحته"
|
||||
},
|
||||
"templateCount": "لا قوالب | قالب واحد | قالبان | {n} قوالب | {n} قالبًا | {n} قالب"
|
||||
"templateCount": "لا قوالب | قالب واحد | قالبان | {n} قوالب | {n} قالبًا | {n} قالب",
|
||||
"fromPlugin": "تصنعه إضافته الخاصة. اعرض كل التفاصيل لتراه مباشرة."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/ar/posterSections.json
Normal file
14
library/i18n/locales/ar/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "أقسام الملصقات",
|
||||
"intro": "اختر الملصقات وشاهدها أدناه، ثم انسخ الرمز لوضعها على أي موقع.",
|
||||
"type": "نوع الملصق",
|
||||
"typeCountry": "الدول",
|
||||
"region": "المنطقة",
|
||||
"allRegions": "كل المناطق",
|
||||
"language": "اللغة",
|
||||
"startCollapsed": "تبدأ مطويّة",
|
||||
"embed": "انسخ رمز التضمين",
|
||||
"embedNote": "الصق هذا في أي صفحة ويب.",
|
||||
"copy": "نسخ",
|
||||
"copied": "تم النسخ"
|
||||
}
|
||||
4
library/i18n/locales/de/posterCard.json
Normal file
4
library/i18n/locales/de/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Dashboard anzeigen",
|
||||
"showDashboardAria": "Dashboard anzeigen: {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Gebetsmappe",
|
||||
"cards": "Gebetskarten",
|
||||
"wall": "Wandtafeln",
|
||||
"mapPrint": "Kartendruck"
|
||||
"mapPrint": "Kartendruck",
|
||||
"plugins": "Aus anderen Plugins"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Länder-Kartenblatt",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "Die Bögen, die Klebestreifen und die Lage der Kartenreihen stammen aus der gemeinsamen Tafelgeometrie; die Karten sind als Layout dargestellt, weil die Segnen-Wand ihre Karten in ihrem eigenen Plugin zeichnet.",
|
||||
"mapPrint": "Diese Vorlage druckt die Live-Karte, die der Leser gerade ansieht, und hat deshalb kein Beispielbild."
|
||||
"mapPrint": "Diese Vorlage druckt die Live-Karte, die der Leser gerade ansieht, und hat deshalb kein Beispielbild.",
|
||||
"noCountryAtSize": "Heute wird kein Land in dieser Größe gedruckt: Indien, das einzige Land, das es wurde, wird jetzt nur noch in 5 × 8 ft gedruckt. Die Vorlage wird hier mit einem Beispielland gezeigt."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Name der Konfiguration",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Beispieldaten",
|
||||
"renderer": "Generator",
|
||||
"legacy": "Alte Größe: Sie wird noch gedruckt, aber die Druckauswahl bietet sie nicht mehr an.",
|
||||
"forAgents": "Um diese Vorlage zu verwenden, sagen Sie einem Agenten: verwende die Konfiguration {name}."
|
||||
"forAgents": "Um diese Vorlage zu verwenden, sagen Sie einem Agenten: verwende die Konfiguration {name}.",
|
||||
"openPage": "Seite öffnen"
|
||||
},
|
||||
"templateCount": "{n} Vorlage | {n} Vorlagen"
|
||||
"templateCount": "{n} Vorlage | {n} Vorlagen",
|
||||
"fromPlugin": "Wird von seinem eigenen Plugin erstellt. „Alle Details anzeigen“ zeigt es live."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/de/posterSections.json
Normal file
14
library/i18n/locales/de/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Plakat-Abschnitte",
|
||||
"intro": "Wähle die Plakate, sieh sie dir unten an und kopiere dann den Code für eine beliebige Website.",
|
||||
"type": "Plakattyp",
|
||||
"typeCountry": "Länder",
|
||||
"region": "Region",
|
||||
"allRegions": "Alle Regionen",
|
||||
"language": "Sprache",
|
||||
"startCollapsed": "Eingeklappt beginnen",
|
||||
"embed": "Einbettung kopieren",
|
||||
"embedNote": "Füge dies in eine beliebige Webseite ein.",
|
||||
"copy": "Kopieren",
|
||||
"copied": "Kopiert"
|
||||
}
|
||||
4
library/i18n/locales/en/posterCard.json
Normal file
4
library/i18n/locales/en/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Show dashboard",
|
||||
"showDashboardAria": "Show the {label} dashboard"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Prayer packet",
|
||||
"cards": "Prayer cards",
|
||||
"wall": "Wall panels",
|
||||
"mapPrint": "Map print"
|
||||
"mapPrint": "Map print",
|
||||
"plugins": "From other plugins"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Country map sheet",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "The sheets, the glue strips and where the card rows go come from the shared panel geometry; the cards are shown as a layout, because the Bless wall draws its cards in its own plugin.",
|
||||
"mapPrint": "This template prints the live map the reader is looking at, so it has no sample picture."
|
||||
"mapPrint": "This template prints the live map the reader is looking at, so it has no sample picture.",
|
||||
"noCountryAtSize": "No country prints at this size today: India, the one country that did, now prints at 5 × 8 ft only. It is drawn here with a sample country to show the template."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Configuration name",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Sample data",
|
||||
"renderer": "Renderer",
|
||||
"legacy": "Legacy size: it still prints, but the print picker no longer offers it.",
|
||||
"forAgents": "To use this template, tell an agent: use configuration {name}."
|
||||
"forAgents": "To use this template, tell an agent: use configuration {name}.",
|
||||
"openPage": "Open its page"
|
||||
},
|
||||
"templateCount": "{n} templates | {n} template | {n} templates"
|
||||
"templateCount": "{n} templates | {n} template | {n} templates",
|
||||
"fromPlugin": "Made by its own plugin. Show full details shows it live."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/en/posterSections.json
Normal file
14
library/i18n/locales/en/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Poster sections",
|
||||
"intro": "Pick the posters, see them below, then copy the tag to put them on any website.",
|
||||
"type": "Poster type",
|
||||
"typeCountry": "Countries",
|
||||
"region": "Region",
|
||||
"allRegions": "All regions",
|
||||
"language": "Language",
|
||||
"startCollapsed": "Start collapsed",
|
||||
"embed": "Copy embed",
|
||||
"embedNote": "Paste this into any web page.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied"
|
||||
}
|
||||
4
library/i18n/locales/es/posterCard.json
Normal file
4
library/i18n/locales/es/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Ver el panel",
|
||||
"showDashboardAria": "Ver el panel de {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Paquete de oración",
|
||||
"cards": "Tarjetas de oración",
|
||||
"wall": "Paneles de pared",
|
||||
"mapPrint": "Impresión del mapa"
|
||||
"mapPrint": "Impresión del mapa",
|
||||
"plugins": "De otros complementos"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Hoja de mapa del país",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "Las hojas, las franjas de pegado y el lugar de las filas de tarjetas vienen de la geometría de paneles compartida; las tarjetas se muestran como un diseño, porque el muro Bendecir dibuja sus tarjetas en su propio complemento.",
|
||||
"mapPrint": "Esta plantilla imprime el mapa en vivo que está mirando el lector, así que no tiene imagen de ejemplo."
|
||||
"mapPrint": "Esta plantilla imprime el mapa en vivo que está mirando el lector, así que no tiene imagen de ejemplo.",
|
||||
"noCountryAtSize": "Hoy ningún país se imprime en este tamaño: la India, el único que lo hacía, ahora se imprime solo a 5 × 8 ft. Aquí se dibuja con un país de muestra para mostrar la plantilla."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Nombre de la configuración",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Datos de ejemplo",
|
||||
"renderer": "Generador",
|
||||
"legacy": "Tamaño heredado: todavía se imprime, pero el selector de impresión ya no lo ofrece.",
|
||||
"forAgents": "Para usar esta plantilla, dile a un agente: usa la configuración {name}."
|
||||
"forAgents": "Para usar esta plantilla, dile a un agente: usa la configuración {name}.",
|
||||
"openPage": "Abrir su página"
|
||||
},
|
||||
"templateCount": "{n} plantilla | {n} de plantillas | {n} plantillas"
|
||||
"templateCount": "{n} plantilla | {n} de plantillas | {n} plantillas",
|
||||
"fromPlugin": "Lo crea su propio complemento. Mostrar todos los detalles lo muestra en vivo."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/es/posterSections.json
Normal file
14
library/i18n/locales/es/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Secciones de carteles",
|
||||
"intro": "Elige los carteles, míralos abajo y copia la etiqueta para ponerlos en cualquier sitio web.",
|
||||
"type": "Tipo de cartel",
|
||||
"typeCountry": "Países",
|
||||
"region": "Región",
|
||||
"allRegions": "Todas las regiones",
|
||||
"language": "Idioma",
|
||||
"startCollapsed": "Empezar contraído",
|
||||
"embed": "Copiar código para insertar",
|
||||
"embedNote": "Pega esto en cualquier página web.",
|
||||
"copy": "Copiar",
|
||||
"copied": "Copiado"
|
||||
}
|
||||
4
library/i18n/locales/fr/posterCard.json
Normal file
4
library/i18n/locales/fr/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Voir le tableau de bord",
|
||||
"showDashboardAria": "Voir le tableau de bord : {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Dossier de prière",
|
||||
"cards": "Cartes de prière",
|
||||
"wall": "Panneaux muraux",
|
||||
"mapPrint": "Impression de la carte"
|
||||
"mapPrint": "Impression de la carte",
|
||||
"plugins": "D’autres plugins"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Feuille de carte du pays",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "Les feuilles, les bandes de collage et l'emplacement des rangées de cartes viennent de la géométrie de panneaux partagée ; les cartes sont montrées comme une mise en page, car le mur Bénir dessine ses cartes dans son propre module.",
|
||||
"mapPrint": "Ce modèle imprime la carte en direct que le lecteur regarde, il n'a donc pas d'image d'exemple."
|
||||
"mapPrint": "Ce modèle imprime la carte en direct que le lecteur regarde, il n'a donc pas d'image d'exemple.",
|
||||
"noCountryAtSize": "Aucun pays ne s’imprime à ce format aujourd’hui : l’Inde, le seul qui le faisait, s’imprime désormais uniquement en 5 × 8 ft. Le modèle est dessiné ici avec un pays d’exemple."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Nom de la configuration",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Données d'exemple",
|
||||
"renderer": "Générateur",
|
||||
"legacy": "Format ancien : il s'imprime encore, mais le sélecteur d'impression ne le propose plus.",
|
||||
"forAgents": "Pour utiliser ce modèle, dites à un agent : utilise la configuration {name}."
|
||||
"forAgents": "Pour utiliser ce modèle, dites à un agent : utilise la configuration {name}.",
|
||||
"openPage": "Ouvrir sa page"
|
||||
},
|
||||
"templateCount": "{n} modèle | {n} de modèles | {n} modèles"
|
||||
"templateCount": "{n} modèle | {n} de modèles | {n} modèles",
|
||||
"fromPlugin": "Créé par son propre plugin. Afficher tous les détails le montre en direct."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/fr/posterSections.json
Normal file
14
library/i18n/locales/fr/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Sections d’affiches",
|
||||
"intro": "Choisissez les affiches, regardez-les ci-dessous, puis copiez la balise pour les placer sur n’importe quel site.",
|
||||
"type": "Type d’affiche",
|
||||
"typeCountry": "Pays",
|
||||
"region": "Région",
|
||||
"allRegions": "Toutes les régions",
|
||||
"language": "Langue",
|
||||
"startCollapsed": "Commencer replié",
|
||||
"embed": "Copier le code d’intégration",
|
||||
"embedNote": "Collez ceci dans n’importe quelle page web.",
|
||||
"copy": "Copier",
|
||||
"copied": "Copié"
|
||||
}
|
||||
4
library/i18n/locales/hi/posterCard.json
Normal file
4
library/i18n/locales/hi/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "डैशबोर्ड देखें",
|
||||
"showDashboardAria": "{label} डैशबोर्ड देखें"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "प्रार्थना पैकेट",
|
||||
"cards": "प्रार्थना कार्ड",
|
||||
"wall": "दीवार पैनल",
|
||||
"mapPrint": "मानचित्र प्रिंट"
|
||||
"mapPrint": "मानचित्र प्रिंट",
|
||||
"plugins": "अन्य प्लगइन से"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "देश का मानचित्र पृष्ठ",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "शीटें, चिपकाने की पट्टियाँ और कार्ड-पंक्तियों की जगह साझा पैनल ज्यामिति से आती हैं; कार्ड लेआउट के रूप में दिखाए गए हैं, क्योंकि आशीष दीवार अपने कार्ड अपने प्लगइन में बनाती है।",
|
||||
"mapPrint": "यह टेम्पलेट वह लाइव मानचित्र प्रिंट करता है जिसे पाठक देख रहा है, इसलिए इसकी कोई नमूना तस्वीर नहीं है।"
|
||||
"mapPrint": "यह टेम्पलेट वह लाइव मानचित्र प्रिंट करता है जिसे पाठक देख रहा है, इसलिए इसकी कोई नमूना तस्वीर नहीं है।",
|
||||
"noCountryAtSize": "आज कोई भी देश इस आकार में नहीं छपता: भारत, जो अकेला देश छपता था, अब केवल 5 × 8 फ़ुट में छपता है। टेम्पलेट दिखाने के लिए इसे यहाँ एक नमूना देश के साथ बनाया गया है।"
|
||||
},
|
||||
"details": {
|
||||
"configName": "कॉन्फ़िगरेशन का नाम",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "नमूना डेटा",
|
||||
"renderer": "जनरेटर",
|
||||
"legacy": "पुराना आकार: यह अब भी प्रिंट होता है, पर प्रिंट चयनकर्ता अब इसे नहीं दिखाता।",
|
||||
"forAgents": "यह टेम्पलेट इस्तेमाल करने के लिए किसी एजेंट से कहें: कॉन्फ़िगरेशन {name} इस्तेमाल करो।"
|
||||
"forAgents": "यह टेम्पलेट इस्तेमाल करने के लिए किसी एजेंट से कहें: कॉन्फ़िगरेशन {name} इस्तेमाल करो।",
|
||||
"openPage": "इसका पेज खोलें"
|
||||
},
|
||||
"templateCount": "{n} टेम्पलेट | {n} टेम्पलेट"
|
||||
"templateCount": "{n} टेम्पलेट | {n} टेम्पलेट",
|
||||
"fromPlugin": "यह अपने ही प्लगइन से बनता है। पूरा विवरण दिखाएँ इसे लाइव दिखाता है।"
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/hi/posterSections.json
Normal file
14
library/i18n/locales/hi/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "पोस्टर अनुभाग",
|
||||
"intro": "पोस्टर चुनें, नीचे देखें, फिर किसी भी वेबसाइट पर लगाने के लिए टैग कॉपी करें।",
|
||||
"type": "पोस्टर का प्रकार",
|
||||
"typeCountry": "देश",
|
||||
"region": "क्षेत्र",
|
||||
"allRegions": "सभी क्षेत्र",
|
||||
"language": "भाषा",
|
||||
"startCollapsed": "बंद रूप में शुरू करें",
|
||||
"embed": "एम्बेड कॉपी करें",
|
||||
"embedNote": "इसे किसी भी वेब पेज में चिपकाएँ।",
|
||||
"copy": "कॉपी करें",
|
||||
"copied": "कॉपी हो गया"
|
||||
}
|
||||
4
library/i18n/locales/it/posterCard.json
Normal file
4
library/i18n/locales/it/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Vedi il cruscotto",
|
||||
"showDashboardAria": "Vedi il cruscotto di {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Fascicolo di preghiera",
|
||||
"cards": "Schede di preghiera",
|
||||
"wall": "Pannelli da parete",
|
||||
"mapPrint": "Stampa della mappa"
|
||||
"mapPrint": "Stampa della mappa",
|
||||
"plugins": "Da altri plugin"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Foglio mappa del paese",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "I fogli, le strisce da incollare e la posizione delle file di schede vengono dalla geometria dei pannelli condivisa; le schede sono mostrate come impaginazione, perché il muro Benedici disegna le sue schede nel proprio plugin.",
|
||||
"mapPrint": "Questo modello stampa la mappa dal vivo che il lettore sta guardando, quindi non ha un'immagine di esempio."
|
||||
"mapPrint": "Questo modello stampa la mappa dal vivo che il lettore sta guardando, quindi non ha un'immagine di esempio.",
|
||||
"noCountryAtSize": "Oggi nessun paese si stampa in questo formato: l’India, l’unico che lo faceva, ora si stampa solo a 5 × 8 ft. Qui il modello è disegnato con un paese di esempio."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Nome della configurazione",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Dati di esempio",
|
||||
"renderer": "Generatore",
|
||||
"legacy": "Formato precedente: si stampa ancora, ma il selettore di stampa non lo offre più.",
|
||||
"forAgents": "Per usare questo modello, di' a un agente: usa la configurazione {name}."
|
||||
"forAgents": "Per usare questo modello, di' a un agente: usa la configurazione {name}.",
|
||||
"openPage": "Apri la sua pagina"
|
||||
},
|
||||
"templateCount": "{n} modello | {n} di modelli | {n} modelli"
|
||||
"templateCount": "{n} modello | {n} di modelli | {n} modelli",
|
||||
"fromPlugin": "Creato dal suo plugin. Mostra tutti i dettagli lo mostra dal vivo."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/it/posterSections.json
Normal file
14
library/i18n/locales/it/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Sezioni di poster",
|
||||
"intro": "Scegli i poster, guardali qui sotto, poi copia il tag per metterli su qualsiasi sito.",
|
||||
"type": "Tipo di poster",
|
||||
"typeCountry": "Paesi",
|
||||
"region": "Regione",
|
||||
"allRegions": "Tutte le regioni",
|
||||
"language": "Lingua",
|
||||
"startCollapsed": "Inizia chiuso",
|
||||
"embed": "Copia il codice da incorporare",
|
||||
"embedNote": "Incolla questo in qualsiasi pagina web.",
|
||||
"copy": "Copia",
|
||||
"copied": "Copiato"
|
||||
}
|
||||
4
library/i18n/locales/pt/posterCard.json
Normal file
4
library/i18n/locales/pt/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Ver o painel",
|
||||
"showDashboardAria": "Ver o painel: {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Pacote de oração",
|
||||
"cards": "Cartões de oração",
|
||||
"wall": "Painéis de parede",
|
||||
"mapPrint": "Impressão do mapa"
|
||||
"mapPrint": "Impressão do mapa",
|
||||
"plugins": "De outros plugins"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Folha de mapa do país",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "As folhas, as faixas de colagem e a posição das fileiras de cartões vêm da geometria de painéis compartilhada; os cartões aparecem como um layout, porque o mural Abençoar desenha os seus cartões no seu próprio plugin.",
|
||||
"mapPrint": "Este modelo imprime o mapa ao vivo que o leitor está vendo, por isso não tem imagem de exemplo."
|
||||
"mapPrint": "Este modelo imprime o mapa ao vivo que o leitor está vendo, por isso não tem imagem de exemplo.",
|
||||
"noCountryAtSize": "Hoje nenhum país é impresso neste tamanho: a Índia, o único que era, agora é impressa apenas em 5 × 8 ft. Aqui o modelo é desenhado com um país de exemplo."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Nome da configuração",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Dados de exemplo",
|
||||
"renderer": "Gerador",
|
||||
"legacy": "Tamanho antigo: ainda imprime, mas o seletor de impressão já não o oferece.",
|
||||
"forAgents": "Para usar este modelo, diga a um agente: use a configuração {name}."
|
||||
"forAgents": "Para usar este modelo, diga a um agente: use a configuração {name}.",
|
||||
"openPage": "Abrir a página"
|
||||
},
|
||||
"templateCount": "{n} modelo | {n} de modelos | {n} modelos"
|
||||
"templateCount": "{n} modelo | {n} de modelos | {n} modelos",
|
||||
"fromPlugin": "Feito pelo próprio plugin. Mostrar todos os detalhes o exibe ao vivo."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/pt/posterSections.json
Normal file
14
library/i18n/locales/pt/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Seções de cartazes",
|
||||
"intro": "Escolha os cartazes, veja-os abaixo e copie a etiqueta para colocá-los em qualquer site.",
|
||||
"type": "Tipo de cartaz",
|
||||
"typeCountry": "Países",
|
||||
"region": "Região",
|
||||
"allRegions": "Todas as regiões",
|
||||
"language": "Idioma",
|
||||
"startCollapsed": "Começar recolhido",
|
||||
"embed": "Copiar código de incorporação",
|
||||
"embedNote": "Cole isto em qualquer página da web.",
|
||||
"copy": "Copiar",
|
||||
"copied": "Copiado"
|
||||
}
|
||||
4
library/i18n/locales/ro/posterCard.json
Normal file
4
library/i18n/locales/ro/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Vezi panoul",
|
||||
"showDashboardAria": "Vezi panoul {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Pachet de rugăciune",
|
||||
"cards": "Cartonașe de rugăciune",
|
||||
"wall": "Panouri de perete",
|
||||
"mapPrint": "Tipărirea hărții"
|
||||
"mapPrint": "Tipărirea hărții",
|
||||
"plugins": "Din alte pluginuri"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Foaie de hartă a țării",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "Colile, benzile de lipire și locul rândurilor de cartonașe vin din geometria comună a panourilor; cartonașele sunt arătate ca machetă, pentru că peretele Binecuvântează își desenează cartonașele în propriul modul.",
|
||||
"mapPrint": "Acest șablon tipărește harta live pe care o privește cititorul, așa că nu are imagine de exemplu."
|
||||
"mapPrint": "Acest șablon tipărește harta live pe care o privește cititorul, așa că nu are imagine de exemplu.",
|
||||
"noCountryAtSize": "Astăzi nicio țară nu se tipărește la această dimensiune: India, singura care o făcea, se tipărește acum doar la 5 × 8 ft. Șablonul este desenat aici cu o țară exemplu."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Numele configurației",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Date de exemplu",
|
||||
"renderer": "Generator",
|
||||
"legacy": "Dimensiune veche: încă se tipărește, dar selectorul de tipărire nu o mai oferă.",
|
||||
"forAgents": "Ca să folosești acest șablon, spune-i unui agent: folosește configurația {name}."
|
||||
"forAgents": "Ca să folosești acest șablon, spune-i unui agent: folosește configurația {name}.",
|
||||
"openPage": "Deschide pagina"
|
||||
},
|
||||
"templateCount": "{n} șablon | {n} șabloane | {n} de șabloane"
|
||||
"templateCount": "{n} șablon | {n} șabloane | {n} de șabloane",
|
||||
"fromPlugin": "Este realizat de propriul plugin. Afișează toate detaliile îl arată în direct."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/ro/posterSections.json
Normal file
14
library/i18n/locales/ro/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Secțiuni de postere",
|
||||
"intro": "Alege posterele, vezi-le mai jos, apoi copiază eticheta ca să le pui pe orice site.",
|
||||
"type": "Tipul posterului",
|
||||
"typeCountry": "Țări",
|
||||
"region": "Regiune",
|
||||
"allRegions": "Toate regiunile",
|
||||
"language": "Limbă",
|
||||
"startCollapsed": "Începe restrâns",
|
||||
"embed": "Copiază codul de încorporare",
|
||||
"embedNote": "Lipește asta în orice pagină web.",
|
||||
"copy": "Copiază",
|
||||
"copied": "Copiat"
|
||||
}
|
||||
4
library/i18n/locales/ru/posterCard.json
Normal file
4
library/i18n/locales/ru/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "Открыть панель",
|
||||
"showDashboardAria": "Открыть панель: {label}"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "Молитвенный пакет",
|
||||
"cards": "Молитвенные карточки",
|
||||
"wall": "Настенные панели",
|
||||
"mapPrint": "Печать карты"
|
||||
"mapPrint": "Печать карты",
|
||||
"plugins": "Из других плагинов"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "Лист карты страны",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "Листы, полосы для склейки и места рядов карточек взяты из общей геометрии панелей; карточки показаны как макет, потому что стена «Благословить» рисует свои карточки в собственном плагине.",
|
||||
"mapPrint": "Этот шаблон печатает живую карту, на которую смотрит читатель, поэтому у него нет примерного изображения."
|
||||
"mapPrint": "Этот шаблон печатает живую карту, на которую смотрит читатель, поэтому у него нет примерного изображения.",
|
||||
"noCountryAtSize": "Сегодня ни одна страна не печатается в этом размере: Индия, единственная, что печаталась, теперь печатается только в 5 × 8 футов. Шаблон показан здесь на примере другой страны."
|
||||
},
|
||||
"details": {
|
||||
"configName": "Название конфигурации",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "Примерные данные",
|
||||
"renderer": "Генератор",
|
||||
"legacy": "Устаревший размер: он ещё печатается, но в выборе печати его больше нет.",
|
||||
"forAgents": "Чтобы использовать этот шаблон, скажите агенту: используй конфигурацию {name}."
|
||||
"forAgents": "Чтобы использовать этот шаблон, скажите агенту: используй конфигурацию {name}.",
|
||||
"openPage": "Открыть страницу"
|
||||
},
|
||||
"templateCount": "{n} шаблон | {n} шаблона | {n} шаблонов | {n} шаблонов"
|
||||
"templateCount": "{n} шаблон | {n} шаблона | {n} шаблонов | {n} шаблонов",
|
||||
"fromPlugin": "Создаётся собственным плагином. «Показать все подробности» покажет его вживую."
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/ru/posterSections.json
Normal file
14
library/i18n/locales/ru/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "Разделы плакатов",
|
||||
"intro": "Выберите плакаты, посмотрите их ниже, затем скопируйте тег, чтобы разместить их на любом сайте.",
|
||||
"type": "Тип плаката",
|
||||
"typeCountry": "Страны",
|
||||
"region": "Регион",
|
||||
"allRegions": "Все регионы",
|
||||
"language": "Язык",
|
||||
"startCollapsed": "Начинать свёрнутым",
|
||||
"embed": "Скопировать код для вставки",
|
||||
"embedNote": "Вставьте это на любую веб-страницу.",
|
||||
"copy": "Копировать",
|
||||
"copied": "Скопировано"
|
||||
}
|
||||
4
library/i18n/locales/zh/posterCard.json
Normal file
4
library/i18n/locales/zh/posterCard.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"showDashboard": "查看仪表板",
|
||||
"showDashboardAria": "查看 {label} 仪表板"
|
||||
}
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"packet": "祷告资料包",
|
||||
"cards": "祷告卡",
|
||||
"wall": "墙面展板",
|
||||
"mapPrint": "地图打印"
|
||||
"mapPrint": "地图打印",
|
||||
"plugins": "来自其他插件"
|
||||
},
|
||||
"tpl": {
|
||||
"mapSheetCountry": "国家地图页",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
},
|
||||
"missing": {
|
||||
"bless": "纸张、粘贴条以及卡片行的位置都来自共享的拼板几何;卡片以版式示意,因为祝福墙在其自己的插件中绘制卡片。",
|
||||
"mapPrint": "此模板打印读者正在查看的实时地图,因此没有示例图片。"
|
||||
"mapPrint": "此模板打印读者正在查看的实时地图,因此没有示例图片。",
|
||||
"noCountryAtSize": "如今没有国家以此尺寸印刷:唯一曾以此尺寸印刷的印度,现在只以 5 × 8 英尺印刷。这里用一个示例国家绘制,以展示该模板。"
|
||||
},
|
||||
"details": {
|
||||
"configName": "配置名称",
|
||||
|
|
@ -60,7 +62,9 @@
|
|||
"sample": "示例数据",
|
||||
"renderer": "生成器",
|
||||
"legacy": "旧尺寸:仍可打印,但打印选择器已不再提供。",
|
||||
"forAgents": "要使用此模板,请告诉代理:使用配置 {name}。"
|
||||
"forAgents": "要使用此模板,请告诉代理:使用配置 {name}。",
|
||||
"openPage": "打开其页面"
|
||||
},
|
||||
"templateCount": "{n} 个模板"
|
||||
"templateCount": "{n} 个模板",
|
||||
"fromPlugin": "由其自身插件生成。点击“显示全部详情”可实时查看。"
|
||||
}
|
||||
|
|
|
|||
14
library/i18n/locales/zh/posterSections.json
Normal file
14
library/i18n/locales/zh/posterSections.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"heading": "海报分区",
|
||||
"intro": "选择海报,在下方查看,然后复制标签,放到任何网站上。",
|
||||
"type": "海报类型",
|
||||
"typeCountry": "国家",
|
||||
"region": "地区",
|
||||
"allRegions": "所有地区",
|
||||
"language": "语言",
|
||||
"startCollapsed": "默认折叠",
|
||||
"embed": "复制嵌入代码",
|
||||
"embedNote": "将其粘贴到任何网页中。",
|
||||
"copy": "复制",
|
||||
"copied": "已复制"
|
||||
}
|
||||
|
|
@ -172,9 +172,14 @@ export function buildPaneColumn(spec, { host, mountPane }) {
|
|||
return {
|
||||
panes: column,
|
||||
/** Animate to a pane by id — this is what the View fieldset tabs call. */
|
||||
// ★ SCROLL THE COLUMN, NEVER THE PAGE. scrollIntoView() scrolls EVERY scrollable ancestor, the embedding
|
||||
// page included: on another site, "Map" / "Show on map" moved the host's own page so the map's top met the
|
||||
// window's top, and a host shorter than the window then showed its white space under the map. The column is
|
||||
// the scroller (position:absolute; overflow-y:auto) and each pane's offsetParent, so its offsetTop is exactly
|
||||
// the scroll position that puts the pane at the top.
|
||||
scrollTo(paneId) {
|
||||
const target = col.querySelector(`.pane[data-pane-id="${paneId}"]`)
|
||||
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
if (target) col.scrollTo({ top: target.offsetTop, behavior: 'smooth' })
|
||||
},
|
||||
/** Which pane is currently filling the viewport (for URL sync). */
|
||||
current() {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ export function seriesFor(countries, item) {
|
|||
* packet and cards are offered on a desktop or laptop only (a phone cannot draw a sheet that big).
|
||||
* The data (country-posters.json) still records why no PAPER sheet fits; this table is what prints instead.
|
||||
*/
|
||||
export const LARGE_SHEETS = { IND: { format: '60x96', formats: ['60x96', '66x66', '36x60'], desktopOnly: true } }
|
||||
// India: 5 x 8 ft (60x96) ONLY, the size its whole-country layout is built and tuned for.
|
||||
export const LARGE_SHEETS = { IND: { format: '60x96', formats: ['60x96'], desktopOnly: true } }
|
||||
const largeOf = (item) => (item && LARGE_SHEETS[String(item.iso3 || '').toUpperCase()]) || null
|
||||
/**
|
||||
* ★ WITHHELD: a whole-country poster that is NOT shipped yet, though the renderer can draw it. India's 855 groups do
|
||||
|
|
@ -89,9 +90,21 @@ const largeOf = (item) => (item && LARGE_SHEETS[String(item.iso3 || '').toUpperC
|
|||
* packet and cards with the same sentence. India's STATE and union-territory posters are separate cards
|
||||
* (`indiaState`) and are not affected. check:country-sheet-one-path fails if India's poster comes back unannounced.
|
||||
*/
|
||||
export const WITHHELD = new Set(['IND'])
|
||||
export function sheetWithheld(item) { return Boolean(item && !item.indiaState && WITHHELD.has(String(item.iso3 || '').toUpperCase())) }
|
||||
export function largeSheet(item) { return sheetWithheld(item) ? null : largeOf(item) }
|
||||
// WHAT of a whole country is withheld: its POSTER, and/or its ROWS (its prayer list, packet and cards).
|
||||
// India's whole-country poster is withheld EVERYWHERE BY DEFAULT (it is not released yet). It prints only in an
|
||||
// explicit PREVIEW context: the Vite DEV server (the pilot's Plugin Studio, where it is reviewed), whose element
|
||||
// bootstrap sets globalThis.DXM_PREVIEW_UNRELEASED. The read is gated on import.meta.env.DEV too, so every BUILT bundle
|
||||
// compiles it away: no page, embed or console can set a global and print it from production. Never by a URL
|
||||
// parameter. Its rows (about a thousand pages) stay withheld everywhere.
|
||||
export const WITHHELD = { IND: { poster: true, rows: true, preview: true } }
|
||||
export const previewUnreleased = () => Boolean(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true)
|
||||
const withheldOf = (item) => (item && !item.indiaState && WITHHELD[String(item.iso3 || '').toUpperCase()]) || null
|
||||
// No caller can open it: the preview state is read HERE, never passed in (so a built bundle cannot print it).
|
||||
export function posterWithheld(item) { const w = withheldOf(item); return Boolean(w?.poster) && !(w.preview && previewUnreleased()) }
|
||||
export function rowsWithheld(item) { return Boolean(withheldOf(item)?.rows) }
|
||||
/** The poster's withholding (the name Poster Design reads). */
|
||||
export function sheetWithheld(item) { return posterWithheld(item) }
|
||||
export function largeSheet(item) { return posterWithheld(item) ? null : largeOf(item) }
|
||||
// The sheet format a request gets: a large-sheet country prints at the large size ASKED FOR when it has one (66x66,
|
||||
// 36x60), else at its default (60x96); a packet's page 1 stays a page; every other country gets exactly what it asked.
|
||||
export function sheetFormatFor(item, format) {
|
||||
|
|
@ -115,7 +128,7 @@ export function makeCountrySheet({ records, assetsBase, countries = null }) {
|
|||
return async function countrySheet(item, format) {
|
||||
const iso3 = item && item.iso3
|
||||
if (!iso3) return '' // a selection or a row without a country: no sheet
|
||||
if (sheetWithheld(item)) return '' // withheld (above): nothing is fetched or drawn
|
||||
if (posterWithheld(item)) return '' // a withheld poster (above): nothing is fetched or drawn
|
||||
// Lazy, like the zone sheet: the generator and the boundary loader are only pulled when a
|
||||
// reader actually prints, so they stay out of every wall's initial download.
|
||||
const { generateCountrySheet } = await import('@map/packet/posterSheets.js')
|
||||
|
|
|
|||
19
package.json
19
package.json
|
|
@ -259,6 +259,7 @@
|
|||
"check:poster-card-fits": "node internal/check-poster-card-fits.mjs",
|
||||
"check:poster-degrades": "node internal/check-poster-degrades-honestly.mjs",
|
||||
"check:poster-design-catalogue": "node plugin-designs/poster-design/scripts/check-catalogue.mjs",
|
||||
"check:poster-design-catalogue:self-test": "node plugin-designs/poster-design/scripts/check-catalogue.mjs --self-test",
|
||||
"check:poster-fetch-guard": "node internal/check-poster-fetch-guard.mjs",
|
||||
"check:poster-print-fallback": "node internal/check-poster-print-fallback.mjs",
|
||||
"check:posters-exportable": "node internal/check-posters-exportable.mjs",
|
||||
|
|
@ -353,8 +354,8 @@
|
|||
"dev": "bun x vite",
|
||||
"fix:sri": "node internal/fix-sri.mjs",
|
||||
"gallery": "node internal/generate-gallery.mjs",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:script-flags check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:region-paint-single-source check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:leaf-hints check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight check:no-fleet-names:self-test check:pin-hint-floor:self-test check:poster-sizes:self-test check:shell-header:self-test check:preview-flag-dev-only:self-test check:poster-type-registry:self-test",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:pins-in-state check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:india-states check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-internal-role-nouns check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:side-lanes check:side-lanes:self-test check:poster-section check:poster-section:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch check:panel-sheets check:poster-wall-host-seams check:poster-design-catalogue check:wall-photo-weight check:no-fleet-names check:pin-hint-floor check:poster-sizes check:upg-count check:shell-header check:pg-name-gate check:preview-flag-dev-only check:poster-type-registry",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:script-flags check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:region-paint-single-source check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:leaf-hints check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight check:no-fleet-names:self-test check:pin-hint-floor:self-test check:poster-sizes:self-test check:shell-header:self-test check:preview-flag-dev-only:self-test check:poster-type-registry:self-test check:host-floor:self-test",
|
||||
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:pins-in-state check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:india-states check:batch-selection-export check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-internal-role-nouns check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:popup-pan-controls check:country-map-refetch check:pane-config-reforward check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:country-sheet-one-path check:country-sheet-one-path:self-test check:side-lanes check:side-lanes:self-test check:poster-section check:poster-section:self-test check:posterverse-shelf check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:coverage-consistent:poison check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:wall-wysiwyg check:wall-print-watch check:wall-no-bg-photos check:wall-pan check:template-i18n check:list-pattern-doc check:shadow-style-labels-unique check:grid-image-loading check:retry-fetch check:panel-sheets check:poster-wall-host-seams check:poster-sections check:poster-sections:self-test check:poster-design-catalogue check:poster-design-catalogue:self-test check:wall-photo-weight check:no-fleet-names check:pin-hint-floor check:poster-sizes check:upg-count check:shell-header check:pg-name-gate check:preview-flag-dev-only check:poster-type-registry check:host-floor",
|
||||
"gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs",
|
||||
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
|
|
@ -423,10 +424,15 @@
|
|||
"check:side-lanes:self-test": "node plugin-designs/country-posters/scripts/check-side-lanes.mjs --self-test",
|
||||
"check:poster-section": "node plugin-designs/country-posters/scripts/check-poster-section.mjs",
|
||||
"check:poster-section:self-test": "node plugin-designs/country-posters/scripts/check-poster-section.mjs --self-test",
|
||||
"check:poster-sections": "node plugin-designs/poster-sections/scripts/check-poster-sections.mjs",
|
||||
"check:poster-sections:self-test": "node plugin-designs/poster-sections/scripts/check-poster-sections.mjs --self-test",
|
||||
"gen:poster-sections-presets": "node plugin-designs/poster-sections/scripts/gen-presets.mjs",
|
||||
"check:side-lane-photo-pixels": "node plugin-designs/country-posters/scripts/check-side-lane-photo-pixels.mjs",
|
||||
"check:side-lane-photo-pixels:self-test": "node plugin-designs/country-posters/scripts/check-side-lane-photo-pixels.mjs --self-test",
|
||||
"check:poster-type-registry": "node internal/check-poster-type-registry.mjs",
|
||||
"check:poster-type-registry:self-test": "node internal/check-poster-type-registry.mjs --self-test"
|
||||
"check:poster-type-registry:self-test": "node internal/check-poster-type-registry.mjs --self-test",
|
||||
"check:host-floor": "node internal/check-host-floor.mjs",
|
||||
"check:host-floor:self-test": "node internal/check-host-floor.mjs --self-test"
|
||||
},
|
||||
"//scripts": {
|
||||
"_": "What each script does. These notes used to sit inside the command strings as trailing \"# …\" comments, which made the shell discard any arguments passed after \"--\" (so a gate given --url silently measured its hardcoded default port — possibly another agent's server). Keep notes here, never in a command.",
|
||||
|
|
@ -602,7 +608,8 @@
|
|||
"check:print-frame-reclaim": "a print frame is kept until it has printed: openPrintFrame reclaims it 60 s after its load (not after its creation), at afterprint, or at a 10-minute ceiling, and printImageBlob revokes its object URL on the same clock. Fake timers; two-sided (the pre-fix copy fails). The Wall poster loads 132 MB of photos, and the old 60 s-from-creation clock removed the frame before it could print.",
|
||||
"check:poster-card-fits": "a poster card's print-options row must stay inside its own card. Reported by the repo agent off a 1440px screenshot: 27/27 cards overflowing by 110px, 36 neighbour intrusions. .pfs already had width:82%, so SOURCE READING SAID IT WAS FINE — flex items default to min-width:auto and refuse to shrink. Asserts BOTH no-overlap AND that the controls keep a usable box, because a row whose buttons collapsed to nothing would also \"not overflow\". Sweeps 390/768/1024/1440/1920px and asserts the row sits BELOW the preview too — measuring only left/right edges is how it passed a driver-visible regression. CARD_FIT_WIDTH overrides the list. SKIPs when nothing is served.",
|
||||
"check:poster-degrades": "with NO stored poster, NO source and NO generator (forced in the browser: every posters/*.webp and *.html answers 404, lib/poster-render.js is aborted), nothing must print and the reader must be told why. Measures any served tree; NOT MEASURED without a wall url.",
|
||||
"check:poster-design-catalogue": "Poster Design's configuration catalogue (plugin-designs/poster-design/src/templates.js) says only true things about the library: its sheet formats are exactly poster-render.js FORMATS at the same pixel sizes (the generator is a pinned asset, so the table cannot be imported); every configuration name is unique and well formed; the card entries are exactly card-render STOCK; every catalogue key it names exists in en/posterDesign.json; the sample country can be drawn. Six mutants (px drift, a missing format, a duplicate name, a bad key, an undrawable sample, a dropped stock) each FAIL.",
|
||||
"check:poster-design-catalogue": "Poster Design's configuration catalogue (plugin-designs/poster-design/src/templates.js) says only true things about the library: its sheet formats are exactly poster-render.js FORMATS at the same pixel sizes (the generator is a pinned asset, so the table cannot be imported); every configuration name is unique and well formed; the card entries are exactly card-render STOCK; every catalogue key it names exists in en/posterDesign.json; the sample country can be drawn. Six mutants (px drift, a missing format, a duplicate name, a bad key, an undrawable sample, a dropped stock) each FAIL. It also validates every plugin's posterDesign block (plugin-designs/<plugin>/<profile>.meta.json: family, name, size, mobileFriendly, optional labelKey and instance), keeps names unique across templates.js and all blocks, and fails when a name in the names ledger (src/catalogue-names.json) was renamed or removed.",
|
||||
"check:poster-design-catalogue:self-test": "an invalid posterDesign block, a name that clashes with templates.js, a renamed ledger name a block naming a preset that does not exist, and a sample that exists but cannot be drawn (India with withholding off) must each make check:poster-design-catalogue fail, and the documented example block must pass.",
|
||||
"check:poster-fetch-guard": "a host's 200 fallback page is never taken for a poster. On a drop-in dev host a missing posters/<id>.webp answered 200 text/html, and export handed that page to the print dialog as the image. Asserts library/packet/poster-fetch.js accepts a raster only if it is an image (by type or by its first bytes) and a source only if it is a poster document (class=\"stage\"), and that neither usePosterPrints nor PosterWall fetches posters/ directly. Node only, no server.",
|
||||
"check:poster-layout-optin": "the composed poster sheet (header, map, legend beside it, footer) is OPT-IN: spec.layout === 'poster' asks for it, and every caller that does not gets the full-page map exactly as before, asserted by object identity, not just by value. Drives the real layout decisions with real spec objects. Headless, no browser.",
|
||||
"check:poster-print-fallback": "clicks the REAL poster print control with the served poster generator and every posters/*.webp aborted, and asserts the preview RASTERISES from posters/<id>.html instead of opening on \"Poster not available yet\". Needs a served tree built with posterSources:true; NOT MEASURED otherwise.",
|
||||
|
|
@ -698,8 +705,12 @@
|
|||
"check:side-lanes:self-test": "a poster-render.js copy WITHOUT the lane clamp must make check:side-lanes' crossing arm FAIL on the same seeded data (it does: 36 crossings), so the arm is shown able to fail.",
|
||||
"check:poster-section": "the reusable poster section (<dxm-poster-section>): the type registry has 'country'; DX Life's /regions slugs (and every other spelling of a region) each show their countries, every country once; a withheld whole-country poster stays withheld through the type; the element's attributes map to its config; PosterSection.vue injects every name its template uses.",
|
||||
"check:poster-section:self-test": "a registry without 'country', a region key without \"&\" → \"and\", and a section that uses a name it does not inject must each make check:poster-section fail.",
|
||||
"check:poster-sections": "the Poster Sections page (plugin-designs/poster-sections): every panel choice (each region or All regions, each language, open or collapsed, 1-5 per row) builds <dxm-poster-section> tags that the element's own attribute reader turns back into exactly those choices; All regions is one section per region; Copy embed is the bundle script plus those same tags. The presets file holds, per poster type, exactly an all-regions preset plus one per region its adapter's scopes() returns, so a region the data drops (or gains) cannot drift from the presets; a poster type not on the tree has no presets (its pages could show no posters); and the built preset pages are exactly the presets (no stale page).",
|
||||
"check:poster-sections:self-test": "a tag builder that drops columns, one that drops collapsed, one that repeats a region under All regions, a preset for a region the type does not have, and a built page for a preset that no longer exists must each make check:poster-sections fail.",
|
||||
"gen:poster-sections-presets": "writes plugin-designs/poster-sections/poster-sections.instances.json from each poster type's own scopes() (all regions + one preset per region, titled by the type's own section title, then configure); the list check:poster-sections holds the file to.",
|
||||
"check:side-lane-photo-pixels": "BROWSER gate (browser seat; elsewhere 75 NOT MEASURED): the 60x96 side-lane sheet rasterised by poster-raster.js, measured in pixels at every photo slot. A data: photo draws; every photo on its own host stays flat, never the broken-image glyph an external <image> rasterises as.",
|
||||
"check:side-lane-photo-pixels:self-test": "the same raster WITHOUT stripExternalImages must leave the external photo slots NOT flat (the glyph), else the gate proves nothing.",
|
||||
"check:host-floor": "the profile floor (min-height 100svh) is decided against the HOST without disturbing live content (library/elements/hostFloor.js, used by ProfileLoader): measured ONCE at mount; after that host resizes never write the loader's min-height nor move a scrolled child's scrollTop (the regression: a per-resize probe reset the embedded UPG-100 app from Map to its first view); a sized host is filled; a host sized after mount flips once; a max-height cap never loops; a collapsed host gets the floor back; an inline host is left alone. Stub DOM, headless. Run the `check:host-floor:self-test` script for the NEGATIVE test (the old per-resize probe and a missing latch).",
|
||||
"check:poster-type-registry": "the poster type registry is ONE per page: every bundle inlines its own copy of posterTypes.js and <dxm-poster-section> is defined by whichever bundle loads first, so with two independent copies loaded side by side a type registered through one is found through the other; the built-in country is listed once; a name registered twice keeps the first; every registration is announced on window (dxm-poster-type-registered) and PosterSectionView re-resolves on it. Run the `check:poster-type-registry:self-test` script for the NEGATIVE test (a per-bundle registry fails)."
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import { getRuntimeLocale, detectLocale, tStatic } from '@map/i18n/index.js'
|
|||
// library/. Generated by library/scripts/posters/emit-countries.mjs, which now writes there.
|
||||
import data from '@map/data/country-posters.json'
|
||||
import { resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { makeCountrySheet, largeSheet, sheetWithheld } from '@map/packet/countrySheet.js'
|
||||
import { makeCountrySheet, largeSheet, posterWithheld, rowsWithheld } from '@map/packet/countrySheet.js'
|
||||
import { isPhonePrint } from '@map/composables/printInPlace.js'
|
||||
import { keyedError } from '@map/i18n/keyedError.js'
|
||||
// India's states and union territories: the shared table (fetched when the wall shows them) and their sheet.
|
||||
|
|
@ -170,7 +170,7 @@ const desktopOnly = () => Promise.reject(keyedError('poster.phoneLimitAll', 'Thi
|
|||
// A WITHHELD whole-country poster (countrySheet.js WITHHELD: India) withholds its list, packet and cards too, with
|
||||
// the same sentence the card shows (about a thousand pages, offered when the poster is).
|
||||
const withheld = () => Promise.reject(keyedError('messages.countryPosterTooMany', 'Too many people groups to fit on one poster legibly — a solution is in progress.'))
|
||||
const countryRecords = (c) => (sheetWithheld(c) ? withheld() : phoneBlocked(c) ? desktopOnly() : c.indiaState
|
||||
const countryRecords = (c) => (rowsWithheld(c) ? withheld() : phoneBlocked(c) ? desktopOnly() : c.indiaState
|
||||
? (c.slugs.length ? recordsFor({ type: 'pg_slugs', values: c.slugs }) : Promise.resolve([]))
|
||||
: recordsFor({ type: 'country', values: [c.iso3] }))
|
||||
// ★ GENERATED AT THE CLICK, through the ONE shared path (@map/packet/countrySheet.js, which the Regions
|
||||
|
|
@ -244,7 +244,7 @@ const collapsedBy = (sec) => sec.total < 60
|
|||
// unsupportedReason is an English note (and a flag), not display text. The card already states the
|
||||
// count above it, so the sentence needs no number and no plural agreement.
|
||||
// A requested country with no poster at all says so in regions-list.vue's words (the regions catalogue).
|
||||
const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : sheetWithheld(c) ? t('messages.countryPosterTooMany') : phoneBlocked(c) ? t('poster.phoneLimitAll') : c.unsupportedReason && !largeSheet(c) ? t('messages.countryPosterTooMany') : null)
|
||||
const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : posterWithheld(c) ? t('messages.countryPosterTooMany') : phoneBlocked(c) ? t('poster.phoneLimitAll') : c.unsupportedReason && !largeSheet(c) ? t('messages.countryPosterTooMany') : null)
|
||||
|
||||
// ★ THROUGH THE CATALOGUE, like the UPG-100 wall: the heading, the tag, the intro and the counts read
|
||||
// in the page's language. The intro keeps its one emphasis as three plain keys (A, the emphasis, B)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue'
|
|||
import { buildPaneColumn, PANE_GRID_STYLES } from '@map/layouts/pane-wrapper/pane-wrapper.js'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
import ViewSelect from '@map/components/ViewSelect.vue'
|
||||
import { countryName } from '@map/utils/countryName.js'
|
||||
import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
|
||||
import ShareButton from '@map/components/map-controls/ShareButton.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import enTabs from '@map/i18n/locales/en/tabs.json'
|
||||
|
|
@ -68,13 +70,21 @@ const spec = computed(() => ({
|
|||
// strip desynchronises from the scroll column.
|
||||
panes: [
|
||||
{ id: 'wall', label: t('tabs.countryPosters'), at: [0, 0], config: { profile: 'country-poster-wall' } },
|
||||
{ id: 'map', label: t('tabs.map'), at: [1, 0], config: { profile: 'countries-map' } },
|
||||
// the same DEV-gated read as countrySheet.js previewUnreleased(), INLINE so every build drops the pane entirely
|
||||
...(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true ? [INDIA_PANE] : []),
|
||||
{ id: 'map', label: t('tabs.map'), at: [2, 0], config: { profile: 'countries-map' } },
|
||||
],
|
||||
// The switcher lives in this element's own header, so the layout's "… below ↓"
|
||||
// pill would be a second, redundant control (same call upg-100-mfe makes).
|
||||
paneHint: false,
|
||||
}))
|
||||
|
||||
// INDIA: the whole-India poster (66x66 by default) on its own tab, the same wall filtered to India, labelled with
|
||||
// India's own name in the reader's language (Intl). Shown ONLY in a preview context (countrySheet.js
|
||||
// previewUnreleased(): the Plugin Studio on the pilot), because the poster is not released; everywhere else the tab
|
||||
// is absent and India's card keeps the withheld sentence.
|
||||
const INDIA_PANE = { id: 'india', label: countryName('IND', getRuntimeLocale() || detectLocale() || 'en') || 'India', at: [1, 0], config: { profile: 'country-poster-wall', countries: ['IND'] } }
|
||||
|
||||
const stage = ref(null)
|
||||
// DERIVED from the pane order, never a hardcoded id — the same trap upg-100-mfe
|
||||
// records: `ref('map')` silently became wrong the moment the panes were reordered.
|
||||
|
|
|
|||
|
|
@ -17,14 +17,27 @@
|
|||
// absent from the data gets no series.
|
||||
//
|
||||
// Exit 0 pass, 1 fail, 75 not measured. `--self-test` plants each defect and requires its arm to fail.
|
||||
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
||||
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
||||
import { join, relative, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { parse as parseSfc, babelParse } from '@vue/compiler-sfc'
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..')
|
||||
const PD = join(ROOT, 'plugin-designs')
|
||||
const KNOWN_CALLERS = ['plugin-designs/country-posters/country-poster-wall.vue', 'plugin-designs/regions-dashboard/regions-list.vue']
|
||||
const KNOWN_CALLERS = ['plugin-designs/country-posters/country-poster-wall.vue']
|
||||
// The Regions list may draw a country sheet itself OR delegate to <dxm-poster-section> (whose library type draws it).
|
||||
// If it still calls makeCountrySheet it must pass arms F and G like the wall; either way the library type must.
|
||||
const REGIONS_LIST = 'plugin-designs/regions-dashboard/regions-list.vue'
|
||||
const COUNTRY_TYPE = 'library/components/poster/types/countryPosterType.js'
|
||||
// ARM H: the library 'country' type (the element's caller) withholds like the wall: rows reject with the sentence,
|
||||
// a withheld poster's card shows the sentence.
|
||||
export function armH(typeSrc) {
|
||||
const fails = []
|
||||
if (!/makeCountrySheet\(/.test(typeSrc)) fails.push(`${COUNTRY_TYPE}: no longer draws through makeCountrySheet`)
|
||||
if (!/rowsWithheld\(c\) \? Promise\.reject\(keyedError\('messages\.countryPosterTooMany'/.test(typeSrc)) fails.push(`${COUNTRY_TYPE}: withheld ROWS are not withheld (the element would print India's list)`)
|
||||
if (!/posterWithheld\(c\) \|\|[^?]*\? t\('messages\.countryPosterTooMany'\)/.test(typeSrc)) fails.push(`${COUNTRY_TYPE}: a withheld POSTER's card does not show the sentence`)
|
||||
return fails
|
||||
}
|
||||
// India's states and union territories draw through their own ONE path; inside this plugin only that helper may
|
||||
// call the generator directly.
|
||||
const STATE_HELPER = 'plugin-designs/country-posters/src/indiaStateSheet.js'
|
||||
|
|
@ -99,11 +112,11 @@ export function armB(seriesFor, countries, sheetFormatFor = null) {
|
|||
const by = (iso) => countries.find((c) => c.iso3 === iso)
|
||||
const ind = by('IND')
|
||||
const s = ind && seriesFor(countries, ind)
|
||||
// THE LARGE-SHEET FORMAT RULE: India prints the large size ASKED FOR, else its 60x96 default; a packet page stays a
|
||||
// THE LARGE-SHEET FORMAT RULE: India prints 60x96 ONLY (any request but a packet page gets it); a packet page stays a
|
||||
// page; any other country gets exactly what it asked for.
|
||||
if (sheetFormatFor) {
|
||||
const want = [[ind, '66x66', '66x66'], [ind, '36x60', '36x60'], [ind, '60x96', '60x96'], [ind, 'letter', '60x96'], [ind, '3x4', '60x96'], [ind, 'packet', 'packet'],
|
||||
[by('NPL'), '3x3', '3x3'], [by('NPL'), 'letter', 'letter'], [by('CHN'), '3x4', '3x4']]
|
||||
const want = [[ind, '60x96', '60x96'], [ind, '66x66', '60x96'], [ind, '36x60', '60x96'], [ind, 'letter', '60x96'], [ind, '3x4', '60x96'], [ind, 'packet', 'packet'],
|
||||
[by('NPL'), '3x3', '3x3'], [by('NPL'), 'letter', 'letter'], [by('CHN'), '3x4', '3x4'], [by('NPL'), '66x66', '66x66']]
|
||||
for (const [item, ask, exp] of want) { const got = sheetFormatFor(item, ask); if (got !== exp) fails.push(`sheetFormatFor(${item?.iso3}, ${ask}) = ${got}, expected ${exp}`) }
|
||||
}
|
||||
const oversized = countries.filter((c) => (c.sheet ?? null) === null)
|
||||
|
|
@ -179,27 +192,41 @@ export function armF(srcs) {
|
|||
// ARM E: India is its OWN section, FIRST, counted ONCE. The India country card, the state cards and the none-listed
|
||||
// cards group together; the section's total is India's own count (not the card sum, which counts 855 twice); on that
|
||||
// true total it outranks every regional section. PosterWall's default (no sectionTotal) must still be the card sum.
|
||||
// ARM G — A WITHHELD WHOLE-COUNTRY POSTER stays withheld (India: not shipped until it prints legibly). The library
|
||||
// draws NOTHING for it in any format and fetches no record (0 big jobs); a state card is never withheld; and each
|
||||
// caller shows messages.countryPosterTooMany on the card and rejects the country's rows with it (no list/packet/cards).
|
||||
export async function armG(cs, srcs) {
|
||||
// ARM G — WHAT OF A WHOLE COUNTRY IS WITHHELD (countrySheet.js WITHHELD), and the PREVIEW guard. India:
|
||||
// (a) BY DEFAULT (every public host: DX Life, the research landing, any embed) its whole-country POSTER is withheld
|
||||
// (the sentence; largeSheet() null) and its ROWS are withheld;
|
||||
// (b) only in a PREVIEW context (the Vite dev server: the Plugin Studio on the pilot) the poster prints, 60x96 ONLY; its rows stay withheld. A state card is never withheld.
|
||||
// Each caller shows the sentence for a withheld POSTER and rejects the rows for withheld ROWS; Country Posters' India
|
||||
// tab exists only under the preview flag.
|
||||
export async function armG(cs, srcs, mfeSrc = '') {
|
||||
const fails = []
|
||||
if (!(cs.WITHHELD instanceof Set) || !cs.WITHHELD.has('IND')) fails.push("countrySheet.js WITHHELD no longer holds India: its whole-country poster would ship")
|
||||
let fetched = 0
|
||||
const sheet = cs.makeCountrySheet({ records: () => { fetched++; return Promise.resolve([{ name: 'x', latitude: 20, longitude: 78 }]) }, assetsBase: () => '', countries: [] })
|
||||
for (const f of ['3x3', '3x4', 'letter', 'packet', '60x96', '66x66', '36x60']) {
|
||||
let out
|
||||
try { out = await sheet({ id: 'country-ind', iso3: 'IND', label: 'India' }, f) } catch (e) { out = 'THREW ' + e.message }
|
||||
if (out !== '') fails.push(`makeCountrySheet drew something for withheld India at ${f}`)
|
||||
}
|
||||
if (fetched) fails.push(`makeCountrySheet fetched records ${fetched}x for withheld India (a big job started)`)
|
||||
if (cs.sheetWithheld({ iso3: 'IND', indiaState: true, id: 'india-state-gujarat' })) fails.push('an India STATE card is withheld (only the whole-country poster may be)')
|
||||
if (cs.largeSheet({ iso3: 'IND' })) fails.push('largeSheet(India) is not null while withheld, so callers would offer its poster')
|
||||
const ind = { id: 'country-ind', iso3: 'IND', label: 'India' }
|
||||
// (a) by default: outside the Vite dev server (here: no import.meta.env.DEV) the preview is shut EVEN IF a page or a
|
||||
// console sets the global; (b) an open preview (passed explicitly) prints the poster at 66x66.
|
||||
const was = globalThis.DXM_PREVIEW_UNRELEASED
|
||||
try {
|
||||
globalThis.DXM_PREVIEW_UNRELEASED = true
|
||||
if (cs.previewUnreleased()) fails.push('(a) the preview opens outside the dev server from a global alone: a page/console could print the withheld poster from production')
|
||||
if (!cs.posterWithheld(ind) || cs.largeSheet(ind)) fails.push("(a) India's whole-country poster is NOT withheld by default: it would ship to every host")
|
||||
} finally { if (was === undefined) delete globalThis.DXM_PREVIEW_UNRELEASED; else globalThis.DXM_PREVIEW_UNRELEASED = was }
|
||||
if (!cs.rowsWithheld(ind)) fails.push("India's rows are not withheld: its list/packet/cards (~1,000 pages) would print")
|
||||
// (b) the open preview itself is proved under Vite (import.meta.env.DEV true) by check:poster-section; here, the size:
|
||||
const big = cs.LARGE_SHEETS && cs.LARGE_SHEETS.IND
|
||||
if (!big || big.format !== '60x96' || big.formats.join() !== '60x96') fails.push(`India's whole-country poster is not 60x96 ONLY (${big && big.formats})`)
|
||||
if (cs.posterWithheld.length > 1 || cs.largeSheet.length > 1) fails.push('posterWithheld()/largeSheet() take a second argument again: a production caller could open the withheld poster')
|
||||
if (cs.sheetWithheld(ind) !== cs.posterWithheld(ind)) fails.push('sheetWithheld() no longer means the poster')
|
||||
if (cs.rowsWithheld({ iso3: 'IND', indiaState: true, id: 'india-state-gujarat' })) fails.push('an India STATE card has withheld rows (only the whole country may)')
|
||||
for (const [name, src] of Object.entries(srcs)) {
|
||||
if (!/sheetWithheld\((?:c|item)\) \? t(?:Key)?\('messages\.countryPosterTooMany'\)/.test(src)) fails.push(`${name}: a withheld country's card does not show messages.countryPosterTooMany`)
|
||||
if (!/sheetWithheld\((?:c|item)\) \? (?:withheld\(\)|Promise\.reject\(keyedError\('messages\.countryPosterTooMany')/.test(src)) fails.push(`${name}: a withheld country's rows are not withheld (its list/packet/cards would print)`)
|
||||
if (/const withheld = /.test(src) && !/const withheld = \(\) => Promise\.reject\(keyedError\('messages\.countryPosterTooMany'/.test(src)) fails.push(`${name}: withheld() does not reject with messages.countryPosterTooMany`)
|
||||
if (!/posterWithheld\((?:c|item)\) \? t(?:Key)?\('messages\.countryPosterTooMany'\)/.test(src)) fails.push(`${name}: a withheld POSTER's card does not show messages.countryPosterTooMany`)
|
||||
if (!/rowsWithheld\((?:c|item)\) \? (?:withheld\(\)|Promise\.reject\(keyedError\('messages\.countryPosterTooMany')/.test(src)) fails.push(`${name}: withheld ROWS are not withheld (its list/packet/cards would print)`)
|
||||
if (/sheetWithheld\(/.test(src)) fails.push(`${name}: still reads sheetWithheld() (ambiguous: poster or rows?)`)
|
||||
}
|
||||
if (mfeSrc && !/\.\.\.\(import\.meta\.env && import\.meta\.env\.DEV && globalThis\.DXM_PREVIEW_UNRELEASED === true \? \[INDIA_PANE\] : \[\]\)/.test(mfeSrc)) fails.push('country-posters-mfe: the India tab is not gated on the DEV-only preview read')
|
||||
// the BUILT tree never mentions the flag (every read is compiled away)
|
||||
try {
|
||||
const pub = join(ROOT, 'public/dx-maps')
|
||||
for (const b of readdirSync(pub)) { const f = join(pub, b, `${b}.js`); if (existsSync(f) && readFileSync(f, 'utf8').includes('DXM_PREVIEW_UNRELEASED')) fails.push(`the built ${b}.js mentions DXM_PREVIEW_UNRELEASED (a preview read survived the build)`) }
|
||||
} catch (_) { /* no built tree here */ }
|
||||
return fails
|
||||
}
|
||||
|
||||
|
|
@ -242,6 +269,10 @@ async function main() {
|
|||
for (const p of listSources(PD)) files[relative(ROOT, p)] = readFileSync(p, 'utf8')
|
||||
|
||||
const REG = readFileSync(join(ROOT, 'plugin-designs/regions-dashboard/regions-list.vue'), 'utf8')
|
||||
const MFE = readFileSync(join(ROOT, 'plugin-designs/country-posters/country-posters-mfe.vue'), 'utf8')
|
||||
const TYPE = readFileSync(join(ROOT, COUNTRY_TYPE), 'utf8')
|
||||
const regIsCaller = /makeCountrySheet\(/.test(REG)
|
||||
const lists = (wallSrc, regSrc = REG) => (regIsCaller ? { wall: wallSrc, regions: regSrc } : { wall: wallSrc })
|
||||
if (selfTest) {
|
||||
const wall = KNOWN_CALLERS[0]
|
||||
const cases = [
|
||||
|
|
@ -255,17 +286,26 @@ async function main() {
|
|||
['a division silently dropped', () => armC(H, states.filter((st) => st.id !== 'india-mz'), adm1Ind)],
|
||||
['the wall stops passing sectionTotal (India counted twice)', () => armE(countries, states, files[wall].replace(':section-total=', ':x-section-total='), readFileSync(join(ROOT, 'library/components/poster/PosterWall.vue'), 'utf8'), H).fails],
|
||||
['India country card left in Asia', () => armE(countries, states, files[wall].replace("=== 'IND'", "=== 'XXX'"), readFileSync(join(ROOT, 'library/components/poster/PosterWall.vue'), 'utf8'), H).fails],
|
||||
['India ignores a requested 66x66', () => armB(seriesFor, countries, (it, f) => (it?.iso3 === 'IND' && f !== 'packet' ? '60x96' : f))],
|
||||
['India offered 66x66 again', () => armB(seriesFor, countries, (it, f) => (it?.iso3 === 'IND' && f === '66x66' ? '66x66' : sheetFormatFor(it, f)))],
|
||||
['a packet page becomes a large sheet', () => armB(seriesFor, countries, (it, f) => (it?.iso3 === 'IND' ? '60x96' : f))],
|
||||
['the wall resolves [] for a phone-blocked card', () => armF({ wall: files[wall].replace("const desktopOnly = () => Promise.reject(keyedError('poster.phoneLimitAll'", "const desktopOnly = () => Promise.resolve([]) || (keyedError('poster.phoneLimitAll'"), regions: REG })],
|
||||
['the Regions list resolves [] for a phone-blocked card', () => armF({ wall: files[wall], regions: REG.replace("phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll'", "phoneBlocked(item) ? Promise.resolve([]) || (keyedError('poster.phoneLimitAll'") })],
|
||||
// Only while the Regions list draws its own sheets (regIsCaller): a list that delegates to <dxm-poster-section> has
|
||||
// no phone-block code of its own to poison, and lists() leaves it out of arm F, so this case would read as MISSED.
|
||||
...(regIsCaller ? [['the Regions list resolves [] for a phone-blocked card', () => armF(lists(files[wall], REG.replace("phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll'", "phoneBlocked(item) ? Promise.resolve([]) || (keyedError('poster.phoneLimitAll'")))]] : []),
|
||||
['a locale loses a state name', () => armD(states, { ...areas, ro: { ...areas.ro, stateNames: Object.fromEntries(Object.entries(areas.ro.stateNames).filter(([k]) => k !== 'IN-GJ')) } }, meta)],
|
||||
['the names lose their licence line', () => armD(states, areas, { ...meta, licence: '' })],
|
||||
['the whole-India poster un-withheld', async () => armG({ ...CS, WITHHELD: new Set(), largeSheet: (it) => CS.LARGE_SHEETS[it?.iso3] || null, makeCountrySheet: (o) => async (it, f) => { await o.records({}); return '<svg/>' } }, { wall: files[wall], regions: REG })],
|
||||
['the wall prints the list of a withheld country', async () => armG(CS, { wall: files[wall].replace("const countryRecords = (c) => (sheetWithheld(c) ? withheld() : ", "const countryRecords = (c) => ("), regions: REG })],
|
||||
['India\'s poster released by default', async () => armG({ ...CS, posterWithheld: () => false, largeSheet: () => CS.LARGE_SHEETS.IND }, lists(files[wall]), MFE)],
|
||||
['an opener argument comes back', async () => armG({ ...CS, posterWithheld: (it, open) => CS.posterWithheld(it) }, lists(files[wall]), MFE)],
|
||||
['the preview read without DEV (a global opens production)', async () => armG({ ...CS, previewUnreleased: () => globalThis.DXM_PREVIEW_UNRELEASED === true }, lists(files[wall]), MFE)],
|
||||
['India\'s rows un-withheld', async () => armG({ ...CS, rowsWithheld: () => false }, lists(files[wall]), MFE)],
|
||||
['the India tab shown without the preview flag', async () => armG(CS, lists(files[wall]), MFE.replace('...(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true ? [INDIA_PANE] : []),', 'INDIA_PANE,'))],
|
||||
['the wall prints the list of a country whose rows are withheld', async () => armG(CS, lists(files[wall].replace('rowsWithheld(c) ? withheld() : ', '')))],
|
||||
['the library type prints withheld rows', () => armH(TYPE.replace('rowsWithheld(c) ? ', 'false ? '))],
|
||||
['the library type shows a withheld poster', () => armH(TYPE.replace('posterWithheld(c) ||', 'false ||'))],
|
||||
['a helper that mutates the shared ADM1', () => armC({ ...H, stateGeometry: (z, a, n) => { (a.features || []).forEach((f) => { if (f.properties.shapeName === n) f.properties.listId = z.id }); return H.stateGeometry(z, a, n) } }, states, JSON.parse(JSON.stringify(adm1Ind)))],
|
||||
]
|
||||
let bad = 0
|
||||
if (!regIsCaller) console.log('n/a the Regions list resolves [] for a phone-blocked card (the list delegates to <dxm-poster-section>; its withholding is arm H, the library type)')
|
||||
for (const [name, run] of cases) {
|
||||
const f = await run()
|
||||
console.log(`${f.length ? '✓ caught' : '✗ MISSED'} ${name}${f.length ? ` -> ${f[0]}` : ''}`)
|
||||
|
|
@ -281,8 +321,8 @@ async function main() {
|
|||
|
||||
const callers = []
|
||||
const E = armE(countries, states, files['plugin-designs/country-posters/country-poster-wall.vue'], readFileSync(join(ROOT, 'library/components/poster/PosterWall.vue'), 'utf8'), H)
|
||||
const G = await armG(CS, { wall: files['plugin-designs/country-posters/country-poster-wall.vue'], regions: REG })
|
||||
const fails = [...armA(files, callers), ...armB(seriesFor, countries, sheetFormatFor), ...armC(H, states, adm1Ind), ...G, ...E.fails, ...armD(states, areas, meta), ...armF({ wall: files['plugin-designs/country-posters/country-poster-wall.vue'], regions: REG })]
|
||||
const G = [...await armG(CS, lists(files['plugin-designs/country-posters/country-poster-wall.vue']), MFE), ...armH(TYPE)]
|
||||
const fails = [...armA(files, callers), ...armB(seriesFor, countries, sheetFormatFor), ...armC(H, states, adm1Ind), ...G, ...E.fails, ...armD(states, areas, meta), ...armF(lists(files['plugin-designs/country-posters/country-poster-wall.vue']))]
|
||||
const missing = [...KNOWN_CALLERS, `${STATE_CALLER}#state`].filter((k) => !callers.includes(k))
|
||||
if (missing.length) fails.push(`known callers not found calling makeCountrySheet: ${missing.join(', ')}`)
|
||||
if (fails.length) { fails.forEach((f) => console.log(`FAIL ${f}`)); process.exitCode = 1; return }
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
// A. the TYPE REGISTRY: 'country' is registered, an unknown type is null, a plugin can register its own.
|
||||
// B. SCOPES: DX Life's /regions slugs (africa … oceania) each resolve; every spelling of a region (its
|
||||
// slug, its API value, its English name) resolves to the same countries; the scopes cover all countries once.
|
||||
// C. a WITHHELD whole-country poster stays withheld through the type (the card's sentence; its rows rejected,
|
||||
// no records fetched).
|
||||
// C. India through the type, by default: its whole-country POSTER card shows the withheld sentence; its ROWS are
|
||||
// rejected with it, no records fetched.
|
||||
// D. the ELEMENT's attributes map to its config (collapsible on by default, collapsible="false", collapsed).
|
||||
// E. THE LIFT: every name PosterSection.vue's template uses is injected from the wall (a section never reads
|
||||
// something its wall does not provide).
|
||||
|
|
@ -18,7 +18,7 @@ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..')
|
|||
// real region), so the /regions/other scope resolves to zero countries and is no longer one of the covering scopes.
|
||||
const DXL = ['africa', 'asia', 'europe', 'latin-america-and-caribbean', 'middle-east', 'north-america-and-non-spanish-caribbean', 'oceania']
|
||||
|
||||
async function arms({ types, country, element, sectionSrc }) {
|
||||
async function arms({ types, country, element, sectionSrc, viewSrc, cs }) {
|
||||
const fails = []
|
||||
// A
|
||||
if (!types.posterTypeNames().includes('country')) fails.push("A: the registry has no 'country' type")
|
||||
|
|
@ -45,18 +45,51 @@ async function arms({ types, country, element, sectionSrc }) {
|
|||
else {
|
||||
let fetched = 0
|
||||
const act = country.actions({ assetsBase: () => '', apiBase: () => { fetched++; return '' }, lang: () => 'en', profileConfig: null, t: (k) => k })
|
||||
if (act.unsupported(ind) !== 'messages.countryPosterTooMany') fails.push("C: withheld India's card does not show messages.countryPosterTooMany")
|
||||
if (act.unsupported(ind) !== 'messages.countryPosterTooMany') fails.push("C: India's whole-country poster card does not show the withheld sentence by default (it is not released)")
|
||||
let why = ''
|
||||
try { await act.records(ind); why = 'resolved' } catch (e) { why = e && (e.i18nKey || e.message) }
|
||||
if (!/countryPosterTooMany/.test(String(why))) fails.push(`C: withheld India's rows are not withheld (${why})`)
|
||||
if (fetched) fails.push('C: withheld India fetched records')
|
||||
}
|
||||
// C2. THE PREVIEW, under Vite (import.meta.env.DEV is true here, as on the pilot's dev server): only with the global
|
||||
// set does India's whole-country poster open, at 60x96; its rows stay withheld; without it, withheld.
|
||||
const was = globalThis.DXM_PREVIEW_UNRELEASED
|
||||
try {
|
||||
globalThis.DXM_PREVIEW_UNRELEASED = true
|
||||
const big = cs.largeSheet({ iso3: 'IND' })
|
||||
if (cs.posterWithheld({ iso3: 'IND' }) || !big || big.format !== '60x96') fails.push(`C2: in a DEV preview India's poster does not open at 60x96 (${big && big.format})`)
|
||||
if (!cs.rowsWithheld({ iso3: 'IND' })) fails.push("C2: the preview also released India's rows")
|
||||
delete globalThis.DXM_PREVIEW_UNRELEASED
|
||||
if (!cs.posterWithheld({ iso3: 'IND' })) fails.push('C2: without the preview global India is not withheld')
|
||||
} finally { if (was === undefined) delete globalThis.DXM_PREVIEW_UNRELEASED; else globalThis.DXM_PREVIEW_UNRELEASED = was }
|
||||
// D
|
||||
const fake = (attrs) => ({ getAttribute: (n) => (n in attrs ? attrs[n] : null), hasAttribute: (n) => n in attrs })
|
||||
const d0 = element.posterSectionConfig(fake({ scope: 'africa' }))
|
||||
if (!(d0.type === 'country' && d0.scope === 'africa' && d0.collapsible === true && d0.collapsed === false)) fails.push(`D: defaults wrong ${JSON.stringify(d0)}`)
|
||||
if (element.posterSectionConfig(fake({ collapsible: 'false' })).collapsible !== false) fails.push('D: collapsible="false" does not turn collapsing off')
|
||||
if (element.posterSectionConfig(fake({ collapsed: '' })).collapsed !== true) fails.push('D: collapsed does not start the section shut')
|
||||
// min-card-px: absent = no key (the wall's own default); an integer 80..600 is kept; anything else is dropped.
|
||||
if ('minCardPx' in d0) fails.push('D: no min-card-px still sets minCardPx')
|
||||
if (element.posterSectionConfig(fake({ 'min-card-px': '150' })).minCardPx !== 150) fails.push('D: min-card-px="150" is not kept')
|
||||
if ('minCardPx' in element.posterSectionConfig(fake({ 'min-card-px': '50' }))) fails.push('D: min-card-px="50" (below 80) is kept')
|
||||
// show-header: absent = no key (shown, every current host unchanged); "false" = showHeader false; any other value = shown.
|
||||
if ('showHeader' in d0) fails.push('D: no show-header still sets showHeader')
|
||||
if (element.posterSectionConfig(fake({ 'show-header': 'false' })).showHeader !== false) fails.push('D: show-header="false" does not hide the head')
|
||||
if ('showHeader' in element.posterSectionConfig(fake({ 'show-header': '' }))) fails.push('D: show-header="" hides the head')
|
||||
// card-action: absent or an unknown value = no key ("Show on map"); "dashboard" = cardAction 'dashboard'.
|
||||
if ('cardAction' in d0) fails.push('D: no card-action still sets cardAction')
|
||||
if (element.posterSectionConfig(fake({ 'card-action': 'dashboard' })).cardAction !== 'dashboard') fails.push('D: card-action="dashboard" is not kept')
|
||||
if ('cardAction' in element.posterSectionConfig(fake({ 'card-action': 'map' }))) fails.push('D: an unknown card-action is kept')
|
||||
// F: the view passes show-header to the wall, and a headless section can neither collapse nor start shut.
|
||||
const view = String(viewSrc || '').replace(/<!--[\s\S]*?-->/g, '').replace(/\/\/[^\n]*/g, '')
|
||||
if (!/:section-heads="showHeader"/.test(view)) fails.push('F: PosterSectionView does not pass showHeader to the wall (section-heads)')
|
||||
if (!/const collapsible\s*=\s*computed\([^\n]*showHeader/.test(view)) fails.push('F: a headless section can still collapse')
|
||||
if (!/const startCollapsed\s*=\s*computed\([^\n]*showHeader/.test(view)) fails.push('F: a headless section can still start shut')
|
||||
// F: card-action reaches the wall's headerAction seam, and the click leaves the element as a bubbling, composed,
|
||||
// cancelable dxm-poster-dashboard (the host's PosterSlot listens on an ancestor, outside the shadow root).
|
||||
if (!/:header-action="headerAction"/.test(view) || !/@header-action="onHeaderAction"/.test(view)) fails.push('F: card-action does not reach the wall (header-action)')
|
||||
if (!/DASHBOARD_EVENT\s*=\s*'dxm-poster-dashboard'/.test(view)) fails.push('F: the dashboard event is not named dxm-poster-dashboard')
|
||||
if (!/new CustomEvent\(DASHBOARD_EVENT,\s*\{\s*bubbles:\s*true,\s*composed:\s*true,\s*cancelable:\s*true/.test(view)) fails.push('F: dxm-poster-dashboard is not bubbling + composed + cancelable')
|
||||
// E
|
||||
const tpl = (sectionSrc.match(/<template>([\s\S]*)<\/template>\s*$/) || ['', ''])[1].replace(/<!--[\s\S]*?-->/g, '')
|
||||
const injected = new Set(((sectionSrc.match(/const \{([\s\S]*?)\} = inject\(POSTER_WALL_CTX\)/) || ['', ''])[1]).split(/[\s,]+/).filter(Boolean))
|
||||
|
|
@ -68,22 +101,6 @@ async function arms({ types, country, element, sectionSrc }) {
|
|||
const local = new Set(['sec', 'z', 'item', 'n', 's', 'v', 'el', 'true', 'false', 'null', 'String', 'Number', '$event', 'in', 'label', 'zIndex'])
|
||||
const missing = [...used].filter((x) => !local.has(x) && !injected.has(x))
|
||||
if (missing.length) fails.push(`E: PosterSection.vue uses names it does not inject: ${missing.join(', ')}`)
|
||||
// F: a phone cannot print a desktop-only large sheet THROUGH THE TYPE — the same rule as country-poster-wall.vue,
|
||||
// mirrored in countryPosterType so the delegating regions list and any <dxm-poster-section> block it too. The only
|
||||
// desktop-only large sheet is India's (withheld today), so the fixture is a NON-withheld India (indiaState => not
|
||||
// withheld => largeSheet.desktopOnly). POISON: drop the phone rule and records() resolves / unsupported() is null.
|
||||
{
|
||||
const savedNav = globalThis.navigator
|
||||
globalThis.navigator = { userAgent: 'iPhone', maxTouchPoints: 0 }
|
||||
try {
|
||||
const act = country.actions({ assetsBase: () => '', apiBase: () => '', lang: () => 'en', profileConfig: null, t: (k) => k })
|
||||
const bigIndia = { id: 'country-ind-x', iso3: 'IND', indiaState: true, label: 'India', count: 855 }
|
||||
if (act.unsupported(bigIndia) !== 'poster.phoneLimitAll') fails.push(`F: on a phone, unsupported() of a desktop-only large sheet should read poster.phoneLimitAll, got ${JSON.stringify(act.unsupported(bigIndia))}`)
|
||||
let why = ''
|
||||
try { await act.records(bigIndia); why = 'resolved' } catch (e) { why = e && (e.i18nKey || e.message) }
|
||||
if (why !== 'poster.phoneLimitAll') fails.push(`F: on a phone, records() of a desktop-only large sheet should reject poster.phoneLimitAll, got ${JSON.stringify(why)}`)
|
||||
} finally { if (savedNav === undefined) delete globalThis.navigator; else globalThis.navigator = savedNav }
|
||||
}
|
||||
return fails
|
||||
}
|
||||
|
||||
|
|
@ -96,12 +113,20 @@ try {
|
|||
const country = (await load('library/components/poster/types/countryPosterType.js')).default
|
||||
const element = await load('library/elements/posterSectionConfig.js')
|
||||
const sectionSrc = readFileSync(join(ROOT, 'library/components/poster/PosterSection.vue'), 'utf8')
|
||||
const real = { types, country, element, sectionSrc }
|
||||
const viewSrc = readFileSync(join(ROOT, 'library/components/poster/PosterSectionView.vue'), 'utf8')
|
||||
const cs = await load('library/packet/countrySheet.js')
|
||||
const real = { types, country, element, sectionSrc, viewSrc, cs }
|
||||
if (process.argv.includes('--self-test')) {
|
||||
const poisons = [
|
||||
["a registry without 'country'", { ...real, types: { ...types, posterTypeNames: () => [] } }, /^A:/],
|
||||
['a region key without "&" → "and"', { ...real, country: { ...country, items: (s, l) => (/-and-/.test(String(s)) ? [] : country.items(s, l)) } }, /^B:/],
|
||||
['the preview ignored under DEV', { ...real, cs: { ...cs, posterWithheld: () => true, largeSheet: () => null } }, /^C2:/],
|
||||
['a section that uses a name it does not inject', { ...real, sectionSrc: sectionSrc.replace('</template>', '{{ notProvided }}</template>') }, /^E:/],
|
||||
['an element config that drops show-header', { ...real, element: { posterSectionConfig: (el) => { const c = element.posterSectionConfig(el); delete c.showHeader; return c } } }, /^D: show-header="false"/],
|
||||
['a view that does not pass show-header to the wall', { ...real, viewSrc: viewSrc.replace(':section-heads="showHeader"', '') }, /^F:/],
|
||||
['an element config that ignores card-action', { ...real, element: { posterSectionConfig: (el) => { const c = element.posterSectionConfig(el); delete c.cardAction; return c } } }, /^D: card-action="dashboard"/],
|
||||
['a dashboard event that cannot leave the shadow root', { ...real, viewSrc: viewSrc.replace('composed: true, cancelable', 'composed: false, cancelable') }, /^F: dxm-poster-dashboard is not/],
|
||||
['a headless section that can still start shut', { ...real, viewSrc: viewSrc.replace(/(const startCollapsed = computed\(\(\) => cfg\.value\.collapsed === true)[^\n]*/, '$1)') }, /^F: a headless section can still start shut/],
|
||||
]
|
||||
for (const [name, inp, rx] of poisons) {
|
||||
const f = await arms(inp); const ok = f.some((x) => rx.test(x))
|
||||
|
|
@ -111,7 +136,7 @@ try {
|
|||
} else {
|
||||
const f = await arms(real)
|
||||
if (f.length) { f.forEach((x) => console.log('FAIL ' + x)); code = 1 }
|
||||
else console.log(`PASS check:poster-section — the 'country' type; ${DXL.length} DX Life scopes cover every country once (and their other spellings); withheld India stays withheld; a phone blocks a desktop-only large sheet (poster.phoneLimitAll); the element's attributes; PosterSection injects every name it uses`)
|
||||
else console.log(`PASS check:poster-section — the 'country' type; ${DXL.length} DX Life scopes cover every country once (and their other spellings); India withheld by default; the element's attributes; PosterSection injects every name it uses`)
|
||||
}
|
||||
} finally { await server.close() }
|
||||
process.exit(code)
|
||||
|
|
|
|||
|
|
@ -377,7 +377,10 @@ const NOT_EXPERIMENTAL = new Set([
|
|||
'posterverse',
|
||||
// bible-posters lives on POSTERVERSE's Experimental shelf, and only there: one poster plugin, shown
|
||||
// once, in the shell that gathers the poster plugins.
|
||||
'bible-posters'
|
||||
'bible-posters',
|
||||
// poster-sections is a TEMPLATE plugin: the <dxm-poster-section> element other plugins and sites embed
|
||||
// (the Regions list, DX Life's region pages). It stays in Plugin Studio, not on a reader's shelf.
|
||||
'poster-sections'
|
||||
])
|
||||
|
||||
// ── EXPERIMENTAL ORDER = KINGDOM IMPACT, not the alphabet ────────────────────────────────
|
||||
|
|
@ -657,6 +660,26 @@ function assertNoDuplicateTabKeys(navModel) {
|
|||
return dupes
|
||||
}
|
||||
|
||||
// ★ FRESH BUNDLES AFTER A DEPLOY, AT THE SAME URLs (2026-10-02). A host that serves the tree with Last-Modified and no
|
||||
// Cache-Control (the DX Life landing does) lets a browser run its cached copies for hours: measured, a typed-URL revisit
|
||||
// after a deploy requested NONE of the gallery, the manifest or the bundles, so a returning reader ran the old files, or
|
||||
// an old manifest against a new bundle. A version query cannot fix it here: loadScriptOnce keys its registry by the full
|
||||
// href, so X.js?v= beside a composite's plain X.js would run the bundle twice. So the manifest is always revalidated, and
|
||||
// when its fingerprint (every bundle's integrity) differs from this reader's last visit to this tree, every listed bundle
|
||||
// gets ONE conditional request before any tab mounts: a 304 refreshes the cached copy, and a 200 is cancelled at its
|
||||
// headers, which drops the stale entry. The plain <script> that follows (ours, or a composite's for its children) then
|
||||
// runs the new file. An ordinary visit costs nothing extra.
|
||||
const TREE_FP_KEY = 'dxm.treeFingerprint@'
|
||||
function treeFingerprint(manifest) {
|
||||
const bundles = (manifest && Array.isArray(manifest.bundles)) ? manifest.bundles : []
|
||||
const s = bundles.map(b => (b && b.bundle) + ':' + ((b && b.integrity) || '')).join('|')
|
||||
let h = 2166136261 // FNV-1a: a short, stable key, not a security hash
|
||||
for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619) }
|
||||
return (h >>> 0).toString(36) + '.' + s.length
|
||||
}
|
||||
function readTreeFp(root) { try { return localStorage.getItem(TREE_FP_KEY + root) } catch { return null } }
|
||||
function writeTreeFp(root, fp) { try { localStorage.setItem(TREE_FP_KEY + root, fp) } catch { /* private mode: revalidate next time */ } }
|
||||
|
||||
// Turn a bundle id into a human label: "all-people-groups" → "All People Groups". Small, local and
|
||||
// dependency-free; a bundle may override it by shipping a profile `title` in the manifest.
|
||||
function titleize(id) {
|
||||
|
|
@ -1321,17 +1344,24 @@ export default defineComponent({
|
|||
const devFallback = '/public/' + __DOXA_TREE_NAME__ + '/manifest.json'
|
||||
if (!candidates.some(c => c.url === devFallback)) candidates.push({ url: devFallback, root: dirOf(devFallback) })
|
||||
const tried = []
|
||||
// Every mount waits for this gate (see FRESH BUNDLES above): it opens once the manifest's bundles are known
|
||||
// to be current, when no manifest can be read (fail-soft, as before), or after 8 s whatever happens.
|
||||
let open
|
||||
this._freshGate = new Promise(res => { open = res })
|
||||
setTimeout(open, 8000)
|
||||
const attempt = (i) => {
|
||||
if (i >= candidates.length) {
|
||||
// Every candidate failed. Record it on the tabs so the PANE says so — see mountTab().
|
||||
this.manifestError = 'manifest.json not found (tried ' + tried.join(', ') + ')'
|
||||
console.error('[doxa-maps-spa] ' + this.manifestError +
|
||||
' — every tab except Big Picture is composed from it, so their panes cannot mount.')
|
||||
open()
|
||||
return
|
||||
}
|
||||
const { url, root: treeRoot } = candidates[i]
|
||||
tried.push(url)
|
||||
return fetch(url, { credentials: 'same-origin' })
|
||||
// no-cache: always revalidated (a 304 when unchanged), so the manifest is never a stale copy.
|
||||
return fetch(url, { credentials: 'same-origin', cache: 'no-cache' })
|
||||
.then(r => { if (!r || !r.ok) throw new Error(url + ' → HTTP ' + (r ? r.status : '?')); return r.json() })
|
||||
.then(m => {
|
||||
if (!m) throw new Error(url + ' → empty')
|
||||
|
|
@ -1341,11 +1371,32 @@ export default defineComponent({
|
|||
// resolves '../' and rooted forms alike, so a caller-supplied treeBase, a built-tree
|
||||
// path and the dev fallback all normalise here.
|
||||
this.manifestBase = treeRoot || ''
|
||||
this.revalidateBundles(m, treeRoot || '').then(open, open)
|
||||
this.applyManifest(m)
|
||||
})
|
||||
.catch(() => attempt(i + 1))
|
||||
}
|
||||
try { attempt(0) } catch { /* fetch unavailable (non-browser): same fail-soft path */ }
|
||||
try { attempt(0) } catch { open() /* fetch unavailable (non-browser): same fail-soft path */ }
|
||||
},
|
||||
// After a deploy (the manifest's fingerprint changed since this reader's last visit to this tree), ONE conditional
|
||||
// request per listed bundle, so no stale cached copy is run (see FRESH BUNDLES). Never rejects.
|
||||
revalidateBundles(m, root) {
|
||||
const fp = treeFingerprint(m)
|
||||
const prev = readTreeFp(root)
|
||||
if (prev === fp) return Promise.resolve()
|
||||
const urls = [...new Set(((m && m.bundles) || []).map(b => (b && b.scriptUrl) ? this.resolve(b.scriptUrl) : '').filter(Boolean))]
|
||||
// Only a copy this browser already HOLDS can be stale, so the cache is asked first (only-if-cached never touches
|
||||
// the network) and only those get the conditional request: a first visit with an empty cache costs nothing.
|
||||
// only-if-cached needs a same-origin request; across origins, only a reader who has been here before is checked.
|
||||
const sameOrigin = (u) => { try { return new URL(u, location.href).origin === location.origin } catch { return false } }
|
||||
const held = (u) => (sameOrigin(u)
|
||||
? fetch(u, { mode: 'same-origin', credentials: 'same-origin', cache: 'only-if-cached' })
|
||||
.then(r => { if (r && r.body && r.body.cancel) r.body.cancel(); return !!(r && r.ok) }, () => false)
|
||||
: Promise.resolve(prev !== null))
|
||||
const revalidate = (u) => fetch(u, { credentials: 'same-origin', cache: 'no-cache' })
|
||||
.then(r => { if (r && r.status === 200 && r.body && r.body.cancel) return r.body.cancel() })
|
||||
const one = (u) => held(u).then(h => (h ? revalidate(u) : null)).catch(() => {})
|
||||
return Promise.all(urls.map(one)).then(() => writeTreeFp(root, fp))
|
||||
},
|
||||
// Complete the curated tabs from the manifest, drop any whose bundle this tree does not ship,
|
||||
// then append the discovered Experimental tabs — and finally (re)drive the active tab's mount,
|
||||
|
|
@ -1589,7 +1640,8 @@ export default defineComponent({
|
|||
// Gate the mount on the TAG being registered, NOT on the script's onload — onload fires even
|
||||
// when a dev ES module then fails to execute (see loadScriptOnce), which would hand us an inert
|
||||
// HTMLUnknownElement and an empty pane cheerfully reporting 'ready'.
|
||||
loadScriptOnce(jsUrl).then(() => whenTagDefined(t.tag)).then(() => {
|
||||
// …and on the freshness gate first, so a deploy's new bundle is the copy that runs (see FRESH BUNDLES).
|
||||
Promise.resolve(this._freshGate).then(() => loadScriptOnce(jsUrl)).then(() => whenTagDefined(t.tag)).then(() => {
|
||||
// Everything below is per-nested-plugin machinery; a throw here (a nested element that blows
|
||||
// up in its connectedCallback, a serialise failure) must NOT crash the SPA — it lands in THIS
|
||||
// tab's error pane. Hence the try/catch on top of the .catch() that handles a failed load.
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@
|
|||
* things through its host seams. headerAction turns the card's "Show on map" sticker into "Show full
|
||||
* details" (there is no map here), and previewSrc draws each thumbnail from the library at view time
|
||||
* instead of reading a stored image, so a change to a template shows up here the next time it opens.
|
||||
* The catalogue itself is src/templates.js.
|
||||
* The catalogue itself is src/templates.js. Below it, every poster design OTHER plugins declare in their own meta
|
||||
* ("posterDesign", carried into manifest.json; src/pluginDesigns.js) gets a card in its own group: it is made by its
|
||||
* own plugin, so the card says so and "Show full details" shows that plugin's page live.
|
||||
*/
|
||||
import { computed, inject, ref } from 'vue'
|
||||
import { computed, inject, onMounted, ref } from 'vue'
|
||||
import PosterWall from '@map/components/poster/PosterWall.vue'
|
||||
import { useOptionalT } from '@map/composables/useOptionalT.js'
|
||||
import { resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { absoluteEmbedUrl, resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
|
||||
import { getApiBaseUrl } from '@map/api/apiBaseUrl.js'
|
||||
|
|
@ -29,6 +31,7 @@ import { panelsSvgUrl } from './src/panelsSvg.js'
|
|||
import { resolvePosterSize } from '@map/constants/posterSizes.js'
|
||||
import { listConfigurations, FAMILY_KEYS, SAMPLE_COUNTRY_ID } from './src/templates.js'
|
||||
import FullDetailsDialog from './src/FullDetailsDialog.vue'
|
||||
import { loadPluginDesigns } from './src/pluginDesigns.js'
|
||||
|
||||
const profileConfig = inject('profileConfig', ref(null))
|
||||
const instanceId = computed(() => profileConfig.value?.instanceId || 'poster-design-styles')
|
||||
|
|
@ -48,16 +51,35 @@ function stockLabel(s) {
|
|||
}
|
||||
const labelOf = (c) => (c.stock ? `${t(c.labelKey)}: ${stockLabel(c.stock)}` : `${t(c.labelKey)}: ${c.size}`)
|
||||
|
||||
// Designs other plugins declare (their meta's posterDesign, via manifest.json): the built tree's manifest, or the dev
|
||||
// server's. A name already in templates.js stays the library's (the check refuses the clash; the page never doubles it).
|
||||
const pluginDesigns = ref([])
|
||||
const byPluginName = computed(() => new Map(pluginDesigns.value.map((d) => [d.name, d])))
|
||||
onMounted(async () => {
|
||||
const urls = [absoluteEmbedUrl('manifest.json', profileConfig.value), import.meta.env.DEV ? '/__plugin-gallery/manifest.json' : '']
|
||||
const { designs } = await loadPluginDesigns(urls)
|
||||
pluginDesigns.value = designs.filter((d) => !byName.has(d.name))
|
||||
})
|
||||
const pluginLabel = (d) => {
|
||||
const v = d.labelKey ? t(d.labelKey) : ''
|
||||
return `${v && v !== d.labelKey ? v : d.label}: ${d.size}`
|
||||
}
|
||||
|
||||
// No `count`: these are templates, not lists of people, so the sticker reads "Show full details" alone.
|
||||
const items = computed(() => configs.map((c) => ({ id: c.name, label: labelOf(c) })))
|
||||
const groupBy = (item) => t(FAMILY_KEYS[byName.get(item.id)?.family] || FAMILY_KEYS.mapSheet)
|
||||
const items = computed(() => [
|
||||
...configs.map((c) => ({ id: c.name, label: labelOf(c) })),
|
||||
...pluginDesigns.value.map((d) => ({ id: d.name, label: pluginLabel(d) })),
|
||||
])
|
||||
const groupBy = (item) => (byPluginName.value.has(item.id) ? t('posterDesign.family.plugins') : t(FAMILY_KEYS[byName.get(item.id)?.family] || FAMILY_KEYS.mapSheet))
|
||||
|
||||
// ── drawing a configuration from the library ──────────────────────────────────────────────────
|
||||
const countryById = (id) => (countryData.countries || []).find((c) => c.id === id)
|
||||
// A design whose sample country is WITHHELD (countrySheet.js: the whole-India poster, for now) shows this sentence on its
|
||||
// card and is never drawn or printed: PosterWall answers `unsupported` before it draws a preview or starts a print. The
|
||||
// designs stay in the catalogue, so they reappear the day the country is no longer withheld.
|
||||
// A design made by another plugin is not drawn here either: its card says so, and its details show its page live.
|
||||
const withheld = (item) => {
|
||||
if (byPluginName.value.has(item.id)) return t('posterDesign.fromPlugin')
|
||||
const c = byName.get(item.id)
|
||||
const sample = c && c.sample ? countryById(c.sample) : null
|
||||
return sample && sheetWithheld(sample) ? t('messages.countryPosterTooMany') : null
|
||||
|
|
@ -153,8 +175,16 @@ async function sheet(item) {
|
|||
|
||||
// ── "Show full details" ───────────────────────────────────────────────────────────────────────
|
||||
const open = ref(null)
|
||||
const openLabel = computed(() => (open.value ? labelOf(open.value) : ''))
|
||||
function showDetails(item) { open.value = byName.get(item.id) || null }
|
||||
const openLabel = computed(() => (!open.value ? '' : open.value.design ? pluginLabel(open.value.design) : labelOf(open.value)))
|
||||
// A plugin's design opens in the same dialog, as a configuration: its renderer is the plugin's page, and the dialog
|
||||
// shows that page live.
|
||||
function showDetails(item) {
|
||||
const d = byPluginName.value.get(item.id)
|
||||
open.value = d
|
||||
? { name: d.name, size: d.size, settings: { family: d.family, mobileFriendly: d.mobileFriendly, ...(d.instance ? { preset: d.instance } : {}) },
|
||||
renderer: `plugin-designs/${d.bundle}/${d.profile}.vue`, pageUrl: d.pageUrl, design: d }
|
||||
: byName.get(item.id) || null
|
||||
}
|
||||
|
||||
const heading = computed(() => t('posterDesign.styles.heading'))
|
||||
// PosterWall renders the intro as HTML, so plain catalogue text is escaped first.
|
||||
|
|
@ -181,6 +211,9 @@ useShadowStyles(`
|
|||
.pd-dialog__note{margin:14px 0 0;color:#6d766e}
|
||||
.pd-dialog__agent{margin:14px 0 0;padding:10px 12px;border-radius:8px;background:#f3f3f1;border-inline-start:4px solid #92b195}
|
||||
.pd-dialog__actions{display:flex;justify-content:flex-end;margin:16px 0 0}
|
||||
.pd-dialog__live{margin:14px 0 0}
|
||||
.pd-dialog__frame{display:block;width:100%;height:min(60vh,520px);border:1px solid #d1d4cd;border-radius:8px;background:#f3f3f1}
|
||||
.pd-dialog__open{display:inline-block;margin-top:8px;color:#3b463d;font-weight:600}
|
||||
.pd-dialog__close{margin:0;background:#92b195;border-color:#92b195;color:#fff;font-weight:600;padding:6px 16px}
|
||||
@media (max-width:480px){.pd-dialog__list{grid-template-columns:1fr}.pd-dialog__list dd{margin-bottom:6px}}
|
||||
`, 'poster-design-styles')
|
||||
|
|
|
|||
|
|
@ -11,10 +11,21 @@
|
|||
* 3. The card entries are exactly card-render.js STOCK, one per stock.
|
||||
* 4. Every catalogue key an entry names (labelKey, missing, the family keys) exists in the English
|
||||
* posterDesign catalogue.
|
||||
* 5. The sample country exists in the country data and can be drawn (has geometry, a sheet tier).
|
||||
* A catalogue that lists nothing FAILS (vacuous guard).
|
||||
* 5. Every sample country (the default and each format's own) exists in the country data and can be drawn (has
|
||||
* geometry, a sheet tier), unless the shared path WITHHOLDS it (countrySheet.js sheetWithheld): that card shows
|
||||
* the sentence instead of a drawing, so it is never drawn here.
|
||||
* 6. PLUGIN DESIGNS: every "posterDesign" block in plugin-designs/<plugin>/<profile>.meta.json is valid
|
||||
* (src/pluginDesigns.js blockProblems: family, name, size, mobileFriendly, optional labelKey / instance), its
|
||||
* labelKey exists in the English catalogue, and its instance is a preset of that profile.
|
||||
* 7. Names are unique across templates.js AND every plugin's blocks.
|
||||
* 8. THE NAMES LEDGER (src/catalogue-names.json): every name ever shipped. A ledger name that no longer exists
|
||||
* (renamed or removed) FAILS: a name is what people hand an agent, so it must keep working (a retired size stays
|
||||
* as a legacy entry, as templates.js does). A new name passes and is reported, to be added to the ledger.
|
||||
* A catalogue that lists nothing FAILS (vacuous guard). --self-test: an invalid block, a name clash, a renamed
|
||||
* ledger name, a block naming a missing preset, and a sample that exists but cannot be drawn (India with withholding
|
||||
* off) must each FAIL, and the documented example block must PASS.
|
||||
*/
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { readFileSync, readdirSync, existsSync } from 'node:fs'
|
||||
import { resolve, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
|
|
@ -28,9 +39,11 @@ const server = await createServer({
|
|||
server: { middlewareMode: true, hmr: false, ws: false, watch: null },
|
||||
resolve: { alias: { '@map': join(ROOT, 'library') } },
|
||||
})
|
||||
let cat, STOCK
|
||||
let cat, STOCK, PD, sheetWithheld
|
||||
try {
|
||||
;({ sheetWithheld } = await server.ssrLoadModule('/library/packet/countrySheet.js'))
|
||||
cat = await server.ssrLoadModule('/plugin-designs/poster-design/src/templates.js')
|
||||
PD = await server.ssrLoadModule('/plugin-designs/poster-design/src/pluginDesigns.js')
|
||||
;({ STOCK } = await server.ssrLoadModule('/library/packet/card-render.js'))
|
||||
} finally { await server.close() }
|
||||
|
||||
|
|
@ -55,11 +68,8 @@ else {
|
|||
}
|
||||
|
||||
// 2. names
|
||||
const seen = new Set()
|
||||
for (const c of configs) {
|
||||
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/i.test(c.name)) fail(`'${c.name}' is not a configuration name (words joined by . or -)`)
|
||||
if (seen.has(c.name)) fail(`two configurations are named '${c.name}'`)
|
||||
seen.add(c.name)
|
||||
}
|
||||
|
||||
// 3. stocks
|
||||
|
|
@ -69,6 +79,15 @@ if (JSON.stringify(stockNames) !== JSON.stringify(want)) fail(`card configuratio
|
|||
|
||||
// 4. keys exist in English
|
||||
const en = JSON.parse(readFileSync(join(ROOT, 'library/i18n/locales/en/posterDesign.json'), 'utf8'))
|
||||
// any catalogue key, in any area: "<area>.<path>" in library/i18n/locales/en/<area>.json
|
||||
const enKey = (key) => {
|
||||
const [area, ...parts] = String(key).split('.')
|
||||
const file = join(ROOT, `library/i18n/locales/en/${area}.json`)
|
||||
if (!parts.length || !existsSync(file)) return false
|
||||
let o = JSON.parse(readFileSync(file, 'utf8'))
|
||||
for (const p of parts) { if (!o || typeof o !== 'object' || !(p in o)) return false; o = o[p] }
|
||||
return typeof o === 'string'
|
||||
}
|
||||
const has = (key) => {
|
||||
const parts = String(key).split('.')
|
||||
if (parts.shift() !== 'posterDesign') return false
|
||||
|
|
@ -80,15 +99,93 @@ const keys = [...configs.flatMap((c) => [c.labelKey, c.missing].filter(Boolean))
|
|||
for (const k of keys) if (!has(k)) fail(`catalogue key '${k}' is not in en/posterDesign.json`)
|
||||
for (const c of configs) if (!cat.FAMILY_KEYS[c.family]) fail(`'${c.name}' has family '${c.family}', which has no heading key`)
|
||||
|
||||
// 5. the sample
|
||||
// 5. the samples
|
||||
const data = JSON.parse(readFileSync(join(ROOT, 'library/data/country-posters.json'), 'utf8'))
|
||||
const s = (data.countries || []).find((c) => c.id === cat.SAMPLE_COUNTRY_ID)
|
||||
if (!s) fail(`sample '${cat.SAMPLE_COUNTRY_ID}' is not in library/data/country-posters.json`)
|
||||
else if (!s.hasGeometry || !s.sheet || s.unsupportedReason) fail(`sample '${s.id}' cannot be drawn (hasGeometry ${s.hasGeometry}, sheet ${s.sheet})`)
|
||||
const samples = [...new Set([cat.SAMPLE_COUNTRY_ID, ...configs.map((c) => c.sample).filter(Boolean)])]
|
||||
function sampleArm(ids, countries, withheld) {
|
||||
const fs = []
|
||||
for (const id of ids) {
|
||||
const s = countries.find((c) => c.id === id)
|
||||
if (!s) fs.push(`5: sample '${id}' is not in library/data/country-posters.json`)
|
||||
else if (withheld(s)) continue // its card shows the withheld sentence; it is never drawn here
|
||||
else if (!s.hasGeometry || !s.sheet || s.unsupportedReason) fs.push(`5: sample '${s.id}' cannot be drawn (hasGeometry ${s.hasGeometry}, sheet ${s.sheet})`)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
sampleArm(samples, data.countries || [], sheetWithheld).forEach(fail)
|
||||
|
||||
// 6-8: plugin designs, unique names, the ledger. Gathered once, judged by one function, so --self-test poisons the
|
||||
// same function the real run uses.
|
||||
function gatherBlocks() {
|
||||
const out = []
|
||||
const base = join(ROOT, 'plugin-designs')
|
||||
for (const plugin of readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name)) {
|
||||
for (const f of readdirSync(join(base, plugin)).filter((x) => x.endsWith('.meta.json'))) {
|
||||
let meta
|
||||
try { meta = JSON.parse(readFileSync(join(base, plugin, f), 'utf8')) } catch { continue }
|
||||
if (meta.posterDesign === undefined) continue
|
||||
const profile = f.replace(/\.meta\.json$/, '')
|
||||
const inst = readdirSync(join(base, plugin)).find((x) => x.endsWith('.instances.json'))
|
||||
const presets = inst ? ((JSON.parse(readFileSync(join(base, plugin, inst), 'utf8')).instances) || []).map((i) => i.id) : []
|
||||
const blocks = PD.blocksOf(meta.posterDesign)
|
||||
if (!blocks.length) out.push({ where: `${plugin}/${f}`, block: meta.posterDesign, presets })
|
||||
for (const block of blocks) out.push({ where: `${plugin}/${profile}.meta.json`, block, presets })
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
function designArms({ names, blocks, ledger }) {
|
||||
const fs = []
|
||||
for (const { where, block, presets } of blocks) {
|
||||
const probs = PD.blockProblems(block)
|
||||
for (const p of probs) fs.push(`6: ${where} posterDesign ${p}`)
|
||||
if (probs.length) continue
|
||||
if (block.labelKey && !enKey(block.labelKey)) fs.push(`6: ${where} labelKey '${block.labelKey}' is not in the English catalogue`)
|
||||
if (block.instance && !presets.includes(block.instance)) fs.push(`6: ${where} instance '${block.instance}' is not one of its presets`)
|
||||
}
|
||||
const all = [...names, ...blocks.filter((b) => !PD.blockProblems(b.block).length).map((b) => b.block.name)]
|
||||
const seen = new Set()
|
||||
for (const n of all) { if (seen.has(n)) fs.push(`7: two designs are named '${n}'`); seen.add(n) }
|
||||
for (const n of ledger) if (!seen.has(n)) fs.push(`8: '${n}' is in the names ledger and no longer exists: a shipped name is never renamed or removed (keep it, as a legacy entry if retired)`)
|
||||
return { fails: fs, fresh: [...seen].filter((n) => !ledger.includes(n)) }
|
||||
}
|
||||
const LEDGER_FILE = join(ROOT, 'plugin-designs/poster-design/src/catalogue-names.json')
|
||||
const ledger = existsSync(LEDGER_FILE) ? JSON.parse(readFileSync(LEDGER_FILE, 'utf8')).names || [] : []
|
||||
if (!ledger.length) fail('8: the names ledger (src/catalogue-names.json) is missing or empty')
|
||||
const blocks = gatherBlocks()
|
||||
const real = { names: configs.map((c) => c.name), blocks, ledger }
|
||||
|
||||
if (process.argv.includes('--self-test')) {
|
||||
const example = { family: 'regionPoster', name: 'region-poster.66x66', size: '66x66', mobileFriendly: true }
|
||||
const B = (block, presets = []) => ({ where: 'fixture/fixture.meta.json', block, presets })
|
||||
const cases = [
|
||||
['an invalid block (mobileFriendly missing)', { ...real, blocks: [...blocks, B({ family: 'regionPoster', name: 'region-poster.x', size: '66x66' })] }, /^6: .*mobileFriendly/],
|
||||
['a name clash with templates.js', { ...real, blocks: [...blocks, B({ ...example, name: real.names[0] })] }, /^7: /],
|
||||
['a renamed ledger name', { ...real, names: real.names.map((n, i) => (i === 0 ? `${n}-renamed` : n)) }, /^8: /],
|
||||
['a block naming a preset that does not exist', { ...real, blocks: [...blocks, B({ ...example, instance: 'nowhere' }, ['africa'])] }, /^6: .*instance/],
|
||||
]
|
||||
let ok = true
|
||||
for (const [name, input, rx] of cases) {
|
||||
const { fails } = designArms(input); const hit = fails.find((f) => rx.test(f))
|
||||
console.log(hit ? ` ✓ caught: ${name} -> ${hit}` : ` ✗ MISSED: ${name}`)
|
||||
if (!hit) ok = false
|
||||
}
|
||||
// a sample that exists but cannot be drawn: India, once nothing withholds it
|
||||
const und = sampleArm(samples, data.countries || [], () => false).find((f) => /cannot be drawn/.test(f))
|
||||
console.log(und ? ` ✓ caught: a sample that exists but cannot be drawn (withholding off) -> ${und}` : ' ✗ MISSED: a sample that exists but cannot be drawn')
|
||||
if (!und) ok = false
|
||||
const good = designArms({ ...real, blocks: [...blocks, B(example)] })
|
||||
console.log(good.fails.length ? ` ✗ the documented example block FAILS: ${good.fails[0]}` : ' ✓ the documented example block passes')
|
||||
if (good.fails.length) ok = false
|
||||
console.log(ok ? 'POSTER-DESIGN-CATALOGUE SELF-TEST: PASS' : 'POSTER-DESIGN-CATALOGUE SELF-TEST: FAIL')
|
||||
process.exit(ok ? 0 : 1)
|
||||
}
|
||||
const judged = designArms(real)
|
||||
judged.fails.forEach(fail)
|
||||
|
||||
if (failures.length) {
|
||||
console.error(`POSTER-DESIGN-CATALOGUE: FAIL (${failures.length})\n - ${failures.join('\n - ')}`)
|
||||
process.exitCode = 1
|
||||
} else {
|
||||
console.log(`POSTER-DESIGN-CATALOGUE: PASS — ${configs.length} configurations, ${Object.keys(cat.SHEET_FORMATS).length} sheet formats matching poster-render FORMATS, ${want.length} card stocks from STOCK, ${keys.length} catalogue keys present, sample ${cat.SAMPLE_COUNTRY_ID}`)
|
||||
console.log(`POSTER-DESIGN-CATALOGUE: PASS — ${configs.length} configurations, ${Object.keys(cat.SHEET_FORMATS).length} sheet formats matching poster-render FORMATS, ${want.length} card stocks from STOCK, ${keys.length} catalogue keys present, samples ${samples.join(', ')} drawable or withheld; ${blocks.length} plugin design block(s) valid; ${ledger.length} ledger names all present${judged.fresh.length ? `; NEW (add to src/catalogue-names.json): ${judged.fresh.join(', ')}` : ''}`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
*
|
||||
* Everything a person needs to name the template to an agent, or an agent needs to find it: the
|
||||
* configuration name (the stable id), what it is, its size, the size of the file it prints, where its
|
||||
* renderer lives, its settings and its sample data.
|
||||
* renderer lives, its settings and its sample data. A design another plugin makes (config.pageUrl) shows that
|
||||
* plugin's page live instead of a file size, with a link to open it.
|
||||
*
|
||||
* A NATIVE <dialog> opened with showModal(), so it sits in the browser's TOP LAYER. A fixed scrim inside
|
||||
* this element would be captured by a host's containing block (VerticalTabsLayout sets contain:layout on
|
||||
|
|
@ -63,8 +64,10 @@ async function copyName() {
|
|||
<button type="button" class="pd-dialog__copy" @click="copyName">{{ t('posterDesign.details.copyName') }}</button></dd>
|
||||
<dt>{{ t('posterDesign.details.size') }}</dt>
|
||||
<dd dir="ltr">{{ config.size }}<span v-if="config.px"> · {{ config.px[0] }} × {{ config.px[1] }} px</span></dd>
|
||||
<template v-if="!config.pageUrl">
|
||||
<dt>{{ t('posterDesign.details.fileSize') }}</dt>
|
||||
<dd>{{ fileSize }}</dd>
|
||||
</template>
|
||||
<dt>{{ t('posterDesign.details.settings') }}</dt>
|
||||
<dd dir="ltr">{{ settings }}</dd>
|
||||
<dt>{{ t('posterDesign.details.sample') }}</dt>
|
||||
|
|
@ -72,6 +75,10 @@ async function copyName() {
|
|||
<dt>{{ t('posterDesign.details.renderer') }}</dt>
|
||||
<dd dir="ltr"><code>{{ config.renderer }}</code></dd>
|
||||
</dl>
|
||||
<div v-if="config.pageUrl" class="pd-dialog__live">
|
||||
<iframe class="pd-dialog__frame" :src="config.pageUrl" :title="label" loading="lazy"></iframe>
|
||||
<a class="pd-dialog__open" :href="config.pageUrl" target="_blank" rel="noopener">{{ t('posterDesign.details.openPage') }}</a>
|
||||
</div>
|
||||
<p v-if="config.legacy" class="pd-dialog__note">{{ t('posterDesign.details.legacy') }}</p>
|
||||
<p v-if="config.missing" class="pd-dialog__note">{{ t(config.missing) }}</p>
|
||||
<p class="pd-dialog__agent">{{ agentLine }}</p>
|
||||
|
|
|
|||
21
plugin-designs/poster-design/src/catalogue-names.json
Normal file
21
plugin-designs/poster-design/src/catalogue-names.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"//": "Every poster design name ever shipped: templates.js and every plugin's posterDesign block. check:poster-design-catalogue fails when a name here no longer exists, because a name is what people hand an agent (a retired design stays as a legacy entry). Append new names; never edit or remove one.",
|
||||
"names": [
|
||||
"map-sheet.country.3x3",
|
||||
"map-sheet.country.60x96",
|
||||
"map-sheet.country.66x66",
|
||||
"map-sheet.country.36x60",
|
||||
"map-sheet.country.3x4",
|
||||
"map-sheet.country.letter",
|
||||
"map-sheet.packet-page",
|
||||
"packet.full",
|
||||
"packet.lightweight",
|
||||
"cards.poker",
|
||||
"cards.pokerLandscape",
|
||||
"cards.postcard",
|
||||
"cards.postcardLandscape",
|
||||
"cards.a6",
|
||||
"wall.bless-panels",
|
||||
"map-print.dialog"
|
||||
]
|
||||
}
|
||||
76
plugin-designs/poster-design/src/pluginDesigns.js
Normal file
76
plugin-designs/poster-design/src/pluginDesigns.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* pluginDesigns.js — poster designs that OTHER plugins declare, read from the tree's manifest.json.
|
||||
*
|
||||
* A poster plugin lists its designs in its OWN <profile>.meta.json, under "posterDesign" (one object, or an array):
|
||||
*
|
||||
* "posterDesign": { "family": "regionPoster", "name": "region-poster.66x66", "size": "66x66",
|
||||
* "mobileFriendly": true, "labelKey": "regions.posterTitle", "instance": "africa" }
|
||||
*
|
||||
* family a word naming the kind of poster (camelCase or dashes)
|
||||
* name the design's stable configuration name, the same rule as templates.js: words joined by "." or
|
||||
* "-". Once shipped it is never renamed or removed (check:poster-design-catalogue's names ledger).
|
||||
* size what it prints at (a ladder key such as "66x66", or "letter", "a4", "phone" …)
|
||||
* mobileFriendly true when it reads well on a phone
|
||||
* labelKey optional: the card's title, a catalogue key the plugin owns; else the profile's own label
|
||||
* instance optional: a preset id (capability 2) whose page shows this design; else the profile's page
|
||||
*
|
||||
* The build carries the block into manifest.json (internal/profile-meta.mjs), so Poster Design lists every plugin's
|
||||
* designs without importing any plugin's code, and a plugin adding its block never makes Poster Design rebuild.
|
||||
* Pure (no Vue, no DOM), so check:poster-design-catalogue validates with the same rules the page filters by.
|
||||
*/
|
||||
export const NAME_RE = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/
|
||||
const FAMILY_RE = /^[a-z][a-zA-Z0-9-]*$/
|
||||
const KEY_RE = /^[a-zA-Z][\w-]*(?:\.[\w-]+)+$/
|
||||
|
||||
/** The blocks a meta's posterDesign holds, as an array (one object or an array of them). */
|
||||
export const blocksOf = (posterDesign) => (Array.isArray(posterDesign) ? posterDesign : posterDesign ? [posterDesign] : [])
|
||||
|
||||
/** What is wrong with one block, as sentences; [] when it is valid. */
|
||||
export function blockProblems(b) {
|
||||
const out = []
|
||||
if (!b || typeof b !== 'object' || Array.isArray(b)) return ['is not an object']
|
||||
if (typeof b.family !== 'string' || !FAMILY_RE.test(b.family)) out.push(`family ${JSON.stringify(b.family)} is not a word`)
|
||||
if (typeof b.name !== 'string' || !NAME_RE.test(b.name) || !b.name.includes('.')) out.push(`name ${JSON.stringify(b.name)} is not a configuration name (words joined by "." or "-", with at least one ".")`)
|
||||
if (typeof b.size !== 'string' || !b.size.trim()) out.push('size is missing')
|
||||
if (typeof b.mobileFriendly !== 'boolean') out.push('mobileFriendly is not true or false')
|
||||
if (b.labelKey !== undefined && (typeof b.labelKey !== 'string' || !KEY_RE.test(b.labelKey))) out.push(`labelKey ${JSON.stringify(b.labelKey)} is not a catalogue key`)
|
||||
if (b.instance !== undefined && (typeof b.instance !== 'string' || !b.instance.trim())) out.push('instance is empty')
|
||||
const known = new Set(['family', 'name', 'size', 'mobileFriendly', 'labelKey', 'instance'])
|
||||
for (const k of Object.keys(b)) if (!known.has(k)) out.push(`unknown field "${k}"`)
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* Every valid design in a manifest, with the page that shows it as an absolute URL (resolved against the manifest's
|
||||
* own URL, since the manifest's URLs are tree-relative). An invalid block is skipped here; the check names it.
|
||||
*/
|
||||
export function designsFromManifest(manifest, manifestUrl) {
|
||||
const out = []
|
||||
for (const b of (manifest && manifest.bundles) || []) {
|
||||
for (const p of b.profiles || []) {
|
||||
for (const d of blocksOf(p.posterDesign)) {
|
||||
if (blockProblems(d).length) continue
|
||||
const inst = d.instance ? (p.instances || []).find((i) => i.id === d.instance) : null
|
||||
if (d.instance && !inst) continue
|
||||
const rel = inst ? inst.iframeUrl : p.iframeUrl
|
||||
let pageUrl = rel
|
||||
try { pageUrl = new URL(rel, manifestUrl).href } catch { /* keep it as written */ }
|
||||
out.push({ name: d.name, family: d.family, size: d.size, mobileFriendly: d.mobileFriendly, labelKey: d.labelKey || '',
|
||||
label: (inst && inst.title) || p.label || p.profile, bundle: b.bundle, profile: p.profile, instance: d.instance || '', pageUrl })
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/** The first manifest that answers, from a list of candidate URLs; { designs: [] } when none does. */
|
||||
export async function loadPluginDesigns(urls, fetchImpl = globalThis.fetch) {
|
||||
for (const url of urls.filter(Boolean)) {
|
||||
try {
|
||||
const r = await fetchImpl(url) // same-origin credentials (the default): a gated tree still answers
|
||||
if (!r.ok) continue
|
||||
return { url, designs: designsFromManifest(await r.json(), url) }
|
||||
} catch { /* the next candidate */ }
|
||||
}
|
||||
return { url: '', designs: [] }
|
||||
}
|
||||
|
|
@ -27,13 +27,15 @@ export const SAMPLE_COUNTRY_ID = 'country-npl'
|
|||
// pinned asset, not bundled, so its table cannot be imported here; check:poster-design-catalogue reads
|
||||
// FORMATS from the source and fails when the two disagree.
|
||||
// In the gallery's order: the 3 × 3 country sheet first, then the large sizes, then the rest. The large SIDE-LANE
|
||||
// sheets name a whole country's groups beside the map; the one country that prints on them is India (countrySheet.js
|
||||
// LARGE_SHEETS), so India is their sample, drawn at each requested side-lane size through the shared path.
|
||||
// sheets name a whole country's groups beside the map. India is the one large-sheet country (countrySheet.js
|
||||
// LARGE_SHEETS) and prints at 5 × 8 ft ONLY, so it samples that card (withheld, so the card shows the sentence). No
|
||||
// country prints at 66x66 or 36x60 today: those two are drawn with the largest drawable countries (China, Sudan) to
|
||||
// show the template, and their details say so (missing: noCountryAtSize). Names never change (src/catalogue-names.json).
|
||||
export const SHEET_FORMATS = {
|
||||
'3x3': { page: '3 × 3', px: [1500, 1500] },
|
||||
'60x96': { page: '5 × 8 ft, side lanes', px: [2500, 4000], sample: 'country-ind' },
|
||||
'66x66': { page: '5.5 × 5.5 ft, side lanes', px: [2750, 2750], sample: 'country-ind' },
|
||||
'36x60': { page: '3 × 5 ft, side lanes', px: [1500, 2500], sample: 'country-ind' },
|
||||
'66x66': { page: '5.5 × 5.5 ft, side lanes', px: [2750, 2750], sample: 'country-chn', missing: 'posterDesign.missing.noCountryAtSize' },
|
||||
'36x60': { page: '3 × 5 ft, side lanes', px: [1500, 2500], sample: 'country-sdn', missing: 'posterDesign.missing.noCountryAtSize' },
|
||||
'3x4': { page: '3 × 4', px: [1500, 2000], legacy: true },
|
||||
letter: { page: 'Letter, portrait', px: [1500, 1941] },
|
||||
packet: { page: 'Letter, portrait (packet page 1)', px: [1500, 1941] },
|
||||
|
|
@ -58,6 +60,7 @@ function sheetEntry(format) {
|
|||
size: f.page,
|
||||
px: f.px,
|
||||
legacy: !!f.legacy,
|
||||
...(f.missing ? { missing: f.missing } : {}),
|
||||
renderer: 'library/packet/posterSheets.js generateCountrySheet → library/packet/poster-render.js',
|
||||
settings: { format },
|
||||
sample: f.sample || SAMPLE_COUNTRY_ID,
|
||||
|
|
|
|||
34
plugin-designs/poster-sections/index.html
Normal file
34
plugin-designs/poster-sections/index.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>doxa-maps · poster-sections staging</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body { background: #f3f3f1; color: #3b463d; min-height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; }
|
||||
.wrap { max-width: 1280px; margin: 0 auto; padding: 32px 20px; }
|
||||
h1 { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
|
||||
.sub { color: #6d766e; font-size: 13px; margin: 6px 0 24px; }
|
||||
.slot { border: 1px solid #d1d4cd; border-radius: 12px; overflow: hidden; background: #fff; margin-bottom: 24px; }
|
||||
.slot .label { font: 12px/1 ui-monospace, monospace; color: #6d766e; padding: 8px 12px;
|
||||
background: #f3f3f1; border-bottom: 1px solid #d1d4cd; }
|
||||
.frame { height: 900px; position: relative; }
|
||||
poster-sections { display: block; width: 100%; height: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1>poster-sections · staging</h1>
|
||||
<p class="sub">One bundle, one profile: the Poster Sections page over the library's
|
||||
<code><dxm-poster-section></code>. Compiles into <code>poster-sections.js</code>.</p>
|
||||
<div class="slot">
|
||||
<div class="label">profile-config.profile = "poster-sections"</div>
|
||||
<div class="frame"><poster-sections profile-config='{"profile":"poster-sections"}'></poster-sections></div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
24
plugin-designs/poster-sections/index.js
Normal file
24
plugin-designs/poster-sections/index.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { defineMapElement } from '@map/elements/defineMapElement.js'
|
||||
import { registerPosterSectionElement } from '@map/elements/posterSectionElement.js'
|
||||
/**
|
||||
* plugin-designs/poster-sections/index.js
|
||||
*
|
||||
* Bundle entry. Registers TWO tags:
|
||||
* - <poster-sections> the Poster Sections page: a panel of choices over the live sections, plus the tag to copy
|
||||
* - <dxm-poster-section> the library's poster section element itself, so a website that loads ONLY this script
|
||||
* can place the tags the page hands out:
|
||||
*
|
||||
* <script src="…/dx-maps/poster-sections/poster-sections.js"></script>
|
||||
* <dxm-poster-section type="country" scope="africa" locale="ar" columns="3"></dxm-poster-section>
|
||||
*/
|
||||
const profileModules = import.meta.glob('./*.vue')
|
||||
|
||||
const PosterSectionsElement = defineMapElement({ profileModules })
|
||||
|
||||
if (typeof customElements !== 'undefined' && !customElements.get('poster-sections')) {
|
||||
customElements.define('poster-sections', PosterSectionsElement)
|
||||
}
|
||||
|
||||
registerPosterSectionElement()
|
||||
|
||||
export default PosterSectionsElement
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"//": "Presets of the poster-sections parameterized profile (capability 2, the same shape as doxa-simple-map's simple-map.instances.json). ONE profile, one preset per row: a poster type × a region (scope; 'all' = one section per region). The build emits a standalone page and embed snippet per preset at poster-sections/poster-sections/<id>/index.html and lists them in manifest.json. GENERATED by `bun run gen:poster-sections-presets` from each poster type's own scopes(), and check:poster-sections holds it to them: country posters = DX Life's /regions regions; a type joins once its type file is on the tree and its page can load it. 'configure' adds the panel of choices and Copy embed, a tool for making new presets.",
|
||||
"profile": "poster-sections",
|
||||
"instances": [
|
||||
{
|
||||
"id": "countries-all",
|
||||
"title": "Country posters — all regions",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "all"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-asia",
|
||||
"title": "Country posters — Asia",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "asia"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-middle-east",
|
||||
"title": "Country posters — Middle East",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "middle-east"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-latin-america-and-caribbean",
|
||||
"title": "Country posters — Latin America & Caribbean",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "latin-america-and-caribbean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-africa",
|
||||
"title": "Country posters — Africa",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "africa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-europe",
|
||||
"title": "Country posters — Europe",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "europe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-north-america-and-non-spanish-caribbean",
|
||||
"title": "Country posters — North America & Non-Spanish Caribbean",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "north-america-and-non-spanish-caribbean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-oceania",
|
||||
"title": "Country posters — Oceania",
|
||||
"props": {
|
||||
"type": "country",
|
||||
"scope": "oceania"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "configure",
|
||||
"title": "Configure — make a new preset",
|
||||
"props": {
|
||||
"configure": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
4
plugin-designs/poster-sections/poster-sections.meta.json
Normal file
4
plugin-designs/poster-sections/poster-sections.meta.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"label": "Poster Sections — presets",
|
||||
"note": "One profile, many presets (capability 2): each region's poster section (country posters by DX Life's /regions regions), plus all regions, each with its own page and embed snippet in manifest.json. Other poster types join when their type can load on the page. The Configure preset adds a panel of choices and Copy embed for making new presets."
|
||||
}
|
||||
182
plugin-designs/poster-sections/poster-sections.vue
Normal file
182
plugin-designs/poster-sections/poster-sections.vue
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<script setup>
|
||||
/**
|
||||
* @description Poster Sections: one region's posters (or every region's), as ready-made presets with their own page and embed code; the Configure preset adds a panel of choices and Copy embed for making new ones.
|
||||
*
|
||||
* poster-sections.vue — ONE parameterized profile (capability 2, like doxa-simple-map): its props pick the poster
|
||||
* type, the region ('all' = one section per region), the language, posters per row and collapsed, and it renders the
|
||||
* library's <dxm-poster-section> element for them. poster-sections.instances.json lists the presets; the build gives
|
||||
* each its own page and embed snippet. The `configure` preset adds the panel of choices and "Copy embed", a tool for
|
||||
* making new presets. Nothing here draws a poster: the sections ARE the element (registered by index.js), and Copy
|
||||
* embed shows the same attributes as text (src/embed.js, one function for both).
|
||||
*/
|
||||
import { computed, h, inject, ref, watch } from 'vue'
|
||||
import { useOptionalI18n } from '@map/composables/useOptionalT.js'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
import { SUPPORTED_LOCALES, RTL_LOCALES, getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
|
||||
import { posterType, posterTypeNames } from '@map/components/poster/posterTypes.js'
|
||||
import { absoluteEmbedUrl, resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { TAG, sectionAttrs, embedSnippet } from './src/embed.js'
|
||||
|
||||
const profileConfig = inject('profileConfig', ref(null))
|
||||
const cfg = computed(() => profileConfig.value || {})
|
||||
const { t, locale: pageLocale } = useOptionalI18n('')
|
||||
const pageLang = computed(() => String(pageLocale?.value || cfg.value.locale || getRuntimeLocale() || detectLocale() || 'en'))
|
||||
const dir = computed(() => (RTL_LOCALES.has(pageLang.value.split('-')[0]) ? 'rtl' : 'ltr'))
|
||||
|
||||
// ── the choices: seeded from the preset's props, then the panel's (configure) ──────────────────────────────
|
||||
const configure = computed(() => cfg.value.configure === true)
|
||||
const ALL = (v) => (!v || v === 'all' ? '' : String(v)) // '' = All regions
|
||||
const type = ref('country')
|
||||
const scope = ref('')
|
||||
const lang = ref('en')
|
||||
const collapsed = ref(false)
|
||||
const columns = ref(2)
|
||||
watch(cfg, (c) => {
|
||||
type.value = c.type || 'country'
|
||||
scope.value = ALL(c.scope)
|
||||
const l = c.locale || pageLang.value
|
||||
lang.value = SUPPORTED_LOCALES.includes(l) ? l : 'en'
|
||||
collapsed.value = c.collapsed === true
|
||||
columns.value = Number(c.columns) || 2
|
||||
}, { immediate: true })
|
||||
const COLUMN_CHOICES = [1, 2, 3, 4, 5]
|
||||
|
||||
// The poster types the registry knows, read at runtime and again whenever the picker is opened: another bundle on
|
||||
// the page may register its type later (each type's own bundle registers it). Named in the reader's language when the
|
||||
// catalogue has a name for it, else by its registry name.
|
||||
const types = ref(posterTypeNames())
|
||||
const refreshTypes = () => { const now = posterTypeNames(); if (now.join() !== types.value.join()) types.value = now }
|
||||
const typeLabel = (name) => {
|
||||
const key = `posterSections.type${name.charAt(0).toUpperCase()}${name.slice(1)}`
|
||||
const v = t(key)
|
||||
return v && v !== key ? v : name
|
||||
}
|
||||
// A language in its OWN name (العربية, Deutsch …), so a reader finds theirs whatever the page's language.
|
||||
const langLabel = (code) => {
|
||||
try { return new Intl.DisplayNames([code], { type: 'language' }).of(code) || code } catch { return code }
|
||||
}
|
||||
|
||||
// The regions the chosen type offers (its adapter's scopes), each named by the type's own section title.
|
||||
const adapter = ref(null)
|
||||
watch(type, async (name) => { adapter.value = await posterType(name) }, { immediate: true })
|
||||
const scopes = computed(() => {
|
||||
const a = adapter.value
|
||||
const list = a && typeof a.scopes === 'function' ? a.scopes() : []
|
||||
const named = list.map((key) => ({ key, label: a.title(key, t) }))
|
||||
// alphabetical in the page's language, with the catch-all last
|
||||
return named.sort((x, y) => (x.key === 'other') - (y.key === 'other') || x.label.localeCompare(y.label, pageLang.value))
|
||||
})
|
||||
// In the panel, a region the newly chosen type does not have falls back to All regions. A preset keeps its scope even
|
||||
// before its type is registered (the element then says it has no posters, rather than silently showing another region).
|
||||
watch(scopes, (list) => { if (configure.value && scope.value && list.length && !list.some((s) => s.key === scope.value)) scope.value = '' })
|
||||
|
||||
// ── the tags ─────────────────────────────────────────────────────────────────
|
||||
// Absolute, so the pasted tags work on another website (a relative path would resolve against THAT site).
|
||||
const absolute = (u) => { try { return new URL(u, document.baseURI).href } catch { return u } }
|
||||
const assetsBase = computed(() => absolute(resolveAssetsBase(cfg.value)))
|
||||
const scriptUrl = computed(() => absoluteEmbedUrl('poster-sections/poster-sections.js', cfg.value))
|
||||
const choices = computed(() => ({
|
||||
type: type.value, scope: scope.value, scopes: scopes.value.map((s) => s.key),
|
||||
locale: lang.value, collapsed: collapsed.value, columns: columns.value, assetsBase: assetsBase.value,
|
||||
}))
|
||||
const snippet = computed(() => embedSnippet(choices.value, scriptUrl.value))
|
||||
|
||||
// The live sections: the element itself, with exactly the attributes above. Keyed by them, so a new choice
|
||||
// mounts a fresh section (a language change rebuilds its catalogue rather than patching it).
|
||||
const Sections = () => h('div', { class: 'pss__sections' },
|
||||
sectionAttrs(choices.value).map((a) => h(TAG, { key: JSON.stringify(a), ...a })))
|
||||
|
||||
const copied = ref(false)
|
||||
async function copy() {
|
||||
try { await navigator.clipboard.writeText(snippet.value) } catch {
|
||||
const ta = document.createElement('textarea'); ta.value = snippet.value; document.body.appendChild(ta); ta.select()
|
||||
try { document.execCommand('copy') } finally { ta.remove() }
|
||||
}
|
||||
copied.value = true; setTimeout(() => { copied.value = false }, 1600)
|
||||
}
|
||||
|
||||
useShadowStyles(`
|
||||
.pss { box-sizing: border-box; height: 100%; overflow: auto; padding: 20px clamp(12px, 3vw, 32px) 40px; color: #3b463d;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: #f3f3f1; }
|
||||
.pss *, .pss *::before, .pss *::after { box-sizing: border-box; }
|
||||
.pss__head h2 { margin: 0; font-size: 22px; font-weight: 700; }
|
||||
.pss__head p { margin: 6px 0 16px; color: #6d766e; font-size: 14px; max-width: 60ch; }
|
||||
.pss__panel { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px 16px; align-items: end;
|
||||
background: #fff; border: 1px solid #d1d4cd; border-radius: 12px; padding: 14px 16px; }
|
||||
.pss__field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
||||
.pss__field > span { font-size: 13px; font-weight: 600; }
|
||||
.pss__field select { font: inherit; font-size: 14px; padding: 7px 8px; border: 1px solid #c3c8bf; border-radius: 8px; background: #fff;
|
||||
color: inherit; min-height: 38px; width: 100%; }
|
||||
.pss__check { flex-direction: row; align-items: center; gap: 8px; min-height: 38px; }
|
||||
.pss__check input { width: 18px; height: 18px; margin: 0; }
|
||||
.pss__embed { margin-top: 12px; background: #fff; border: 1px solid #d1d4cd; border-radius: 12px; padding: 12px 16px; }
|
||||
.pss__embed-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
||||
.pss__embed h3 { margin: 0; font-size: 15px; }
|
||||
.pss__embed p { margin: 2px 0 0; font-size: 13px; color: #6d766e; }
|
||||
.pss__copy { font: inherit; font-size: 14px; font-weight: 600; padding: 7px 16px; border-radius: 999px; border: 1px solid #3b463d;
|
||||
background: #3b463d; color: #fff; cursor: pointer; min-height: 38px; }
|
||||
.pss__copy:focus-visible, .pss__field select:focus-visible, .pss__check input:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
|
||||
.pss__code { display: block; width: 100%; margin-top: 10px; min-height: 84px; max-height: 220px; resize: vertical; font: 12px/1.5 ui-monospace, monospace;
|
||||
direction: ltr; text-align: left; white-space: pre; overflow: auto; border: 1px solid #d1d4cd; border-radius: 8px; padding: 8px; background: #f8f8f6; color: #1f2937; }
|
||||
.pss--preset { padding-top: 12px; }
|
||||
.pss--preset .pss__sections { margin-top: 0; }
|
||||
.pss__sections { margin-top: 20px; display: flex; flex-direction: column; gap: 16px; }
|
||||
.pss__sections > ${TAG} { display: block; }
|
||||
`, 'poster-sections')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pss" :class="{ 'pss--preset': !configure }" :dir="dir" :lang="pageLang">
|
||||
<template v-if="configure">
|
||||
<header class="pss__head">
|
||||
<h2>{{ t('posterSections.heading') }}</h2>
|
||||
<p>{{ t('posterSections.intro') }}</p>
|
||||
</header>
|
||||
|
||||
<form class="pss__panel" @submit.prevent>
|
||||
<label class="pss__field">
|
||||
<span>{{ t('posterSections.type') }}</span>
|
||||
<select v-model="type" data-param="type" @focus="refreshTypes" @pointerdown="refreshTypes">
|
||||
<option v-for="n in types" :key="n" :value="n">{{ typeLabel(n) }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="pss__field">
|
||||
<span>{{ t('posterSections.region') }}</span>
|
||||
<select v-model="scope" data-param="scope">
|
||||
<option value="">{{ t('posterSections.allRegions') }}</option>
|
||||
<option v-for="s in scopes" :key="s.key" :value="s.key">{{ s.label }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="pss__field">
|
||||
<span>{{ t('posterSections.language') }}</span>
|
||||
<select v-model="lang" data-param="locale">
|
||||
<option v-for="c in SUPPORTED_LOCALES" :key="c" :value="c" :lang="c">{{ langLabel(c) }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="pss__field">
|
||||
<span>{{ t('buttons.postersPerRow') }}</span>
|
||||
<select v-model.number="columns" data-param="columns">
|
||||
<option v-for="n in COLUMN_CHOICES" :key="n" :value="n">{{ n }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="pss__field pss__check">
|
||||
<input v-model="collapsed" type="checkbox" data-param="collapsed" />
|
||||
<span>{{ t('posterSections.startCollapsed') }}</span>
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<section class="pss__embed">
|
||||
<div class="pss__embed-row">
|
||||
<div>
|
||||
<h3>{{ t('posterSections.embed') }}</h3>
|
||||
<p>{{ t('posterSections.embedNote') }}</p>
|
||||
</div>
|
||||
<button type="button" class="pss__copy" @click="copy">{{ copied ? t('posterSections.copied') : t('posterSections.copy') }}</button>
|
||||
</div>
|
||||
<textarea class="pss__code" readonly :value="snippet" :aria-label="t('posterSections.embed')" spellcheck="false"></textarea>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<Sections />
|
||||
</div>
|
||||
</template>
|
||||
133
plugin-designs/poster-sections/scripts/check-poster-sections.mjs
Normal file
133
plugin-designs/poster-sections/scripts/check-poster-sections.mjs
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env node
|
||||
// check:poster-sections — the Poster Sections panel's choices reach the element unchanged.
|
||||
// For every choice the panel offers (type, one region or All regions, each language, collapsed or open, 1-5 per
|
||||
// row), the tags src/embed.js builds, read back through the ELEMENT's own attribute reader (posterSectionConfig),
|
||||
// give exactly those choices; "All regions" is one section per region, each once; the snippet's script and tags
|
||||
// are the same tags the page renders.
|
||||
// PRESETS: poster-sections.instances.json has, per poster type, exactly "<prefix>-all" + one preset per region its
|
||||
// adapter's scopes() returns, no more, no fewer. So a region the data no longer has (a stale "other") or a new one
|
||||
// cannot drift from the presets. A type whose file is NOT on the tree must have no presets at all (its pages would
|
||||
// only say "No posters"): 'upg100-list' is listed once its type file is on the tree.
|
||||
// PAGES: the built preset pages (public/<tree>/poster-sections/poster-sections/<id>/) are exactly the presets: the tree
|
||||
// generator writes a page per preset but never deletes one, so a dropped preset would leave a stale page behind.
|
||||
// --self-test: a builder that drops `columns`, one that drops `collapsed`, one that repeats a region under All, and a
|
||||
// presets file with a region the type does not have must each FAIL.
|
||||
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
// the site's languages = the catalogue's locale folders (library/i18n/index.js needs Vite's import.meta.glob)
|
||||
const LOC = join(dirname(fileURLToPath(import.meta.url)), '../../../library/i18n/locales')
|
||||
const SUPPORTED_LOCALES = readdirSync(LOC).filter((d) => statSync(join(LOC, d)).isDirectory()).sort()
|
||||
import { posterSectionConfig } from '../../../library/elements/posterSectionConfig.js'
|
||||
import * as real from '../src/embed.js'
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '../../..')
|
||||
const PRESETS = JSON.parse(readFileSync(join(ROOT, 'plugin-designs/poster-sections/poster-sections.instances.json'), 'utf8'))
|
||||
// poster type → [preset id prefix, the file that defines the type's adapter (its default export has scopes())]
|
||||
const TYPES = { country: ['countries', 'library/components/poster/types/countryPosterType.js'], 'upg100-list': ['upg100', 'plugin-designs/my-upg-100-list/data/upg100-poster-type.js'] }
|
||||
|
||||
// Each type's scopes(), loaded through Vite (the adapters import through the @map alias).
|
||||
async function loadScopes() {
|
||||
const { createServer } = await import('vite')
|
||||
const server = await createServer({ root: ROOT, configFile: false, logLevel: 'error', appType: 'custom', server: { middlewareMode: true, hmr: false, ws: false }, resolve: { alias: { '@map': join(ROOT, 'library') } } })
|
||||
const out = {}
|
||||
try {
|
||||
for (const [type, [, file]] of Object.entries(TYPES)) {
|
||||
if (!existsSync(join(ROOT, file))) { out[type] = null; continue }
|
||||
const a = (await server.ssrLoadModule(join(ROOT, file))).default
|
||||
out[type] = a.scopes()
|
||||
}
|
||||
} finally { await server.close() }
|
||||
return out
|
||||
}
|
||||
|
||||
function presetArm(presets, scopesByType) {
|
||||
const fails = []
|
||||
for (const [type, [prefix]] of Object.entries(TYPES)) {
|
||||
const scopes = scopesByType[type]
|
||||
const ids = presets.instances.filter((i) => i.props && i.props.type === type).map((i) => i.id)
|
||||
if (scopes == null) { // the type's file is not on this tree: a preset of it could only say "No posters"
|
||||
if (ids.length) fails.push(`presets for "${type}" exist (${ids.join(', ')}) but its type is not on this tree, so their pages would show no posters`)
|
||||
continue
|
||||
}
|
||||
const want = [`${prefix}-all`, ...scopes.map((s) => `${prefix}-${s}`)].sort()
|
||||
const have = [...ids].sort()
|
||||
const extra = have.filter((x) => !want.includes(x)), missing = want.filter((x) => !have.includes(x))
|
||||
if (extra.length || missing.length) fails.push(`presets for "${type}" differ from its scopes(): extra ${extra.join(', ') || '-'}; missing ${missing.join(', ') || '-'}`)
|
||||
for (const i of presets.instances.filter((x) => x.props && x.props.type === type && x.id !== `${prefix}-all`)) {
|
||||
if (i.id !== `${prefix}-${i.props.scope}`) fails.push(`preset "${i.id}" shows scope "${i.props.scope}"`)
|
||||
}
|
||||
}
|
||||
if (!presets.instances.some((i) => i.id === 'configure' && i.props && i.props.configure === true)) fails.push('no "configure" preset')
|
||||
return fails
|
||||
}
|
||||
|
||||
// The built preset pages, when a tree is built: every folder under poster-sections/poster-sections/ that holds a page.
|
||||
function builtPages() {
|
||||
const pub = join(ROOT, 'public')
|
||||
if (!existsSync(pub)) return null
|
||||
for (const tree of readdirSync(pub)) {
|
||||
const dir = join(pub, tree, 'poster-sections', 'poster-sections')
|
||||
if (existsSync(dir)) return readdirSync(dir).filter((d) => existsSync(join(dir, d, 'index.html')))
|
||||
}
|
||||
return null
|
||||
}
|
||||
function pagesArm(presets, pages) {
|
||||
if (pages == null) return []
|
||||
const ids = presets.instances.map((i) => i.id)
|
||||
const stale = pages.filter((p) => !ids.includes(p)), missing = ids.filter((i) => !pages.includes(i))
|
||||
return stale.length || missing.length ? [`built preset pages differ from the presets: stale ${stale.join(', ') || '-'}; missing ${missing.join(', ') || '-'} (delete a dropped preset's page; rebuild the tree for a new one)`] : []
|
||||
}
|
||||
|
||||
const SCOPES = ['africa', 'asia', 'europe', 'latin-america-and-caribbean', 'middle-east', 'north-america-and-non-spanish-caribbean', 'oceania', 'other']
|
||||
const fake = (attrs) => ({ getAttribute: (n) => (n in attrs ? attrs[n] : null), hasAttribute: (n) => n in attrs })
|
||||
|
||||
function arms(embed) {
|
||||
const fails = []
|
||||
for (const scope of ['', ...SCOPES]) for (const locale of SUPPORTED_LOCALES) for (const collapsed of [false, true]) for (const columns of [1, 2, 3, 4, 5]) {
|
||||
const choices = { type: 'country', scope, scopes: SCOPES, locale, collapsed, columns, assetsBase: 'https://example.org/dx-maps/assets' }
|
||||
const tags = embed.sectionAttrs(choices)
|
||||
const want = scope ? [scope] : SCOPES
|
||||
if (tags.map((a) => a.scope).join() !== want.join()) { fails.push(`scope "${scope || 'All'}": sections ${tags.map((a) => a.scope).join()}`); continue }
|
||||
for (const a of tags) {
|
||||
const c = posterSectionConfig(fake(a))
|
||||
const bad = [c.type !== 'country' && 'type', c.locale !== locale && 'locale', c.collapsed !== collapsed && 'collapsed',
|
||||
c.columns !== columns && 'columns', c.assetsBase !== choices.assetsBase && 'assets-base', c.collapsible !== true && 'collapsible'].filter(Boolean)
|
||||
if (bad.length) { fails.push(`${a.scope}/${locale}/${collapsed ? 'shut' : 'open'}/${columns}: the element reads back a different ${bad.join(', ')}`); break }
|
||||
}
|
||||
if (fails.length > 3) return fails
|
||||
}
|
||||
const snip = embed.embedSnippet({ type: 'country', scope: '', scopes: SCOPES, locale: 'ar', collapsed: true, columns: 3 }, 'https://example.org/dx-maps/poster-sections/poster-sections.js')
|
||||
const lines = snip.split('\n')
|
||||
if (lines[0] !== '<script src="https://example.org/dx-maps/poster-sections/poster-sections.js"></script>') fails.push(`snippet: first line is not the bundle script: ${lines[0]}`)
|
||||
if (lines.length !== SCOPES.length + 1 || !lines.slice(1).every((l, i) => l === `<dxm-poster-section type="country" scope="${SCOPES[i]}" locale="ar" collapsed columns="3"></dxm-poster-section>`)) fails.push(`snippet: the tags are not one per region with the chosen attributes:\n${snip}`)
|
||||
return fails
|
||||
}
|
||||
|
||||
const SCOPES_BY_TYPE = await loadScopes()
|
||||
|
||||
if (process.argv.includes('--self-test')) {
|
||||
const mutants = {
|
||||
'drops columns': { ...real, sectionAttrs: (c) => real.sectionAttrs(c).map(({ columns, ...a }) => a) },
|
||||
'drops collapsed': { ...real, sectionAttrs: (c) => real.sectionAttrs(c).map(({ collapsed, ...a }) => a) },
|
||||
'repeats a region under All': { ...real, sectionAttrs: (c) => { const r = real.sectionAttrs(c); return c.scope ? r : [...r, r[0]] } },
|
||||
}
|
||||
let ok = true
|
||||
const stale = { ...PRESETS, instances: [...PRESETS.instances, { id: 'countries-atlantis', props: { type: 'country', scope: 'atlantis' } }] }
|
||||
const pg = pagesArm(PRESETS, [...PRESETS.instances.map((i) => i.id), 'countries-atlantis'])
|
||||
console.log(` mutant "a built page for a preset that no longer exists": ${pg.length ? 'FAILS (good)' : 'PASSES (bad)'}`)
|
||||
if (!pg.length) ok = false
|
||||
const pf = presetArm(stale, SCOPES_BY_TYPE)
|
||||
console.log(` mutant "a preset for a region the type does not have": ${pf.length ? 'FAILS (good)' : 'PASSES (bad)'}`)
|
||||
if (!pf.length) ok = false
|
||||
for (const [name, m] of Object.entries(mutants)) {
|
||||
const f = arms(m)
|
||||
console.log(` mutant "${name}": ${f.length ? 'FAILS (good)' : 'PASSES (bad)'}`)
|
||||
if (!f.length) ok = false
|
||||
}
|
||||
console.log(ok ? 'POSTER-SECTIONS SELF-TEST: PASS — every mutant fails the check.' : 'POSTER-SECTIONS SELF-TEST: FAIL')
|
||||
process.exit(ok ? 0 : 1)
|
||||
}
|
||||
const fails = [...arms(real), ...presetArm(PRESETS, SCOPES_BY_TYPE), ...pagesArm(PRESETS, builtPages())]
|
||||
const measured = Object.entries(SCOPES_BY_TYPE).filter(([, v]) => v).map(([k, v]) => `${k} ${v.length}`).join(', ')
|
||||
if (fails.length) { console.log(`POSTER-SECTIONS: FAIL (${fails.length})`); fails.forEach((f) => console.log(` - ${f}`)); process.exit(1) }
|
||||
console.log(`POSTER-SECTIONS: PASS — every panel choice (${SCOPES.length + 1} region choices × ${SUPPORTED_LOCALES.length} languages × open/collapsed × 5 per row) reads back through the element unchanged; All regions = one section per region; the snippet is the script + those tags; presets = each type's scopes() + all (${measured}).`)
|
||||
42
plugin-designs/poster-sections/scripts/gen-presets.mjs
Normal file
42
plugin-designs/poster-sections/scripts/gen-presets.mjs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env node
|
||||
// gen-presets.mjs (bun run gen:poster-sections-presets) — writes poster-sections.instances.json from each poster type's
|
||||
// OWN regions: "<prefix>-all" plus one preset per scope its adapter's scopes() returns, titled by the type's own
|
||||
// (English) section title, then "configure". The same source check:poster-sections holds the file to, so when the
|
||||
// data's regions change, running this is the whole fix. A type whose file is not on the tree gets NO presets: its
|
||||
// pages could only say "No posters".
|
||||
import { readFileSync, writeFileSync, existsSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '../../..')
|
||||
const FILE = join(ROOT, 'plugin-designs/poster-sections/poster-sections.instances.json')
|
||||
const TYPES = [
|
||||
{ type: 'country', prefix: 'countries', noun: 'Country posters', file: 'library/components/poster/types/countryPosterType.js' },
|
||||
{ type: 'upg100-list', prefix: 'upg100', noun: 'UPG 100 posters', file: 'plugin-designs/my-upg-100-list/data/upg100-poster-type.js' },
|
||||
]
|
||||
const old = JSON.parse(readFileSync(FILE, 'utf8'))
|
||||
const { createServer } = await import('vite')
|
||||
const server = await createServer({ root: ROOT, configFile: false, logLevel: 'error', appType: 'custom', server: { middlewareMode: true, hmr: false, ws: false }, resolve: { alias: { '@map': join(ROOT, 'library') } } })
|
||||
// English from the real catalogue (library/i18n/locales/en/<area>.json), so a title the type translates reads as the
|
||||
// site's English; a key the catalogue lacks comes back as itself, which every adapter treats as "use the region name".
|
||||
const english = (key) => {
|
||||
const [area, ...parts] = String(key).split('.')
|
||||
const f = join(ROOT, `library/i18n/locales/en/${area}.json`)
|
||||
if (!parts.length || !existsSync(f)) return key
|
||||
let o = JSON.parse(readFileSync(f, 'utf8'))
|
||||
for (const p of parts) { if (!o || typeof o !== 'object' || !(p in o)) return key; o = o[p] }
|
||||
return typeof o === 'string' ? o : key
|
||||
}
|
||||
const instances = []
|
||||
try {
|
||||
for (const { type, prefix, noun, file } of TYPES) {
|
||||
if (!existsSync(join(ROOT, file))) continue
|
||||
const a = (await server.ssrLoadModule(join(ROOT, file))).default
|
||||
instances.push({ id: `${prefix}-all`, title: `${noun} — all regions`, props: { type, scope: 'all' } })
|
||||
for (const scope of a.scopes()) instances.push({ id: `${prefix}-${scope}`, title: `${noun} — ${a.title(scope, english)}`, props: { type, scope } })
|
||||
}
|
||||
} finally { await server.close() }
|
||||
instances.push({ id: 'configure', title: 'Configure — make a new preset', props: { configure: true } })
|
||||
writeFileSync(FILE, JSON.stringify({ ...old, instances }, null, 2) + '\n')
|
||||
const before = new Set(old.instances.map((i) => i.id)), after = new Set(instances.map((i) => i.id))
|
||||
console.log(`poster-sections presets: ${instances.length} (removed: ${[...before].filter((x) => !after.has(x)).join(', ') || 'none'}; added: ${[...after].filter((x) => !before.has(x)).join(', ') || 'none'})`)
|
||||
31
plugin-designs/poster-sections/src/embed.js
Normal file
31
plugin-designs/poster-sections/src/embed.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* embed.js — the Poster Sections panel's choices → the <dxm-poster-section> tags that show them. Pure (no Vue, no
|
||||
* DOM), so the page and scripts/check-poster-sections.mjs use the SAME function: what the live sections render is
|
||||
* exactly what "Copy embed" hands a website.
|
||||
*
|
||||
* choices = { type, scope, scopes, locale, collapsed, columns, assetsBase }
|
||||
* scope '' = "All regions": one section per scope in `scopes`, in that order
|
||||
* columns 0 = leave it to the wall (no attribute)
|
||||
*/
|
||||
export const TAG = 'dxm-poster-section'
|
||||
|
||||
export function sectionAttrs(choices) {
|
||||
const { type = 'country', scope = '', scopes = [], locale = '', collapsed = false, columns = 0, assetsBase = '' } = choices || {}
|
||||
const list = scope ? [scope] : scopes
|
||||
return list.map((s) => {
|
||||
const a = { type, scope: s }
|
||||
if (locale) a.locale = locale
|
||||
if (collapsed) a.collapsed = ''
|
||||
if (columns) a.columns = String(columns)
|
||||
if (assetsBase) a['assets-base'] = assetsBase
|
||||
return a
|
||||
})
|
||||
}
|
||||
|
||||
const esc = (v) => String(v).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<')
|
||||
|
||||
/** The text a website pastes: the bundle's script once, then one tag per section. */
|
||||
export function embedSnippet(choices, scriptUrl) {
|
||||
const tags = sectionAttrs(choices).map((a) => `<${TAG}${Object.entries(a).map(([k, v]) => (v === '' ? ` ${k}` : ` ${k}="${esc(v)}"`)).join('')}></${TAG}>`)
|
||||
return [`<script src="${esc(scriptUrl)}"></script>`, ...tags].join('\n')
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* GATED. The allow-list lives in src/tabs.js; this gate is what keeps it one.
|
||||
*
|
||||
* WHAT IT ASSERTS (each arm is poisoned by --self-test before a pass is trusted):
|
||||
* A. the rail's rows are exactly the named sets: Posters = upg-100, country-posters, poster-design;
|
||||
* A. the Experimental folder starts open, and the rail's rows are exactly the named sets: Posters = upg-100, country-posters, poster-design;
|
||||
* Experimental = wall-of-people-groups, bible-posters (poster plugins only). Ids unique,
|
||||
* every row in a declared section.
|
||||
* B. no row names a NEVER bundle (key-cities, the shells themselves), and resolving the built
|
||||
|
|
@ -52,6 +52,9 @@ export function checkShelf({ SECTIONS, TABS, NEVER, resolveTabs, manifest, meta,
|
|||
const got = TABS.filter((t) => t.section === sec).map((t) => t.id)
|
||||
if (!sameList(got, want)) errs.push(`A: section ${sec} rows are [${got.join(', ')}], expected [${want.join(', ')}]`)
|
||||
}
|
||||
// The Experimental folder starts OPEN: closed, under one short section, it read as an empty caption.
|
||||
const exp = SECTIONS.find((g) => g.key === 'experimental')
|
||||
if (!exp || !exp.folder || exp.open !== true) errs.push('A: the Experimental folder must be declared folder:true, open:true (closed, it is easy to miss)')
|
||||
const extra = TABS.filter((t) => !EXPECT[t.section]).map((t) => t.id)
|
||||
if (extra.length) errs.push(`A: rows outside the allow-listed sections: ${extra.join(', ')}`)
|
||||
// B. never a gated bundle — by declaration and by resolution over the real manifest
|
||||
|
|
@ -114,6 +117,7 @@ function selfTest(base) {
|
|||
const clone = (o) => JSON.parse(JSON.stringify(o))
|
||||
const cases = [
|
||||
['A: key-cities added to Experimental', (c) => { c.TABS = [...c.TABS, { id: 'key-cities', section: 'experimental', bundle: 'key-cities', profile: 'key-cities' }] }, /^A: section experimental|^B: row key-cities/],
|
||||
['A: the Experimental folder closed again', (c) => { c.SECTIONS = c.SECTIONS.map((g) => g.key === 'experimental' ? { ...g, open: false } : g) }, /^A: the Experimental folder must be/],
|
||||
['A: a row dropped', (c) => { c.TABS = c.TABS.filter((t) => t.id !== 'wall-of-people-groups') }, /^A: section experimental/],
|
||||
['B: resolver that shows whatever the manifest carries', (c) => { c.resolveTabs = (m) => (m.bundles || []).map((b) => ({ id: b.bundle, bundle: b.bundle, profile: b.bundle, tag: b.bundle, scriptUrl: b.scriptUrl })) }, /^B: resolution produced a key-cities row/],
|
||||
['C: a composite row falls back to a leaf', (c) => { c.manifest = clone(c.manifest); const b = c.manifest.bundles.find((x) => x.bundle === 'my-upg-100-list'); b.profiles = b.profiles.filter((p) => p.profile !== 'upg-100-mfe') }, /^C: row upg-100 opens/],
|
||||
|
|
|
|||
|
|
@ -20,10 +20,14 @@
|
|||
* composite is the plugin as its owner designed it (and it is what makes Posterverse capability 5).
|
||||
*/
|
||||
|
||||
// The rail's sections, in order. `folder` renders collapsible and starts closed, like the gallery's.
|
||||
// The rail's sections, in order. Experimental is a collapsible folder, like the gallery's, but it starts
|
||||
// OPEN: the gallery's closed folder sits at the foot of a long rail, while Posterverse has one short
|
||||
// section above it, so a closed folder sat mid-drawer with empty space below and read as a caption with
|
||||
// nothing under it (measured at 390: the heading at y 335 in an 800px drawer, ~500px empty below).
|
||||
// Open, its plugins show like any section's rows; a reader can still collapse it.
|
||||
export const SECTIONS = [
|
||||
{ key: 'posters', i18n: 'sec.posters', icon: 'poster' },
|
||||
{ key: 'experimental', i18n: 'sec.experimental', icon: 'flask', folder: true, open: false },
|
||||
{ key: 'experimental', i18n: 'sec.experimental', icon: 'flask', folder: true, open: true },
|
||||
]
|
||||
|
||||
// Every row Posterverse can show, in rail order.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "single",
|
||||
"label": "Regions — country posters by region",
|
||||
"note": "The Regions dashboard's third view: one collapsible <dxm-poster-section type=\"country\"> per region (the library's element, the same cards and print options as the Country Posters wall), starting shut, in the page's locale. The regions are the ones the poster data files its countries under, in the dashboard's region order."
|
||||
}
|
||||
66
plugin-designs/regions-dashboard/country-poster-sections.vue
Normal file
66
plugin-designs/regions-dashboard/country-poster-sections.vue
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<script setup>
|
||||
/**
|
||||
* @description Country posters by region: one collapsible poster section per region (the library's <dxm-poster-section>), the Regions dashboard's third view.
|
||||
*
|
||||
* country-poster-sections.vue — the Regions dashboard's THIRD view, "Country Posters", beside the regions list and the
|
||||
* region dashboards. One <dxm-poster-section type="country"> per region, collapsible and starting shut, in the page's
|
||||
* locale.
|
||||
*
|
||||
* ★ THE ELEMENT IS THE LIBRARY'S (library/elements/posterSectionElement.js): the same section, cards and print options as
|
||||
* the Country Posters wall. This bundle only registers it (a second registration on one page is a no-op) and places it.
|
||||
*
|
||||
* ★ THE SECTIONS ARE THE REGIONS THE POSTERS ARE FILED UNDER. The country type groups its cards by the poster data's own
|
||||
* region (library/data/country-posters.json), so the list of sections comes from that same data, in the dashboard's
|
||||
* fixed region order (REGION_ORDER). That way every country with a poster lands in exactly one section; a country the
|
||||
* data files under no WAGF region gets the type's "Other" section instead of disappearing. The scope keys go through
|
||||
* the type's own regionKey, so this page and the element can never spell a region two ways.
|
||||
*/
|
||||
import { computed, h, inject, ref } from 'vue'
|
||||
import { registerPosterSectionElement } from '@map/elements/posterSectionElement.js'
|
||||
import { regionKey } from '@map/components/poster/types/countryPosterType.js'
|
||||
import posterData from '@map/data/country-posters.json'
|
||||
import { REGION_ORDER } from '@map/colors/wagf-region.js'
|
||||
import { resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
|
||||
registerPosterSectionElement()
|
||||
|
||||
const profileConfig = inject('profileConfig', ref(null))
|
||||
const cfg = computed(() => profileConfig.value || {})
|
||||
const locale = computed(() => cfg.value.locale || cfg.value.lang || cfg.value.languageCode || getRuntimeLocale() || detectLocale() || 'en')
|
||||
// The people-groups service the Regions list prints from (regions-list.vue's prayBaseUrl), so a section's prayer list
|
||||
// and cards come from the same records as the list's own country posters.
|
||||
const apiBase = computed(() => cfg.value.prayBaseUrl || cfg.value.apiBaseUrl || 'https://pray.doxa.life')
|
||||
|
||||
const scopes = computed(() => {
|
||||
const inData = new Set((posterData.countries || []).map((c) => regionKey(c.region)))
|
||||
const ordered = REGION_ORDER.map(regionKey).filter((k) => inData.has(k))
|
||||
return [...ordered, ...[...inData].filter((k) => !ordered.includes(k)).sort()]
|
||||
})
|
||||
|
||||
const section = (scope) => h('dxm-poster-section', {
|
||||
key: scope,
|
||||
type: 'country',
|
||||
scope,
|
||||
locale: locale.value,
|
||||
collapsed: '',
|
||||
'assets-base': resolveAssetsBase(cfg.value),
|
||||
'api-base-url': apiBase.value,
|
||||
...(cfg.value.isDark === true ? { 'is-dark': '' } : {}),
|
||||
})
|
||||
const Page = () => h('div', { class: 'cps' }, h('div', { class: 'cps__inner' }, scopes.value.map(section)))
|
||||
|
||||
useShadowStyles(
|
||||
// The pane gives this element its whole box; the page scrolls here, and the sections stack in it.
|
||||
':host{ display:block; height:100%; }' +
|
||||
'.cps{ height:100%; overflow-y:auto; -webkit-overflow-scrolling:touch; box-sizing:border-box; padding:16px 12px 32px; }' +
|
||||
'.cps__inner{ max-width:1180px; margin:0 auto; display:flex; flex-direction:column; gap:12px; }' +
|
||||
'.cps__inner > *{ display:block; }',
|
||||
'country-poster-sections'
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page />
|
||||
</template>
|
||||
|
|
@ -44,7 +44,6 @@ import { applyPeopleGroupList } from '@map/lists/peopleGroupList.js'
|
|||
import { SHADOW_BOX_SIZING_RESET } from './src/shadowReset.js'
|
||||
import { splitLastWord } from './src/useLastWordHighlight.js'
|
||||
import { useRegionsI18n } from '@map/i18n/useRegionsI18n.js'
|
||||
import { countryName } from '@map/utils/countryName.js'
|
||||
import { useOptionalT } from '@map/composables/useOptionalT.js'
|
||||
// ar renders right-to-left. RTL_LOCALES is the shared set and :dir on our own root is the
|
||||
// house pattern (doxa-simple-map.vue:18, :246, :1346) — an EMBEDDED element must not reach for
|
||||
|
|
@ -60,20 +59,14 @@ import { DXLIFE_TOKENS } from './dxlife-tokens.js'
|
|||
// and the no-selection chooser. The requirement: a toggle at the bottom of each region section
|
||||
// that opens that region's country posters.
|
||||
//
|
||||
// NOTHING HERE IS REBUILT. PosterWall is already the shared, fully parameterised grid that
|
||||
// my-upg-100-list/poster-wall.vue and country-posters/country-poster-wall.vue both render, and
|
||||
// it already owns the card layout, the "Show on map" header button, the Print Options footer
|
||||
// and the client-side raster. This file supplies only data and a toggle — the same division of
|
||||
// labour country-poster-wall.vue states in its own header ("this profile supplies only its
|
||||
// data and taxonomy").
|
||||
import PosterWall from '@map/components/poster/PosterWall.vue'
|
||||
// NOTHING HERE IS REBUILT. Each region's posters are the library's <dxm-poster-section type="country">
|
||||
// (library/elements/posterSectionElement.js), the same section, cards and print options as the Country Posters
|
||||
// wall and the Regions dashboard's third view, so the list is composed from the shared element rather than
|
||||
// drawing a wall of its own. This file supplies only the toggle and the region.
|
||||
import RegionSectionCard from '@map/components/regions/RegionSectionCard.vue'
|
||||
import posterData from '@map/data/country-posters.json'
|
||||
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
|
||||
import { makeCountrySheet, largeSheet, sheetWithheld } from '@map/packet/countrySheet.js'
|
||||
import { isPhonePrint } from '@map/composables/printInPlace.js'
|
||||
import { keyedError } from '@map/i18n/keyedError.js'
|
||||
import { resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
import { registerPosterSectionElement } from '@map/elements/posterSectionElement.js'
|
||||
import { regionKey } from '@map/components/poster/types/countryPosterType.js'
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
import { cyrillicHeading } from '@map/styles/portedGlobals.js'
|
||||
import { hostElement } from '@map/utils/hostElement.js'
|
||||
|
|
@ -276,44 +269,32 @@ async function loadRegions() {
|
|||
}
|
||||
}
|
||||
|
||||
// ─── COUNTRY POSTERS, per region ─────────────────────────────────────────────────────────
|
||||
// One wall per region, opened by the footer toggle. instanceId is suffixed with the region
|
||||
// slug because PosterWall scopes its CustomEvent by instanceId so several embeds on one page
|
||||
// never cross-talk (PosterWall.vue:40) — and here there genuinely are several.
|
||||
const POSTER_EVENT = 'doxa-regions:show-country-on-map'
|
||||
// ─── COUNTRY POSTERS, per region ─────────────────────────────────────────────
|
||||
// One poster section per region, opened by the footer toggle: the library's <dxm-poster-section type="country">.
|
||||
// Its scope is the region's key through the country type's own regionKey, so the list and the element can never
|
||||
// spell a region two ways. It is not collapsible (the toggle already opens and shuts it) and shows two cards a row,
|
||||
// on a phone as well.
|
||||
// ⚠ The element files countries under the poster data's region (library/data/country-posters.json), so this list
|
||||
// shows exactly that data's membership for each region.
|
||||
registerPosterSectionElement()
|
||||
const SECTION_EVENT = 'doxa-country-posters:show-on-map'
|
||||
// Read off the injected config the same way every other value in this file is (see the note
|
||||
// above cfg) — a bare default here would make two embeds on one page answer each other's
|
||||
// poster events.
|
||||
const instanceId = computed(() => cfg.value.instanceId || 'regions-list')
|
||||
const posterAssetsBase = computed(() => resolveAssetsBase(cfg.value))
|
||||
|
||||
// ★ THE PRINT ROWS, AND THE SHEET DRAWN FROM THEM. Supplying `records` is what makes the Prayer
|
||||
// List, Card Deck and Prayer Packet buttons appear on each country (PosterWall hides them when a
|
||||
// wall cannot supply rows, rather than failing at click time). One fetch per language, cached in
|
||||
// the provider and shared by every country in every region, so opening five regions is still one
|
||||
// request. The same provider feeds the sheet, so the printed map and the printed list can never
|
||||
// disagree about which groups are in a country.
|
||||
const countryRecords = createPeopleGroupRecords({
|
||||
apiBase: () => prayBaseUrl.value,
|
||||
lang: () => languageCode.value,
|
||||
profileConfig: injectedCfg,
|
||||
const sectionAttrs = (region) => ({
|
||||
type: 'country',
|
||||
scope: regionKey(region.value || region.label),
|
||||
locale: i18nLocale.value || languageCode.value || 'en',
|
||||
collapsible: 'false',
|
||||
columns: '2',
|
||||
// Two posters a row on a phone too, as this list has always shown them: 150 px lets two cards fit at 390.
|
||||
'min-card-px': '150',
|
||||
'assets-base': posterAssetsBase.value,
|
||||
// The same people-groups service this list reads its regions from, so a card's list and deck match it.
|
||||
'api-base-url': prayBaseUrl.value,
|
||||
})
|
||||
// Page 1 of a country packet, and the country poster, drawn at the click. Nothing is stored: no
|
||||
// country sheet ships, and the packet's default lift would ask for posters/country-<iso3>.html and
|
||||
// 404 on all 175.
|
||||
const sheetForRecord = makeCountrySheet({
|
||||
records: countryRecords,
|
||||
assetsBase: () => posterAssetsBase.value,
|
||||
// The same list the Country Posters wall computes its series from, so this tab's printed
|
||||
// "map N of M" agrees with the wall's print and with the country's thumbnail.
|
||||
countries: posterData.countries || [],
|
||||
})
|
||||
// ★ THE POSTER IS ENGLISH BY RECORDED DECISION (series.json english_only_note), so the generator gets
|
||||
// the DATA's own record, never the card: the card's label is localised (localisedPoster below), and
|
||||
// handing it over printed "Pray for السودان" beside an English "Countries: Sudan" in Arabic. The
|
||||
// Country Posters wall resolves the record by id the same way (country-poster-wall.vue, byId).
|
||||
const POSTER_BY_ID = new Map((posterData.countries || []).map(c => [c.id, c]))
|
||||
const countrySheet = (card, format) => sheetForRecord(POSTER_BY_ID.get(card && card.id) || card, format)
|
||||
const openPosters = ref(new Set())
|
||||
|
||||
function togglePosters(region) {
|
||||
|
|
@ -323,74 +304,17 @@ function togglePosters(region) {
|
|||
}
|
||||
const postersOpen = (region) => openPosters.value.has(region.slug)
|
||||
|
||||
// The poster dataset is keyed by ISO-3 (`iso3`); the regions rollup keys by `code`. Same
|
||||
// codes, so the join is direct.
|
||||
const POSTER_BY_ISO3 = new Map((posterData.countries || []).map(c => [String(c.iso3 || '').toUpperCase(), c]))
|
||||
|
||||
// Every country in the region gets a card, in PosterWall's own shape. A country with no
|
||||
// generated poster is NOT dropped — it is passed with a reason, which is what the wall's
|
||||
// `unsupported` slot is for, so the grid never silently loses a country.
|
||||
// ★ EACH CARD NAMED IN THE READER'S LANGUAGE. item.label is the card's accessible name, the print
|
||||
// HUD's title, and the title on the printed list, deck and packet — and country-posters.json carries
|
||||
// it in English only, so every card was English in every locale. Same fix as the country-posters
|
||||
// wall, and the same two caveats travel with it:
|
||||
// · ENGLISH KEEPS THE DATA'S OWN NAMES. Intl's English differs from them for 32 of these 175
|
||||
// countries, and countryName.js records that swap as a product decision rather than a refactor.
|
||||
// · THE SHEET GENERATOR STILL GETS THE ORIGINAL RECORD. The poster itself is English by recorded
|
||||
// decision, so only the card's label is localised — postersFor's result feeds the wall, while
|
||||
// countrySheet (above) swaps the card back for the json's own record, by id, before generating.
|
||||
// The i18n locale REF is read, not a snapshot, so a runtime language switch renames the cards.
|
||||
function localisedPoster(rec) {
|
||||
const locale = i18nLocale.value || 'en'
|
||||
if (/^en\b/i.test(locale)) return rec
|
||||
const name = countryName(rec.iso3, locale)
|
||||
return name && name !== rec.iso3 ? { ...rec, label: name } : rec
|
||||
}
|
||||
|
||||
function postersFor(region) {
|
||||
return (region.countries || []).map((c) => {
|
||||
const rec = POSTER_BY_ISO3.get(String(c.code || '').toUpperCase())
|
||||
if (rec) return localisedPoster(rec)
|
||||
return {
|
||||
id: `country-${String(c.code || '').toLowerCase()}`,
|
||||
iso3: c.code,
|
||||
label: c.name,
|
||||
count: c.count,
|
||||
unsupportedReason: t('No poster has been generated for this country yet.'),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ★ THE REASON IS TRANSLATED, THE PRESENCE OF ONE IS STILL THE DATA'S. The record carries its own
|
||||
// English note ("India has 855 people groups — too many…"), and returning it put one untranslated
|
||||
// sentence on the wall in every locale. The record still DECIDES whether a country is unsupported;
|
||||
// only the wording comes from the catalogue now. messages.countryPosterTooMany exists in all
|
||||
// eleven, and needs no number in it: the card already shows the count directly above.
|
||||
// India is the only country in the dataset carrying one (855 people groups, no sheet); China is
|
||||
// fine at 3x4. My own not-yet reason, for a country with no record at all, is already through t().
|
||||
// ★ A COUNTRY TOO LARGE FOR PAPER (countrySheet.js's LARGE_SHEETS; India) prints as one large sheet through the same
|
||||
// makeCountrySheet, so its record's "too many" note no longer makes it unsupported here. It is DESKTOP-ONLY: on a phone
|
||||
// its card says so and offers no rows, rather than failing (the Country Posters wall applies the same rule, same table).
|
||||
const ON_PHONE = isPhonePrint()
|
||||
const phoneBlocked = (c) => ON_PHONE && Boolean(largeSheet(c)?.desktopOnly)
|
||||
const posterUnsupported = (c) => (sheetWithheld(c) ? tKey('messages.countryPosterTooMany') : phoneBlocked(c) ? tKey('poster.phoneLimitAll')
|
||||
: c.unsupportedReason && !largeSheet(c) ? tKey('messages.countryPosterTooMany') : null)
|
||||
// A blocked card's rows REJECT with the notice, never [] ("no people groups" on a card that has hundreds).
|
||||
// A withheld whole-country poster (countrySheet.js WITHHELD: India) withholds its list, packet and cards too.
|
||||
const posterRows = (item) => (sheetWithheld(item) ? Promise.reject(keyedError('messages.countryPosterTooMany', 'Too many people groups to fit on one poster legibly — a solution is in progress.')) : phoneBlocked(item) ? Promise.reject(keyedError('poster.phoneLimitAll', 'This needs a desktop or laptop computer.')) : countryRecords({ type: 'country', values: [item.iso3] }))
|
||||
|
||||
// One region's wall is already inside a region section, so it needs no second level of
|
||||
// collapsing and no grouping: everything in it is that one region.
|
||||
const posterGroupBy = () => 'countries'
|
||||
|
||||
// PosterWall's "Show on map" dispatches a window CustomEvent carrying listId (the poster id,
|
||||
// A poster card's "Show on map" dispatches a window CustomEvent carrying listId (the poster id,
|
||||
// e.g. "country-chn") or null when the user toggles the same card off. Mapping that id back to
|
||||
// the country record and reusing selectCountry() is what makes the button open the dashboard
|
||||
// tab for that country: the requirement is that the view-on-map button opens the country
|
||||
// dashboard page for that country.
|
||||
// The library's poster sections name themselves 'poster-section-country-<region>' (PosterSectionView.vue). In the
|
||||
// Regions composite this list is always mounted, so it also answers the third view's sections (Country Posters):
|
||||
// ONE handler for both, which keeps the region context in the selection.
|
||||
function onPosterShowOnMap(ev) {
|
||||
const d = ev?.detail || {}
|
||||
if (!String(d.instanceId || '').startsWith(instanceId.value)) return
|
||||
if (!String(d.instanceId || '').startsWith('poster-section-country-')) return
|
||||
if (!d.listId) return // a deselect is not a navigation
|
||||
const iso3 = String(d.listId).replace(/^country-/, '').toUpperCase()
|
||||
for (const region of regions.value) {
|
||||
|
|
@ -407,8 +331,8 @@ function onPosterShowOnMap(ev) {
|
|||
// already had this watcher (line 541); this tab was simply missed.
|
||||
watch(languageCode, () => { loadRegions() })
|
||||
|
||||
onMounted(() => { if (typeof window !== 'undefined') window.addEventListener(POSTER_EVENT, onPosterShowOnMap) })
|
||||
onBeforeUnmount(() => { if (typeof window !== 'undefined') window.removeEventListener(POSTER_EVENT, onPosterShowOnMap) })
|
||||
onMounted(() => { if (typeof window !== 'undefined') window.addEventListener(SECTION_EVENT, onPosterShowOnMap) })
|
||||
onBeforeUnmount(() => { if (typeof window !== 'undefined') window.removeEventListener(SECTION_EVENT, onPosterShowOnMap) })
|
||||
|
||||
onMounted(loadRegions)
|
||||
|
||||
|
|
@ -583,32 +507,7 @@ useShadowStyles(
|
|||
@select-country="(c) => selectCountry(c, region)"
|
||||
>
|
||||
<template #extra>
|
||||
<PosterWall
|
||||
:items="postersFor(region)"
|
||||
:group-by="posterGroupBy"
|
||||
:instance-id="`${instanceId}-${region.slug}`"
|
||||
:assets-base="posterAssetsBase"
|
||||
:event-name="POSTER_EVENT"
|
||||
:heading="''"
|
||||
:intro="''"
|
||||
unit="UPG"
|
||||
:item-noun="t('Country')"
|
||||
:min-card-px="150"
|
||||
:item-noun-plural="t('Countries')"
|
||||
item-count-key="regions.countryCount"
|
||||
:records="countryRecords ? posterRows : null"
|
||||
:sheet="countrySheet"
|
||||
:card-stocks="true"
|
||||
:collapsible="false"
|
||||
:embedded="true"
|
||||
:section-heads="false"
|
||||
:columns="2"
|
||||
:columns-control="true"
|
||||
:unsupported="posterUnsupported"
|
||||
download-prefix="doxa-country"
|
||||
poster-action="print"
|
||||
:prayer-list-soon-message="t('Coming soon — a prayer list will be generated for each country, naming every unengaged people group within it.')"
|
||||
/>
|
||||
<dxm-poster-section v-bind="sectionAttrs(region)" />
|
||||
</template>
|
||||
</RegionSectionCard>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
* listens for, and scrolls to tab 2. That is the same seam upg-100-mfe uses for the poster
|
||||
* wall's 'show-on-map', and it keeps each pane under its own ProfileLoader/Pinia.
|
||||
*/
|
||||
import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ref, computed, inject, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import { usePaneConfig } from '@map/composables/usePaneConfig.js'
|
||||
import { buildPaneColumn, PANE_GRID_STYLES } from '@map/layouts/pane-wrapper/pane-wrapper.js'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
|
|
@ -47,8 +47,11 @@ import MapInfoButton from '@map/components/map-controls/MapInfoButton.vue'
|
|||
// them to 97 and fail the gate. The moment someone adds these three strings to all locales,
|
||||
// the strip localises with no code change here.
|
||||
import { useRegionsI18n } from '@map/i18n/useRegionsI18n.js'
|
||||
// The third view's label is the gallery's own tab name for country posters (tabs.countryPosters, all eleven locales).
|
||||
import { useOptionalT } from '@map/composables/useOptionalT.js'
|
||||
|
||||
const { t } = useRegionsI18n()
|
||||
const tKey = useOptionalT('')
|
||||
const cfg = inject('profileConfig', ref(null))
|
||||
const uiStore = inject('uiStore', null)
|
||||
const isDark = computed(() => !!uiStore?.isDark)
|
||||
|
|
@ -70,6 +73,8 @@ const spec = computed(() => ({
|
|||
panes: [
|
||||
{ id: 'list', label: t('Regions List'), at: [0, 0], config: { profile: 'regions-list' } },
|
||||
{ id: 'dashboard', label: t('Region Dashboards'), at: [1, 0], config: { profile: 'region-dashboards' } },
|
||||
// THE THIRD VIEW: country posters, one collapsible section per region (the library's <dxm-poster-section>).
|
||||
{ id: 'posters', label: tKey('tabs.countryPosters'), at: [2, 0], config: { profile: 'country-poster-sections' } },
|
||||
],
|
||||
// The View switcher lives in this element's own header, so the layout's "… below ↓" pill
|
||||
// would be a second, redundant control (same call upg-100-mfe makes).
|
||||
|
|
@ -146,6 +151,13 @@ function go(id) {
|
|||
column?.scrollTo(id)
|
||||
}
|
||||
|
||||
// On a phone the three tabs scroll sideways (see the 600px rule below): keep the current one in view.
|
||||
const hdr = ref(null)
|
||||
watch(view, () => nextTick(() => {
|
||||
const b = hdr.value && hdr.value.querySelector('.seg button[aria-pressed="true"]')
|
||||
if (b && b.scrollIntoView) b.scrollIntoView({ inline: 'center', block: 'nearest' })
|
||||
}))
|
||||
|
||||
// ── tab 1 -> tab 2: forward the selection, then swap the visible pane ───────
|
||||
const SELECT_EVENT = 'doxa-regions-dashboard:select'
|
||||
|
||||
|
|
@ -169,6 +181,10 @@ function onCountrySelected(e) {
|
|||
forwardSelection({ regionSlug: d.region?.slug || '', countrySlug: d.slug || '' })
|
||||
}
|
||||
|
||||
// A card's "Show on map" in the third view (Country Posters) opens that country's dashboard: the Regions list pane
|
||||
// (always mounted here) answers every poster section's event with its own selectCountry(), which arrives above as
|
||||
// 'country-selected'. One handler for both views (regions-list.vue onPosterShowOnMap).
|
||||
|
||||
onMounted(() => {
|
||||
if (!stage.value) return
|
||||
column = buildPaneColumn(spec.value, { host: stage.value })
|
||||
|
|
@ -234,6 +250,14 @@ useShadowStyles(SHADOW_BOX_SIZING_RESET + PANE_GRID_STYLES + '\n' +
|
|||
// ON A PHONE the centred view switcher (about 290 px at 390) leaves no room for two buttons at one end, so the
|
||||
// ⓘ takes the header's START edge, where the floating ⓘ sat before; Share keeps the end.
|
||||
'@media (max-width: 600px){ .rmfe .hdr .hdr-btns > :first-child{ position:absolute; inset-inline-start:12px; top:50%; transform:translateY(-50%); } }' +
|
||||
// THREE VIEWS DO NOT FIT A PHONE'S HEADER (measured at 390: the tabs need about 390 px and ran under the corner
|
||||
// buttons). The switcher keeps the room between the two 36 px buttons (12 + 36 + 8 on each side) and its tab row
|
||||
// scrolls sideways, every label whole; the current tab is scrolled into view on each change. A fieldset is
|
||||
// min-content wide by default, hence min-width:0. Desktop is unchanged.
|
||||
'@media (max-width: 600px){ .rmfe .hdr .viewsel{ min-width:0; max-width:calc(100% - 112px); }' +
|
||||
' .rmfe .hdr .viewsel .seg{ overflow-x:auto; scrollbar-width:none; scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; }' +
|
||||
' .rmfe .hdr .viewsel .seg::-webkit-scrollbar{ display:none; }' +
|
||||
' .rmfe .hdr .viewsel .seg button{ flex:0 0 auto; scroll-snap-align:center; } }' +
|
||||
// Stage: the pane-wrapper column mounts here (its .pane-col is absolute).
|
||||
'.rmfe .stage{ position:relative; flex:1 1 auto; min-height:0; }' +
|
||||
'.pane > .pane-mount > *{ position:absolute; inset:0; width:100%; height:100%; display:block; }',
|
||||
|
|
@ -243,7 +267,7 @@ useShadowStyles(SHADOW_BOX_SIZING_RESET + PANE_GRID_STYLES + '\n' +
|
|||
|
||||
<template>
|
||||
<div class="rmfe">
|
||||
<header class="hdr">
|
||||
<header ref="hdr" class="hdr">
|
||||
<ViewSelect
|
||||
:views="spec.panes"
|
||||
:current="view"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This folder is GENERATED by `bun run build`; do not edit it by hand. It holds exactly one thing:
|
||||
|
||||
- **`dx-maps/`** — the output folder: 28 plugin bundles and 43 pages, self-contained.
|
||||
- **`dx-maps/`** — the output folder: 29 plugin bundles and 45 pages, self-contained.
|
||||
Copy this one folder to any web host (or open it from disk) and it works.
|
||||
- `dx-maps/index.html` — the front page: the Plugin Studio, every plugin in one place.
|
||||
- `dx-maps/<plugin>/<plugin>.js` — a plugin's bundle (a custom element; load it with a `<script>` tag).
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -76,7 +76,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <all-people-groups> custom element. -->
|
||||
<script src="../all-people-groups.js" integrity="sha384-azOGRGFfV4YljIIGPS+n31IinoZXoivnjQs9/A0AlRbGd1cLAKuUWSoc9h+tcxdl" crossorigin="anonymous"></script>
|
||||
<script src="../all-people-groups.js" integrity="sha384-LMy0vhHZstVM6y2VkOkv/qjy4f13oCHJ7Vk8kDH87Il73sqGL3UlqFh/rq1JEr0h" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <bible-posters> custom element. -->
|
||||
<script src="../bible-posters.js" integrity="sha384-OGqkjjOZzn9dtgXe/HX3oz2Q6zhDtZYMfwJY4EVk90HE5UcVT/nDfSXhykzdUeq0" crossorigin="anonymous"></script>
|
||||
<script src="../bible-posters.js" integrity="sha384-whAYGz/in0MdgL9cy+hRRvIcGnKWDJaZ9ctpDzva7xiwCirUmiwRVmMOJmDKYm1a" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
|
||||
<script src="../cities-internet.js" integrity="sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg" crossorigin="anonymous"></script>
|
||||
<script src="../cities-internet.js" integrity="sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
|
||||
<script src="../cities-internet.js" integrity="sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg" crossorigin="anonymous"></script>
|
||||
<script src="../cities-internet.js" integrity="sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <cities-internet-map> custom element. -->
|
||||
<script src="../cities-internet.js" integrity="sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg" crossorigin="anonymous"></script>
|
||||
<script src="../cities-internet.js" integrity="sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -76,7 +76,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-country-map> custom element. -->
|
||||
<script src="../country-map.js" integrity="sha384-bDKYWX2YaqxpdzficmYeXsj+sGnAFF876sB3ESz1AxLDCN/yE+7uN+qikcJCigXa" crossorigin="anonymous"></script>
|
||||
<script src="../country-map.js" integrity="sha384-vvDqMtWQyDXWHXFK//1n/bnh8iCT4bMh8C+FQk7memFFM1gDEj8aMWl4XGj4WhZx" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -76,7 +76,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-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,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-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -82,7 +82,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-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -67,7 +67,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-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -76,7 +76,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <demo-map> custom element. -->
|
||||
<script src="../demo-map.js" integrity="sha384-XU/yXglx4Zu1uoGhqn+NYW83lB8b4fe3BwDSH7jrfvmuCC9RzXJcECLP1gcNIGTq" crossorigin="anonymous"></script>
|
||||
<script src="../demo-map.js" integrity="sha384-gF/7Qipig6bZEohT/dCvqgnLCtUvyBvwAljPPMGB0N48aqGgHe9ouNsl56WrUXFA" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <demo-map> custom element. -->
|
||||
<script src="../../demo-map.js" integrity="sha384-XU/yXglx4Zu1uoGhqn+NYW83lB8b4fe3BwDSH7jrfvmuCC9RzXJcECLP1gcNIGTq" crossorigin="anonymous"></script>
|
||||
<script src="../../demo-map.js" integrity="sha384-gF/7Qipig6bZEohT/dCvqgnLCtUvyBvwAljPPMGB0N48aqGgHe9ouNsl56WrUXFA" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-maps-spa> custom element. -->
|
||||
<script src="../../doxa-maps-spa.js" integrity="sha384-NTW8JAlFhr7toBjxKiN0S0jympiM1em5jx+0Y156CA/ef3ABAruQzJOctvsvFtBa" crossorigin="anonymous"></script>
|
||||
<script src="../../doxa-maps-spa.js" integrity="sha384-D/MCvAZhvdZ4CaVcpxKTxFLdT9GEuHidcKwBImnHSQfvUJg6mkAae/Y5JeUQYUjc" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-maps-spa> custom element. -->
|
||||
<script src="../doxa-maps-spa.js" integrity="sha384-NTW8JAlFhr7toBjxKiN0S0jympiM1em5jx+0Y156CA/ef3ABAruQzJOctvsvFtBa" crossorigin="anonymous"></script>
|
||||
<script src="../doxa-maps-spa.js" integrity="sha384-D/MCvAZhvdZ4CaVcpxKTxFLdT9GEuHidcKwBImnHSQfvUJg6mkAae/Y5JeUQYUjc" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
"schema": "doxa-maps-manifest@1",
|
||||
"generatedFrom": "plugin-designs/*/*.vue",
|
||||
"embedBase": ".",
|
||||
"bundleCount": 28,
|
||||
"profileCount": 43,
|
||||
"bundleCount": 29,
|
||||
"profileCount": 45,
|
||||
"runtime": {
|
||||
"apiBaseUrl": "https://pray.doxa.life"
|
||||
},
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
"bundle": "all-people-groups",
|
||||
"scriptUrl": "./all-people-groups/all-people-groups.js",
|
||||
"output": "dx-maps/all-people-groups/all-people-groups.js",
|
||||
"integrity": "sha384-azOGRGFfV4YljIIGPS+n31IinoZXoivnjQs9/A0AlRbGd1cLAKuUWSoc9h+tcxdl",
|
||||
"integrity": "sha384-LMy0vhHZstVM6y2VkOkv/qjy4f13oCHJ7Vk8kDH87Il73sqGL3UlqFh/rq1JEr0h",
|
||||
"elements": [
|
||||
"all-people-groups"
|
||||
],
|
||||
|
|
@ -30,8 +30,8 @@
|
|||
"scriptUrl": "./all-people-groups/all-people-groups.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-azOGRGFfV4YljIIGPS+n31IinoZXoivnjQs9/A0AlRbGd1cLAKuUWSoc9h+tcxdl\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-azOGRGFfV4YljIIGPS+n31IinoZXoivnjQs9/A0AlRbGd1cLAKuUWSoc9h+tcxdl\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-LMy0vhHZstVM6y2VkOkv/qjy4f13oCHJ7Vk8kDH87Il73sqGL3UlqFh/rq1JEr0h\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-LMy0vhHZstVM6y2VkOkv/qjy4f13oCHJ7Vk8kDH87Il73sqGL3UlqFh/rq1JEr0h\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<all-people-groups profile-config='{\"profile\":\"all-people-groups\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></all-people-groups>"
|
||||
},
|
||||
"bundle": "all-people-groups"
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
"bundle": "bible-posters",
|
||||
"scriptUrl": "./bible-posters/bible-posters.js",
|
||||
"output": "dx-maps/bible-posters/bible-posters.js",
|
||||
"integrity": "sha384-OGqkjjOZzn9dtgXe/HX3oz2Q6zhDtZYMfwJY4EVk90HE5UcVT/nDfSXhykzdUeq0",
|
||||
"integrity": "sha384-whAYGz/in0MdgL9cy+hRRvIcGnKWDJaZ9ctpDzva7xiwCirUmiwRVmMOJmDKYm1a",
|
||||
"elements": [
|
||||
"bible-posters"
|
||||
],
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
"scriptUrl": "./bible-posters/bible-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./bible-posters/bible-posters/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=\"./bible-posters/bible-posters.js\" integrity=\"sha384-OGqkjjOZzn9dtgXe/HX3oz2Q6zhDtZYMfwJY4EVk90HE5UcVT/nDfSXhykzdUeq0\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<bible-posters profile-config='{\"profile\":\"bible-posters\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></bible-posters>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./bible-posters/bible-posters.js\" integrity=\"sha384-whAYGz/in0MdgL9cy+hRRvIcGnKWDJaZ9ctpDzva7xiwCirUmiwRVmMOJmDKYm1a\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<bible-posters profile-config='{\"profile\":\"bible-posters\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></bible-posters>"
|
||||
},
|
||||
"bundle": "bible-posters"
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@
|
|||
"bundle": "cities-internet",
|
||||
"scriptUrl": "./cities-internet/cities-internet.js",
|
||||
"output": "dx-maps/cities-internet/cities-internet.js",
|
||||
"integrity": "sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg",
|
||||
"integrity": "sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5",
|
||||
"elements": [
|
||||
"cities-internet-map"
|
||||
],
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
"scriptUrl": "./cities-internet/cities-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./cities-internet/connectivity-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<cities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -177,8 +177,8 @@
|
|||
"scriptUrl": "./cities-internet/cities-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<cities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -194,7 +194,7 @@
|
|||
"scriptUrl": "./cities-internet/cities-internet.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./cities-internet/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-xspDgonifrR1RvyShKSvbO3Xf+UnlYs4Yrw+qhFLa0PrTWFv9VVOS4XrRp0Q5BCg\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-/kNcRxqLX/p7+cFvQ7Ft1uhwBYfW28D82LEHcvRBZM+TvTIKvZ12R60ujHB4v4/5\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<cities-internet-map profile-config='{\"profile\":\"data-tables\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></cities-internet-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -203,7 +203,7 @@
|
|||
"bundle": "country-map",
|
||||
"scriptUrl": "./country-map/country-map.js",
|
||||
"output": "dx-maps/country-map/country-map.js",
|
||||
"integrity": "sha384-bDKYWX2YaqxpdzficmYeXsj+sGnAFF876sB3ESz1AxLDCN/yE+7uN+qikcJCigXa",
|
||||
"integrity": "sha384-vvDqMtWQyDXWHXFK//1n/bnh8iCT4bMh8C+FQk7memFFM1gDEj8aMWl4XGj4WhZx",
|
||||
"elements": [
|
||||
"doxa-country-map"
|
||||
],
|
||||
|
|
@ -222,8 +222,8 @@
|
|||
"scriptUrl": "./country-map/country-map.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-map/country-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-map/country-map.js\" integrity=\"sha384-bDKYWX2YaqxpdzficmYeXsj+sGnAFF876sB3ESz1AxLDCN/yE+7uN+qikcJCigXa\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-country-map profile-config='{\"profile\":\"country-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-country-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-map/country-map.js\" integrity=\"sha384-bDKYWX2YaqxpdzficmYeXsj+sGnAFF876sB3ESz1AxLDCN/yE+7uN+qikcJCigXa\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-country-map profile-config='{\"profile\":\"country-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-country-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-map/country-map.js\" integrity=\"sha384-vvDqMtWQyDXWHXFK//1n/bnh8iCT4bMh8C+FQk7memFFM1gDEj8aMWl4XGj4WhZx\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-country-map profile-config='{\"profile\":\"country-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-country-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-map/country-map.js\" integrity=\"sha384-vvDqMtWQyDXWHXFK//1n/bnh8iCT4bMh8C+FQk7memFFM1gDEj8aMWl4XGj4WhZx\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-country-map profile-config='{\"profile\":\"country-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-country-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -232,7 +232,7 @@
|
|||
"bundle": "country-posters",
|
||||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"output": "dx-maps/country-posters/country-posters.js",
|
||||
"integrity": "sha384-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+",
|
||||
"integrity": "sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx",
|
||||
"elements": [
|
||||
"country-posters-map"
|
||||
],
|
||||
|
|
@ -255,8 +255,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -272,7 +272,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-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"provider\":\"maplibre\",\"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-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -288,8 +288,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -305,7 +305,7 @@
|
|||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./country-posters/poster-section/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-wo556zVsMm0BC5dqD9Grf/T03snHhZwLb7V61TjczWpRiGBE7tSEyCXkfrIs+zC+\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"poster-section\",\"provider\":\"maplibre\",\"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-ofbrnL5Hde62XavrZ24bxcy+1y2dXo7wKGGbgl9X2i4xrll32I2NktgiP0YO5nPx\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<country-posters-map profile-config='{\"profile\":\"poster-section\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -363,7 +363,7 @@
|
|||
"bundle": "demo-map",
|
||||
"scriptUrl": "./demo-map/demo-map.js",
|
||||
"output": "dx-maps/demo-map/demo-map.js",
|
||||
"integrity": "sha384-XU/yXglx4Zu1uoGhqn+NYW83lB8b4fe3BwDSH7jrfvmuCC9RzXJcECLP1gcNIGTq",
|
||||
"integrity": "sha384-gF/7Qipig6bZEohT/dCvqgnLCtUvyBvwAljPPMGB0N48aqGgHe9ouNsl56WrUXFA",
|
||||
"elements": [
|
||||
"demo-map"
|
||||
],
|
||||
|
|
@ -381,8 +381,8 @@
|
|||
"scriptUrl": "./demo-map/demo-map.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-XU/yXglx4Zu1uoGhqn+NYW83lB8b4fe3BwDSH7jrfvmuCC9RzXJcECLP1gcNIGTq\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<demo-map profile-config='{\"profile\":\"demo-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-XU/yXglx4Zu1uoGhqn+NYW83lB8b4fe3BwDSH7jrfvmuCC9RzXJcECLP1gcNIGTq\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<demo-map profile-config='{\"profile\":\"demo-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-gF/7Qipig6bZEohT/dCvqgnLCtUvyBvwAljPPMGB0N48aqGgHe9ouNsl56WrUXFA\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<demo-map profile-config='{\"profile\":\"demo-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./demo-map/demo-map.js\" integrity=\"sha384-gF/7Qipig6bZEohT/dCvqgnLCtUvyBvwAljPPMGB0N48aqGgHe9ouNsl56WrUXFA\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<demo-map profile-config='{\"profile\":\"demo-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></demo-map>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -409,7 +409,7 @@
|
|||
"bundle": "doxa-maps-spa",
|
||||
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
|
||||
"output": "dx-maps/doxa-maps-spa/doxa-maps-spa.js",
|
||||
"integrity": "sha384-NTW8JAlFhr7toBjxKiN0S0jympiM1em5jx+0Y156CA/ef3ABAruQzJOctvsvFtBa",
|
||||
"integrity": "sha384-D/MCvAZhvdZ4CaVcpxKTxFLdT9GEuHidcKwBImnHSQfvUJg6mkAae/Y5JeUQYUjc",
|
||||
"elements": [
|
||||
"doxa-maps-spa"
|
||||
],
|
||||
|
|
@ -439,7 +439,7 @@
|
|||
"scriptUrl": "./doxa-maps-spa/doxa-maps-spa.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-NTW8JAlFhr7toBjxKiN0S0jympiM1em5jx+0Y156CA/ef3ABAruQzJOctvsvFtBa\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-D/MCvAZhvdZ4CaVcpxKTxFLdT9GEuHidcKwBImnHSQfvUJg6mkAae/Y5JeUQYUjc\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<doxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-maps-spa>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -466,7 +466,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-xhNGGZM8YUVM8TGVod952SI1tWsOvMYPhLJgYo9/DC5BBpdq2dTR97b/kYyByxJD",
|
||||
"integrity": "sha384-WeOQrnujUZ4A942d6Y3k8d2TxlVowsYxSrjxoD2XOTJtTUX0c2C4ntp6oi5wQDN4",
|
||||
"elements": [
|
||||
"doxa-research-map"
|
||||
],
|
||||
|
|
@ -485,8 +485,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" 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-xhNGGZM8YUVM8TGVod952SI1tWsOvMYPhLJgYo9/DC5BBpdq2dTR97b/kYyByxJD\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" 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-xhNGGZM8YUVM8TGVod952SI1tWsOvMYPhLJgYo9/DC5BBpdq2dTR97b/kYyByxJD\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" 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-WeOQrnujUZ4A942d6Y3k8d2TxlVowsYxSrjxoD2XOTJtTUX0c2C4ntp6oi5wQDN4\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" 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-WeOQrnujUZ4A942d6Y3k8d2TxlVowsYxSrjxoD2XOTJtTUX0c2C4ntp6oi5wQDN4\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -512,7 +512,7 @@
|
|||
"bundle": "doxa-simple-map",
|
||||
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
|
||||
"output": "dx-maps/doxa-simple-map/doxa-simple-map.js",
|
||||
"integrity": "sha384-VoHBFqN672lP983lYAasMtWIQlNdfLuCxOR0W6B82vioEVAeA0w6ZwGfBl+QK3eA",
|
||||
"integrity": "sha384-jk+quEe8NnQEMtsEM71OE5IquBg4vpRktGEQueboxU0+/HQimXSbsyCeUrs/Rzmu",
|
||||
"elements": [
|
||||
"dxm-simple-map"
|
||||
],
|
||||
|
|
@ -531,8 +531,8 @@
|
|||
"scriptUrl": "./doxa-simple-map/doxa-simple-map.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-VoHBFqN672lP983lYAasMtWIQlNdfLuCxOR0W6B82vioEVAeA0w6ZwGfBl+QK3eA\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<dxm-simple-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dxm-simple-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-VoHBFqN672lP983lYAasMtWIQlNdfLuCxOR0W6B82vioEVAeA0w6ZwGfBl+QK3eA\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<dxm-simple-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dxm-simple-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-jk+quEe8NnQEMtsEM71OE5IquBg4vpRktGEQueboxU0+/HQimXSbsyCeUrs/Rzmu\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<dxm-simple-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dxm-simple-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-jk+quEe8NnQEMtsEM71OE5IquBg4vpRktGEQueboxU0+/HQimXSbsyCeUrs/Rzmu\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<dxm-simple-map profile-config='{\"profile\":\"doxa-simple-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></dxm-simple-map>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -702,7 +702,7 @@
|
|||
"bundle": "gospel-internet",
|
||||
"scriptUrl": "./gospel-internet/gospel-internet.js",
|
||||
"output": "dx-maps/gospel-internet/gospel-internet.js",
|
||||
"integrity": "sha384-knR+PgAzEpTX/xXwqL5jYH28VAvVfI0dPutaRKsL4OMKQYKYrDmqJR2DbNIdmv8E",
|
||||
"integrity": "sha384-AXN+leNDnr8KW7/fl1dw+mOyq25ehWuYS+FZgvD8tcaY9eo8ju0X17oecvQ9BQ16",
|
||||
"elements": [
|
||||
"gospel-internet-board"
|
||||
],
|
||||
|
|
@ -723,8 +723,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-knR+PgAzEpTX/xXwqL5jYH28VAvVfI0dPutaRKsL4OMKQYKYrDmqJR2DbNIdmv8E\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-knR+PgAzEpTX/xXwqL5jYH28VAvVfI0dPutaRKsL4OMKQYKYrDmqJR2DbNIdmv8E\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-AXN+leNDnr8KW7/fl1dw+mOyq25ehWuYS+FZgvD8tcaY9eo8ju0X17oecvQ9BQ16\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-internet/gospel-internet.js\" integrity=\"sha384-AXN+leNDnr8KW7/fl1dw+mOyq25ehWuYS+FZgvD8tcaY9eo8ju0X17oecvQ9BQ16\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<gospel-internet-board profile-config='{\"profile\":\"board\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-internet-board>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -831,7 +831,7 @@
|
|||
"bundle": "gospel-resources",
|
||||
"scriptUrl": "./gospel-resources/gospel-resources.js",
|
||||
"output": "dx-maps/gospel-resources/gospel-resources.js",
|
||||
"integrity": "sha384-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw",
|
||||
"integrity": "sha384-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf",
|
||||
"elements": [
|
||||
"gospel-resources-map"
|
||||
],
|
||||
|
|
@ -854,7 +854,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-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"provider\":\"maplibre\",\"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-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -870,7 +870,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-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"provider\":\"maplibre\",\"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-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -886,8 +886,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -903,7 +903,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-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"provider\":\"maplibre\",\"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-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -919,8 +919,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-ZFRraAdl0MvvCH/tQ8bKfrhrDofHWCa2Y61tVPUrpniy21lI424dFYNfCeqN+wbw\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-J4FMKFTitEHYSFkIUUJus1hmXfIrvz+Z4ZpqD2iwO57WiqVFckwJeSgkJzLf0MSf\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -929,7 +929,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-wqGkce90Mk4Dsb5HfOxpamV+vhIh2/W6vtzTTi/gqub5DeZgxKgHg+XzN+36RRRT",
|
||||
"integrity": "sha384-NI+A8OCrOoKb53elAZUtoKYfH1+fT0CyF8htPQGnHKpZvJAckCPNoUBUAohAp9eR",
|
||||
"elements": [
|
||||
"my-upg-100-list-map"
|
||||
],
|
||||
|
|
@ -952,7 +952,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-wqGkce90Mk4Dsb5HfOxpamV+vhIh2/W6vtzTTi/gqub5DeZgxKgHg+XzN+36RRRT\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"provider\":\"maplibre\",\"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-NI+A8OCrOoKb53elAZUtoKYfH1+fT0CyF8htPQGnHKpZvJAckCPNoUBUAohAp9eR\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -968,8 +968,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" 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-wqGkce90Mk4Dsb5HfOxpamV+vhIh2/W6vtzTTi/gqub5DeZgxKgHg+XzN+36RRRT\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" 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-wqGkce90Mk4Dsb5HfOxpamV+vhIh2/W6vtzTTi/gqub5DeZgxKgHg+XzN+36RRRT\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" 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-NI+A8OCrOoKb53elAZUtoKYfH1+fT0CyF8htPQGnHKpZvJAckCPNoUBUAohAp9eR\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" 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-NI+A8OCrOoKb53elAZUtoKYfH1+fT0CyF8htPQGnHKpZvJAckCPNoUBUAohAp9eR\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -985,8 +985,8 @@
|
|||
"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": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" 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-wqGkce90Mk4Dsb5HfOxpamV+vhIh2/W6vtzTTi/gqub5DeZgxKgHg+XzN+36RRRT\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" 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-wqGkce90Mk4Dsb5HfOxpamV+vhIh2/W6vtzTTi/gqub5DeZgxKgHg+XzN+36RRRT\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" 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-NI+A8OCrOoKb53elAZUtoKYfH1+fT0CyF8htPQGnHKpZvJAckCPNoUBUAohAp9eR\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" 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-NI+A8OCrOoKb53elAZUtoKYfH1+fT0CyF8htPQGnHKpZvJAckCPNoUBUAohAp9eR\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<my-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></my-upg-100-list-map>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1117,7 +1117,7 @@
|
|||
"bundle": "poster-design",
|
||||
"scriptUrl": "./poster-design/poster-design.js",
|
||||
"output": "dx-maps/poster-design/poster-design.js",
|
||||
"integrity": "sha384-pROMj7g7crTFS3wvGKwBSrLfZF5t/bOWnQFUJmXTSVU3sGZiCtVNuXTFsZcdV8Bh",
|
||||
"integrity": "sha384-lnZQZgYYcsz3aFI9fwT6UBlrYcYefVSixV7CmeQvav5fgXz9m5sFyWYNHJnn96/Q",
|
||||
"elements": [
|
||||
"poster-design"
|
||||
],
|
||||
|
|
@ -1140,7 +1140,7 @@
|
|||
"scriptUrl": "./poster-design/poster-design.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-design/poster-design-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./poster-design/poster-design.js\" integrity=\"sha384-pROMj7g7crTFS3wvGKwBSrLfZF5t/bOWnQFUJmXTSVU3sGZiCtVNuXTFsZcdV8Bh\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-design profile-config='{\"profile\":\"poster-design-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-design>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./poster-design/poster-design.js\" integrity=\"sha384-lnZQZgYYcsz3aFI9fwT6UBlrYcYefVSixV7CmeQvav5fgXz9m5sFyWYNHJnn96/Q\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-design profile-config='{\"profile\":\"poster-design-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-design>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1156,7 +1156,7 @@
|
|||
"scriptUrl": "./poster-design/poster-design.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-design/poster-sizes/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=\"./poster-design/poster-design.js\" integrity=\"sha384-pROMj7g7crTFS3wvGKwBSrLfZF5t/bOWnQFUJmXTSVU3sGZiCtVNuXTFsZcdV8Bh\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-design profile-config='{\"profile\":\"poster-sizes\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-design>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./poster-design/poster-design.js\" integrity=\"sha384-lnZQZgYYcsz3aFI9fwT6UBlrYcYefVSixV7CmeQvav5fgXz9m5sFyWYNHJnn96/Q\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-design profile-config='{\"profile\":\"poster-sizes\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-design>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1172,16 +1172,180 @@
|
|||
"scriptUrl": "./poster-design/poster-design.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-design/poster-styles/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=\"./poster-design/poster-design.js\" integrity=\"sha384-pROMj7g7crTFS3wvGKwBSrLfZF5t/bOWnQFUJmXTSVU3sGZiCtVNuXTFsZcdV8Bh\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-design profile-config='{\"profile\":\"poster-styles\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-design>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./poster-design/poster-design.js\" integrity=\"sha384-lnZQZgYYcsz3aFI9fwT6UBlrYcYefVSixV7CmeQvav5fgXz9m5sFyWYNHJnn96/Q\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-design profile-config='{\"profile\":\"poster-styles\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-design>"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bundle": "poster-sections",
|
||||
"scriptUrl": "./poster-sections/poster-sections.js",
|
||||
"output": "dx-maps/poster-sections/poster-sections.js",
|
||||
"integrity": "sha384-pOuNSxS6NxmK0yMui0WNKjdjeW/3X2mKfcYo8KRfL2NRnDxOohwJc7BqJx5t9eDD",
|
||||
"elements": [
|
||||
"poster-sections"
|
||||
],
|
||||
"stage": "experimental",
|
||||
"profiles": [
|
||||
{
|
||||
"profile": "poster-sections",
|
||||
"stage": "experimental",
|
||||
"label": "Poster Sections — presets",
|
||||
"file": "poster-sections.vue",
|
||||
"description": "Poster Sections: one region's posters (or every region's), as ready-made presets with their own page and embed code; the Configure preset adds a panel of choices and Copy embed for making new ones.",
|
||||
"element": "poster-sections",
|
||||
"type": "parameterized",
|
||||
"isMap": false,
|
||||
"iframeUrl": "./poster-sections/poster-sections/index.html",
|
||||
"scriptUrl": "./poster-sections/poster-sections.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/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=\"./poster-sections/poster-sections.js\" integrity=\"sha384-pOuNSxS6NxmK0yMui0WNKjdjeW/3X2mKfcYo8KRfL2NRnDxOohwJc7BqJx5t9eDD\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<poster-sections profile-config='{\"profile\":\"poster-sections\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></poster-sections>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
"id": "countries-all",
|
||||
"title": "Country posters — all regions",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "all"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-all/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-all/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-asia",
|
||||
"title": "Country posters — Asia",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "asia"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-asia/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-asia/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-middle-east",
|
||||
"title": "Country posters — Middle East",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "middle-east"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-middle-east/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-middle-east/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-latin-america-and-caribbean",
|
||||
"title": "Country posters — Latin America & Caribbean",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "latin-america-and-caribbean"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-latin-america-and-caribbean/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-latin-america-and-caribbean/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-africa",
|
||||
"title": "Country posters — Africa",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "africa"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-africa/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-africa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-europe",
|
||||
"title": "Country posters — Europe",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "europe"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-europe/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-europe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-north-america-and-non-spanish-caribbean",
|
||||
"title": "Country posters — North America & Non-Spanish Caribbean",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "north-america-and-non-spanish-caribbean"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-north-america-and-non-spanish-caribbean/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-north-america-and-non-spanish-caribbean/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "countries-oceania",
|
||||
"title": "Country posters — Oceania",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"type": "country",
|
||||
"scope": "oceania"
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/countries-oceania/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/countries-oceania/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "configure",
|
||||
"title": "Configure — make a new preset",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "poster-sections",
|
||||
"configure": true
|
||||
},
|
||||
"element": "poster-sections",
|
||||
"iframeUrl": "./poster-sections/poster-sections/configure/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./poster-sections/poster-sections/configure/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bundle": "posterverse",
|
||||
"scriptUrl": "./posterverse/posterverse.js",
|
||||
"output": "dx-maps/posterverse/posterverse.js",
|
||||
"integrity": "sha384-ymAlpF3AiStPvnMzBJXPb3e5cBzsnNEGAlcy8Q+FwJhFrT5McyPEjPHFLjuuXWTn",
|
||||
"integrity": "sha384-Eu6xYVd6sm1N+Jmlt9wMiWk/f3TdS/KyyTmkKu6ClKdwwFzRGenJTXpLGgFxGHrV",
|
||||
"elements": [
|
||||
"posterverse-app"
|
||||
],
|
||||
|
|
@ -1207,7 +1371,7 @@
|
|||
"scriptUrl": "./posterverse/posterverse.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./posterverse/posterverse/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=\"./posterverse/posterverse.js\" integrity=\"sha384-ymAlpF3AiStPvnMzBJXPb3e5cBzsnNEGAlcy8Q+FwJhFrT5McyPEjPHFLjuuXWTn\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<posterverse-app profile-config='{\"profile\":\"posterverse\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></posterverse-app>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./posterverse/posterverse.js\" integrity=\"sha384-Eu6xYVd6sm1N+Jmlt9wMiWk/f3TdS/KyyTmkKu6ClKdwwFzRGenJTXpLGgFxGHrV\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<posterverse-app profile-config='{\"profile\":\"posterverse\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></posterverse-app>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -1234,7 +1398,7 @@
|
|||
"bundle": "pplr-data-maps",
|
||||
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
|
||||
"output": "dx-maps/pplr-data-maps/pplr-data-maps.js",
|
||||
"integrity": "sha384-KEKlNUDZ86LsVHgEwI4TPxg7JR9/5yMeCaAS3Lfudk7R8g+4RitKwlkFAF3GKyuo",
|
||||
"integrity": "sha384-m6UQFVO4zuVefJeLM+H3UYo7bLVYRbei6h6ycKRu+URUDxZZKKvxr3JsmCYAhTPp",
|
||||
"elements": [
|
||||
"pplr-data-maps"
|
||||
],
|
||||
|
|
@ -1253,8 +1417,8 @@
|
|||
"scriptUrl": "./pplr-data-maps/pplr-data-maps.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./pplr-data-maps/pplr-data-maps/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-KEKlNUDZ86LsVHgEwI4TPxg7JR9/5yMeCaAS3Lfudk7R8g+4RitKwlkFAF3GKyuo\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-KEKlNUDZ86LsVHgEwI4TPxg7JR9/5yMeCaAS3Lfudk7R8g+4RitKwlkFAF3GKyuo\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-m6UQFVO4zuVefJeLM+H3UYo7bLVYRbei6h6ycKRu+URUDxZZKKvxr3JsmCYAhTPp\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./pplr-data-maps/pplr-data-maps.js\" integrity=\"sha384-m6UQFVO4zuVefJeLM+H3UYo7bLVYRbei6h6ycKRu+URUDxZZKKvxr3JsmCYAhTPp\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<pplr-data-maps profile-config='{\"profile\":\"pplr-data-maps\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></pplr-data-maps>"
|
||||
},
|
||||
"bundle": "pplr-data-maps"
|
||||
}
|
||||
|
|
@ -1292,7 +1456,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "dx-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-i/PiEh5uiDOjqhKOJfE9t7doRPTTZA5dNciLUf975lCB7uANyS4tA6barLz9RCjx",
|
||||
"integrity": "sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -1302,6 +1466,22 @@
|
|||
],
|
||||
"stage": "production",
|
||||
"profiles": [
|
||||
{
|
||||
"profile": "country-poster-sections",
|
||||
"stage": "experimental",
|
||||
"label": "Regions — country posters by region",
|
||||
"file": "country-poster-sections.vue",
|
||||
"description": "Country posters by region: one collapsible poster section per region (the library's <dxm-poster-section>), the Regions dashboard's third view.",
|
||||
"element": "regions-dashboard",
|
||||
"type": "single",
|
||||
"isMap": false,
|
||||
"iframeUrl": "./regions-dashboard/country-poster-sections/index.html",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/country-poster-sections/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<regions-dashboard profile-config='{\"profile\":\"country-poster-sections\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"profile": "region-dashboards",
|
||||
"stage": "experimental",
|
||||
|
|
@ -1315,8 +1495,8 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-i/PiEh5uiDOjqhKOJfE9t7doRPTTZA5dNciLUf975lCB7uANyS4tA6barLz9RCjx\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-i/PiEh5uiDOjqhKOJfE9t7doRPTTZA5dNciLUf975lCB7uANyS4tA6barLz9RCjx\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1332,7 +1512,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-i/PiEh5uiDOjqhKOJfE9t7doRPTTZA5dNciLUf975lCB7uANyS4tA6barLz9RCjx\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1348,8 +1528,8 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-i/PiEh5uiDOjqhKOJfE9t7doRPTTZA5dNciLUf975lCB7uANyS4tA6barLz9RCjx\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-i/PiEh5uiDOjqhKOJfE9t7doRPTTZA5dNciLUf975lCB7uANyS4tA6barLz9RCjx\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-+h2q7ZOXJHxxyXUwa84JZF2eaG7Nob2eqdqQwVhzRn0YKnbxLqp86zrfRt1VhnDa\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1358,7 +1538,7 @@
|
|||
"bundle": "research-dashboard",
|
||||
"scriptUrl": "./research-dashboard/research-dashboard.js",
|
||||
"output": "dx-maps/research-dashboard/research-dashboard.js",
|
||||
"integrity": "sha384-PxtNbKy+sdiUCFBKjvtRKaoLrS7ZiG4dzbPTmVT+5F/3MPqWQm9/zYxbGqppQvt9",
|
||||
"integrity": "sha384-EKqww5nctwiI2nXSsbDTGjXCyFNUNEJlzjxf7UA0bVvWebLfz0MZtKwdgtOEizd3",
|
||||
"elements": [
|
||||
"research-dashboard-map"
|
||||
],
|
||||
|
|
@ -1377,8 +1557,8 @@
|
|||
"scriptUrl": "./research-dashboard/research-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-PxtNbKy+sdiUCFBKjvtRKaoLrS7ZiG4dzbPTmVT+5F/3MPqWQm9/zYxbGqppQvt9\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-PxtNbKy+sdiUCFBKjvtRKaoLrS7ZiG4dzbPTmVT+5F/3MPqWQm9/zYxbGqppQvt9\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-EKqww5nctwiI2nXSsbDTGjXCyFNUNEJlzjxf7UA0bVvWebLfz0MZtKwdgtOEizd3\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-EKqww5nctwiI2nXSsbDTGjXCyFNUNEJlzjxf7UA0bVvWebLfz0MZtKwdgtOEizd3\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<research-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-dashboard-map>"
|
||||
},
|
||||
"bundle": "research-dashboard"
|
||||
}
|
||||
|
|
@ -1483,7 +1663,7 @@
|
|||
"bundle": "upg-neighbor",
|
||||
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
|
||||
"output": "dx-maps/upg-neighbor/upg-neighbor.js",
|
||||
"integrity": "sha384-0mRH8hCnz3kVP3wOflBEZoZVQuHNPxVzsbPtQgaX0wV1/Ptalqn21WvcRLEGcEZZ",
|
||||
"integrity": "sha384-N7i9n5Dcw7KMGXurPDZzycNhSzC6ZooRTVqxE5AqI0YzQu5CM2dfRH0cMZSBThS+",
|
||||
"elements": [
|
||||
"upg-neighbor"
|
||||
],
|
||||
|
|
@ -1502,8 +1682,8 @@
|
|||
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./upg-neighbor/upg-neighbor/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-0mRH8hCnz3kVP3wOflBEZoZVQuHNPxVzsbPtQgaX0wV1/Ptalqn21WvcRLEGcEZZ\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-0mRH8hCnz3kVP3wOflBEZoZVQuHNPxVzsbPtQgaX0wV1/Ptalqn21WvcRLEGcEZZ\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
|
||||
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-N7i9n5Dcw7KMGXurPDZzycNhSzC6ZooRTVqxE5AqI0YzQu5CM2dfRH0cMZSBThS+\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>",
|
||||
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\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<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-N7i9n5Dcw7KMGXurPDZzycNhSzC6ZooRTVqxE5AqI0YzQu5CM2dfRH0cMZSBThS+\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
@ -1585,7 +1765,7 @@
|
|||
"bundle": "wall-of-people-groups",
|
||||
"scriptUrl": "./wall-of-people-groups/wall-of-people-groups.js",
|
||||
"output": "dx-maps/wall-of-people-groups/wall-of-people-groups.js",
|
||||
"integrity": "sha384-40lU8+R2SQ9XoXwif+eTYaeXZNCzO+ReuPZyhBLwx1HzWqgvO91zShZPRCpqLO+L",
|
||||
"integrity": "sha384-5oZkjMzpKJV1v4lxKhFWfZ9sJcDQgNhfph2N2zf9mjEEk+0Z1VdfBdAETO1NWcWF",
|
||||
"elements": [
|
||||
"wall-of-people-groups"
|
||||
],
|
||||
|
|
@ -1605,7 +1785,7 @@
|
|||
"scriptUrl": "./wall-of-people-groups/wall-of-people-groups.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./wall-of-people-groups/wall-of-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./wall-of-people-groups/wall-of-people-groups.js\" integrity=\"sha384-40lU8+R2SQ9XoXwif+eTYaeXZNCzO+ReuPZyhBLwx1HzWqgvO91zShZPRCpqLO+L\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<wall-of-people-groups profile-config='{\"profile\":\"wall-of-people-groups\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></wall-of-people-groups>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./wall-of-people-groups/wall-of-people-groups.js\" integrity=\"sha384-5oZkjMzpKJV1v4lxKhFWfZ9sJcDQgNhfph2N2zf9mjEEk+0Z1VdfBdAETO1NWcWF\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<wall-of-people-groups profile-config='{\"profile\":\"wall-of-people-groups\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></wall-of-people-groups>"
|
||||
},
|
||||
"bundle": "wall-of-people-groups"
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -76,7 +76,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-xhNGGZM8YUVM8TGVod952SI1tWsOvMYPhLJgYo9/DC5BBpdq2dTR97b/kYyByxJD" crossorigin="anonymous"></script>
|
||||
<script src="../doxa-research-map.js" integrity="sha384-WeOQrnujUZ4A942d6Y3k8d2TxlVowsYxSrjxoD2XOTJtTUX0c2C4ntp6oi5wQDN4" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -72,7 +72,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-xhNGGZM8YUVM8TGVod952SI1tWsOvMYPhLJgYo9/DC5BBpdq2dTR97b/kYyByxJD" crossorigin="anonymous"></script>
|
||||
<script src="../../doxa-research-map.js" integrity="sha384-WeOQrnujUZ4A942d6Y3k8d2TxlVowsYxSrjxoD2XOTJtTUX0c2C4ntp6oi5wQDN4" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -76,7 +76,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <dxm-simple-map> custom element. -->
|
||||
<script src="../../doxa-simple-map.js" integrity="sha384-VoHBFqN672lP983lYAasMtWIQlNdfLuCxOR0W6B82vioEVAeA0w6ZwGfBl+QK3eA" crossorigin="anonymous"></script>
|
||||
<script src="../../doxa-simple-map.js" integrity="sha384-jk+quEe8NnQEMtsEM71OE5IquBg4vpRktGEQueboxU0+/HQimXSbsyCeUrs/Rzmu" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue