Merge origin/development into gospel/lm-image-seat
# Conflicts: # public/dx-maps/country-posters/countries-map/index.html # public/dx-maps/country-posters/country-poster-wall/index.html # public/dx-maps/country-posters/country-posters-mfe/index.html # public/dx-maps/country-posters/country-posters.js # public/dx-maps/doxa-maps-spa/manifest.json # public/dx-maps/doxa-research-map/doxa-research-map.js # public/dx-maps/doxa-research-map/research-map/index.html # public/dx-maps/doxa-research-map/research-map/maplibre/index.html # public/dx-maps/manifest.json # public/dx-maps/my-upg-100-list/my-upg-100-list.js # public/dx-maps/my-upg-100-list/poster-wall/index.html # public/dx-maps/my-upg-100-list/upg-100-list/index.html # public/dx-maps/my-upg-100-list/upg-100-mfe/index.html
This commit is contained in:
commit
aabaff9c28
61 changed files with 1619 additions and 831 deletions
BIN
data/posters/country-ind.webp
Normal file
BIN
data/posters/country-ind.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
|
|
@ -10,6 +10,8 @@
|
|||
"check:coverage-consistent": "the full run includes clause L, the age of the oldest link verification (45 days), which depends on the CALENDAR: chained, it would turn gates red on a date (first 2026-11-08) with no code change. Run it with the link re-verification; every other clause runs in gates:pre as check:coverage-consistent:chained",
|
||||
"check:snippets-draw": "AWAITING ITS FIRST BROWSER RUN: the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded.",
|
||||
"check:grid-merge-defaults": "WAITS FOR ITS CODE: it asserts the defaults of three grid props (filtersToggle, backdrop, backdropFloor) that are not on development, because the grid merge (b1e839a33) has not landed. Chain it in gates:pre in the same landing as that merge.",
|
||||
"check:snippets-draw:mapbox": "AWAITING ITS FIRST BROWSER RUN: the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded. Needs MAP_TOKEN."
|
||||
"check:snippets-draw:mapbox": "AWAITING ITS FIRST BROWSER RUN: the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded. Needs MAP_TOKEN.",
|
||||
"check:side-lane-photo-pixels": "a BROWSER gate (browser seat only; elsewhere NOT MEASURED, 75). Its first slot run must come before it joins the browser suite (verify-browser-all lists only gates already run there); until then its source twin, check:side-lanes' photo arms, runs in gates:pre",
|
||||
"check:side-lane-photo-pixels:self-test": "the poison run of check:side-lane-photo-pixels (a raster without stripExternalImages must draw the glyph); same browser-seat reason"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -573,7 +573,11 @@ async function exportPoster(item) {
|
|||
const format = props.posterAction === 'print' ? 'letter' : (item.sheet === '3x3' ? '3x3' : '3x4')
|
||||
const html = await Promise.resolve().then(() => props.sheet(item, format)).catch(() => '')
|
||||
if (html) {
|
||||
const { rasterPoster, looksRendered } = await import('@map/packet/poster-raster.js')
|
||||
const { rasterPoster, looksRendered, hasExternalImages } = await import('@map/packet/poster-raster.js')
|
||||
// ★ A SHEET WITH PHOTOS PRINTS AS ITS OWN DOCUMENT (the large side-lane sheets): a raster cannot carry a
|
||||
// photo from its own host (poster-raster.js says why), and the sheet's own @page is its true size
|
||||
// (60in 96in), where an image of it would be shrunk onto letter. A download still rasterises, without them.
|
||||
if (props.posterAction === 'print' && hasExternalImages(html)) { await printSheetDocument(html); return }
|
||||
const blob = await rasterPoster(html, { scale: 2 })
|
||||
if (await looksRendered(blob)) { await deliverPoster(blob, item); return }
|
||||
}
|
||||
|
|
@ -802,6 +806,20 @@ function printPacketAs(item, variantId) {
|
|||
// ONE delivery point for the finished poster, so 'download' and 'print' cannot
|
||||
// drift apart or be applied to only one of the two paths (shipped .webp vs
|
||||
// browser-rasterised). Both callers in exportPoster() route through here.
|
||||
// Print a generated sheet as the document it is (photos load, the page is its own size). Same HUD steps as
|
||||
// deliverPoster's print, including the phone's Print button when the tap has expired.
|
||||
async function printSheetDocument(html) {
|
||||
exportState.value = { ...exportState.value, phase: 'save', message: hud('wall.hud.openingPrint', 'opening the print dialog'), pct: 80 }
|
||||
const { printDocument } = await import('@map/composables/printInPlace.js')
|
||||
const how = await printDocument(html, { onNeedsTap: (printNow, cancel) => {
|
||||
exportState.value = { ...exportState.value, phase: 'ready', message: hud('wall.hud.readyToPrint', 'ready to print'), pct: 100,
|
||||
action: () => { exportState.value = null; printNow() }, cancel }
|
||||
} })
|
||||
if (how === 'needs-tap') return
|
||||
exportState.value = { ...exportState.value, phase: 'done', message: hud('wall.hud.printOpened', 'print dialog opened'), pct: 100 }
|
||||
setTimeout(() => { exportState.value = null }, 900)
|
||||
}
|
||||
|
||||
async function deliverPoster(blob, item) {
|
||||
if (props.posterAction === 'print') {
|
||||
exportState.value = { ...exportState.value, phase: 'save', message: hud('wall.hud.openingPrint', 'opening the print dialog'), pct: 80 }
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "تعذّر إنشاء الملصق. حاول مرة أخرى أو اختر حجمًا أصغر.",
|
||||
"optional": "(اختياري)",
|
||||
"phoneLimit": "على الهاتف، أكبر المقاسات غير متاحة: لا يستطيع الهاتف رسم ملصق بهذا الحجم. استخدم حاسوبًا لها.",
|
||||
"phoneLimitAll": "يتطلّب هذا حاسوبًا مكتبيًا أو محمولًا.",
|
||||
"glue_here": "الصق هنا"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "Das Poster konnte nicht erstellt werden. Versuchen Sie es erneut oder wählen Sie eine kleinere Größe.",
|
||||
"optional": "(optional)",
|
||||
"phoneLimit": "Auf dem Handy sind die größten Formate ausgeschaltet: Ein Handy kann kein so großes Poster zeichnen. Nutzen Sie dafür einen Computer.",
|
||||
"phoneLimitAll": "Dafür ist ein Desktop- oder Laptop-Computer nötig.",
|
||||
"glue_here": "hier kleben"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "The poster could not be rendered. Try again, or choose a smaller size.",
|
||||
"optional": "(optional)",
|
||||
"phoneLimit": "On a phone, the largest sizes are off: a phone can't draw a poster that big. Use a computer for them.",
|
||||
"phoneLimitAll": "This needs a desktop or laptop computer.",
|
||||
"glue_here": "glue here"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "No se pudo generar el póster. Inténtalo de nuevo o elige un tamaño menor.",
|
||||
"optional": "(opcional)",
|
||||
"phoneLimit": "En un teléfono, los tamaños más grandes están desactivados: un teléfono no puede dibujar un póster tan grande. Usa un ordenador para ellos.",
|
||||
"phoneLimitAll": "Esto requiere una computadora de escritorio o portátil.",
|
||||
"glue_here": "pegar aquí"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "L’affiche n’a pas pu être générée. Réessayez ou choisissez un format plus petit.",
|
||||
"optional": "(facultatif)",
|
||||
"phoneLimit": "Sur un téléphone, les plus grands formats sont désactivés : un téléphone ne peut pas dessiner une affiche aussi grande. Utilisez un ordinateur pour ceux-là.",
|
||||
"phoneLimitAll": "Cela nécessite un ordinateur de bureau ou un portable.",
|
||||
"glue_here": "coller ici"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "पोस्टर नहीं बन सका। फिर से कोशिश करें या छोटा आकार चुनें।",
|
||||
"optional": "(वैकल्पिक)",
|
||||
"phoneLimit": "फ़ोन पर सबसे बड़े आकार बंद हैं: फ़ोन इतना बड़ा पोस्टर नहीं बना सकता। उनके लिए कंप्यूटर का उपयोग करें।",
|
||||
"phoneLimitAll": "इसके लिए डेस्कटॉप या लैपटॉप कंप्यूटर चाहिए।",
|
||||
"glue_here": "यहाँ चिपकाएँ"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "Impossibile generare il poster. Riprova o scegli un formato più piccolo.",
|
||||
"optional": "(facoltativo)",
|
||||
"phoneLimit": "Sul telefono i formati più grandi sono disattivati: un telefono non può disegnare un poster così grande. Per quelli usa un computer.",
|
||||
"phoneLimitAll": "Serve un computer desktop o portatile.",
|
||||
"glue_here": "incollare qui"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "Não foi possível gerar o pôster. Tente novamente ou escolha um tamanho menor.",
|
||||
"optional": "(opcional)",
|
||||
"phoneLimit": "No telefone, os tamanhos maiores ficam desativados: um telefone não consegue desenhar um cartaz tão grande. Use um computador para eles.",
|
||||
"phoneLimitAll": "Isto requer um computador de mesa ou portátil.",
|
||||
"glue_here": "colar aqui"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "Posterul nu a putut fi generat. Încercați din nou sau alegeți o dimensiune mai mică.",
|
||||
"optional": "(opțional)",
|
||||
"phoneLimit": "Pe telefon, cele mai mari dimensiuni sunt dezactivate: un telefon nu poate desena un poster atât de mare. Folosește un calculator pentru ele.",
|
||||
"phoneLimitAll": "Pentru aceasta este nevoie de un computer desktop sau laptop.",
|
||||
"glue_here": "lipiți aici"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "Не удалось создать плакат. Попробуйте ещё раз или выберите меньший размер.",
|
||||
"optional": "(необязательно)",
|
||||
"phoneLimit": "На телефоне самые большие размеры отключены: телефон не может нарисовать такой большой плакат. Используйте для них компьютер.",
|
||||
"phoneLimitAll": "Для этого нужен настольный компьютер или ноутбук.",
|
||||
"glue_here": "клеить здесь"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,6 @@
|
|||
"error_failed": "无法生成海报。请重试或选择较小的尺寸。",
|
||||
"optional": "(可选)",
|
||||
"phoneLimit": "在手机上,最大的尺寸已关闭:手机无法绘制这么大的海报。请使用电脑制作这些尺寸。",
|
||||
"phoneLimitAll": "这需要台式电脑或笔记本电脑。",
|
||||
"glue_here": "在此粘贴"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,34 @@ export function seriesFor(countries, item) {
|
|||
return { totalGroups: tier.reduce((a, x) => a + (x.count || 0), 0), totalLists: tier.length, index }
|
||||
}
|
||||
|
||||
/**
|
||||
* ★ COUNTRIES TOO LARGE FOR ANY PAPER SHEET print as ONE LARGE side-lane sheet instead (poster-render's 60x96 /
|
||||
* 66x66 / 36x60: every group named beside the map, crossing-free leaders). ONE source of truth for every caller (the
|
||||
* Country Posters wall, the Regions list), so one country never gets two answers. `desktopOnly`: its poster, list,
|
||||
* 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 } }
|
||||
export function largeSheet(item) { return (item && LARGE_SHEETS[String(item.iso3 || '').toUpperCase()]) || null }
|
||||
// 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) {
|
||||
const big = largeSheet(item)
|
||||
if (!big || format === 'packet') return format
|
||||
return big.formats.includes(format) ? format : big.format
|
||||
}
|
||||
|
||||
// The facts row's "Administrative Areas" cell for a large-sheet country whose divisions we know (India: its states and
|
||||
// union territories, counted from the shared state table). English, like the rest of the sheet.
|
||||
async function adminAreasFor(iso3, base) {
|
||||
if (iso3 !== 'IND') return undefined
|
||||
try {
|
||||
const { loadIndiaStates } = await import('@map/data/indiaStates.js')
|
||||
const n = ((await loadIndiaStates(base)).states || []).length
|
||||
return n ? `${n} states and union territories` : undefined
|
||||
} catch (_) { return undefined } // the cell falls back to the country's name
|
||||
}
|
||||
|
||||
export function makeCountrySheet({ records, assetsBase, countries = null }) {
|
||||
return async function countrySheet(item, format) {
|
||||
const iso3 = item && item.iso3
|
||||
|
|
@ -84,14 +112,16 @@ export function makeCountrySheet({ records, assetsBase, countries = null }) {
|
|||
records({ type: 'country', values: [iso3] }),
|
||||
])
|
||||
if (!rows || !rows.length) return '' // nothing to pin: fall through rather than draw an empty country
|
||||
const big = largeSheet(item)
|
||||
const adminAreas = item.adminAreas || (big ? await adminAreasFor(String(iso3).toUpperCase(), assetsBase()) : undefined)
|
||||
return generateCountrySheet({
|
||||
// region travels: countryZone colours the sheet from it, and a country arriving without one
|
||||
// silently falls back to the old flat teal for that poster alone.
|
||||
country: { id: item.id, iso3, label: item.label, count: item.count, region: item.region },
|
||||
country: { id: item.id, iso3, label: item.label, count: item.count, region: item.region, adminAreas },
|
||||
records: rows,
|
||||
assetsBase: assetsBase(),
|
||||
ISO3_NAME,
|
||||
format,
|
||||
format: sheetFormatFor(item, format),
|
||||
series: seriesFor(countries, item),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,13 +38,24 @@ export function inlineSvg(posterHtml) {
|
|||
return svg.replace(/<svg([^>]*)>/, `<svg$1><style>${safe}</style>`);
|
||||
}
|
||||
|
||||
/**
|
||||
* ⚠ AN EXTERNAL IMAGE CAN NEVER DRAW IN THAT ISOLATED DOCUMENT. An SVG loaded through <img> fetches no external
|
||||
* resource by spec, and no people-group photo host sends CORS headers, so a photo cannot be inlined as data: in
|
||||
* the browser either. Left in, each one rasterises as the browser's broken-image glyph (measured on the large
|
||||
* side-lane sheet's photos). So the raster drops every <image> whose href is not data: (the basemap tiles, which
|
||||
* ARE data:, stay); a sheet that carries photos PRINTS AS A DOCUMENT instead, where they load (hasExternalImages).
|
||||
*/
|
||||
const EXTERNAL_IMAGE = /<image\b[^>]*?\bhref="(?!data:)[^"]*"[^>]*?\/>/g;
|
||||
export function hasExternalImages(posterHtml) { return /<image\b[^>]*?\bhref="(?!data:)/.test(posterHtml || ''); }
|
||||
export function stripExternalImages(svg) { return svg.replace(EXTERNAL_IMAGE, ''); }
|
||||
|
||||
/**
|
||||
* Rasterise a poster to a Blob in the browser.
|
||||
* @param {string} posterHtml the poster document (posters/<id>.html)
|
||||
* @param {object} opts scale (default 2), type ('image/webp'|'image/png'), quality
|
||||
*/
|
||||
export async function rasterPoster(posterHtml, { scale = 2, type = 'image/webp', quality = 0.85 } = {}) {
|
||||
const svg = inlineSvg(posterHtml);
|
||||
const svg = stripExternalImages(inlineSvg(posterHtml));
|
||||
// encodeURIComponent, not btoa: the posters carry non-Latin-1 characters (people
|
||||
// group and place names), and btoa throws on those.
|
||||
const url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
// The sha384 of library/packet/poster-render.js. The browser fetches the served copy of that file
|
||||
// (<assets>/lib/poster-render.js), hashes it, and runs it only when it matches this pin: code loaded at
|
||||
// print time is integrity-pinned like every embed, because it executes on partner pages.
|
||||
export const POSTER_RENDER_SHA384 = 'sha384-G47HkYq8eBHUsabdbIppQnCHHlYH6wMYwDH0JjOD++43LdHQugVItepj2arJ1ONR'
|
||||
export const POSTER_RENDER_SHA384 = 'sha384-eiiTZts7LtjX2hiisXAI4WuoxPQJk9uPEeN5yLHiYXmxsyw+Y+KmJkred53w1ioC'
|
||||
// The sha384 of library/scripts/posters/data/ne-places.json, served at <assets>/boundaries/ne-places.json.
|
||||
// Data, not code, but pinned the same way: a sheet uses the places only when they are the pinned ones.
|
||||
export const NE_PLACES_SHA384 = 'sha384-Qtz2q8mcnA9Ei8Kwa2MRMyX95hSbSnUXjXdtsy8rHTX++mgSCih4LO4Rdrb2MIQy'
|
||||
// The sha384 of the classic-script copy (<assets>/lib/poster-render.global.js, globalVariant of the file above),
|
||||
// loaded with <script integrity> where a page's policy forbids importing a blob: of the verified text.
|
||||
export const POSTER_RENDER_GLOBAL_SHA384 = 'sha384-Y9bdIl2H1EAjFWNlSLua0OPdDjAlRnnq6k14gv1X/Sa8rUmNOB77jpttQHbg/kWz'
|
||||
export const POSTER_RENDER_GLOBAL_SHA384 = 'sha384-bFtiRPqRP81tqK81UvcglvCfdEUQnoTqIAw4ezEbx14Hg3nR++nz3LNASQwtgXsk'
|
||||
// The sha384 of library/scripts/posters/packet-strings.json, served at <assets>/lib/packet-strings.json. The
|
||||
// packet, list and card text in every language, fetched at print time (packet/packetStrings.js) and used only
|
||||
// when it matches; otherwise the print falls back to the bundled English block.
|
||||
|
|
|
|||
|
|
@ -259,8 +259,22 @@ const FORMATS = {
|
|||
// about 1,600px of height against the letter sheet's ~1,200, which is what lets the names grow.
|
||||
// Output: <zone>.packet.html beside the posters (see FILE_SUFFIX), never over them.
|
||||
packet: { w: 1500, h: 1941, gutter: 330, page: 'letter portrait', pageWidthIn: 8.5, mapOnly: true, header: 250, footer: 90 },
|
||||
// ★ LARGE SHEETS WITH SIDE LANES (`sideLanes`): a whole country's hundreds of groups, every one named horizontally
|
||||
// in two staggered columns per side, ONE leader per cluster of co-located groups, crossing-free by construction
|
||||
// (see sideLanesLayout). Same 41.67 px/in as the 3x3, so the header, pins and footer keep their physical size.
|
||||
// `nameMaxPt` caps the names' print size; the layout solves the largest size that fits the rows and columns.
|
||||
'60x96': { w: 2500, h: 4000, gutter: 572, page: '60in 96in', pageWidthIn: 60, sideLanes: true, nameMaxPt: 20.7, photos: true },
|
||||
'66x66': { w: 2750, h: 2750, gutter: 470, page: '66in 66in', pageWidthIn: 66, sideLanes: true, nameMaxPt: 18, photos: true },
|
||||
'36x60': { w: 1500, h: 2500, gutter: 400, page: '36in 60in', pageWidthIn: 36, sideLanes: true, nameMaxPt: 14, photos: false },
|
||||
};
|
||||
const FMT_KEY = FORMATS[process.env.FORMAT] ? process.env.FORMAT : '3x4';
|
||||
// The side-lane marks' styles: emitted ONLY on a sideLanes sheet, so every other format's page is byte-identical.
|
||||
const SIDE_LANES_CSS = `
|
||||
.slead{stroke:#5a5246;stroke-width:0.9;fill:none;opacity:.5}
|
||||
.sbr{stroke:#5a5246;stroke-width:1.1;fill:none}
|
||||
.srow{stroke:#b9ac8c;stroke-width:.3;fill:none;opacity:.7}
|
||||
.spin{fill:#1c1c17;stroke:#f7f4ec;stroke-width:.8}
|
||||
.snm{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-weight:700;fill:#1c1c17}`;
|
||||
const FMT = FORMATS[FMT_KEY];
|
||||
// Map-only sheets (packet page 1) skip the poster's header and footer furniture — see FORMATS.packet.
|
||||
const MAP_ONLY = FMT.mapOnly === true;
|
||||
|
|
@ -852,7 +866,9 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
const floorDeg = CITIES ? _floor(Math.max(_w, _h)) : 1;
|
||||
// PER-AXIS floor. A collinear cluster (zero span on one axis) must not be padded
|
||||
// as if it were as wide as the other axis — that is what produced a 0.0% spread.
|
||||
const pad = CITIES
|
||||
// A side-lane sheet is the COUNTRY at its largest: a thin margin, since the names, not neighbours, fill the sides.
|
||||
const pad = FMT.sideLanes ? [_w * 0.04, _h * 0.04]
|
||||
: CITIES
|
||||
? [_w * 0.22 + _floor(Math.max(_w, _h * 0.35)), _h * 0.22 + _floor(Math.max(_h, _w * 0.35))]
|
||||
: [_w * 0.22 + floorDeg, _h * 0.22 + floorDeg];
|
||||
const viewBbox = [bbox[0] - pad[0], bbox[1] - pad[1], bbox[2] + pad[0], bbox[3] + pad[1]];
|
||||
|
|
@ -882,12 +898,14 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// frame is exactly what shows them.
|
||||
// FULL SHEET HEIGHT — the map runs from just under the header to just above the
|
||||
// footer rule, so there is no dead band anywhere on the page.
|
||||
const FRAME_H = MAP_H;
|
||||
// A side-lane sheet fits the frame to the country's own shape and centres it: its names run the sheet's full height
|
||||
// beside it, so the frame need not, and a full-height frame would only shrink the country to show its neighbours.
|
||||
const FRAME_H = FMT.sideLanes ? Math.min(MAP_H, FRAME_W / zoneAspect) : MAP_H;
|
||||
// OPTICAL CENTRING, not geometric. A block centred by arithmetic looks like it
|
||||
// is falling off the bottom of the sheet — the optical centre sits ABOVE the
|
||||
// geometric one, which is why framers cut a bottom mat 1.25-1.4x the top. Here
|
||||
// the remainder is split 42/58 so the bottom margin is the larger one.
|
||||
const FRAME_Y = MAP_Y;
|
||||
const FRAME_Y = FMT.sideLanes ? MAP_Y + (MAP_H - FRAME_H) / 2 : MAP_Y;
|
||||
// PROJECT FROM viewBbox, NOT bbox.
|
||||
// These two had drifted apart: `viewBbox` (bbox + 22% context pad) decides WHICH
|
||||
// countries are drawn, while the projection scaled from the bare `bbox` — so the
|
||||
|
|
@ -1245,7 +1263,8 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
//
|
||||
// This is what makes leaders viable again: leaders can only be routed without
|
||||
// crossing if their endpoints are distinct, and the endpoints are these pins.
|
||||
{
|
||||
// A side-lane sheet keeps every pin at its TRUE position: co-located groups share one leader instead.
|
||||
if (!FMT.sideLanes) {
|
||||
// MIN_SEP MUST TRACK THE BADGE SIZE. It was a hardcoded 25 tuned against the
|
||||
// old r=10.8 disc; leaving it fixed while the badge shrinks would keep pushing
|
||||
// pins just as far apart and defeat the whole point of the smaller badge.
|
||||
|
|
@ -1292,7 +1311,126 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// Feed the settled pixel positions back as lng/lat-free overrides.
|
||||
const settled = prePins.map((p) => ({ ...p, _fixed: true }));
|
||||
|
||||
const laid = layoutLabels(settled, proj);
|
||||
// ── SIDE LANES: a whole country's groups on one large sheet (FMT.sideLanes) ───────────────────────────
|
||||
// Every name HORIZONTAL, on the left and right of the map, in two STAGGERED columns per side: the rows alternate
|
||||
// inner/outer at half the line pitch, so each column keeps a full pitch and each row holds exactly ONE name.
|
||||
// Co-located groups (within CL_R px) form a CLUSTER with ONE leader, a bracket and a block of names.
|
||||
// CROSSING-FREE BY CONSTRUCTION, which the numbered design below could not reach at this density:
|
||||
// · one vertical split (the weighted median x) sends each cluster to the nearer side, so runs never meet;
|
||||
// · each leader is a short vertical stub to its own LANE, a horizontal run to the map edge, then a straight fan
|
||||
// to its block; lanes are placed FARTHEST-FROM-THE-EDGE first, each clamped between its neighbouring lanes,
|
||||
// so a stub never jumps a lane, and the fan keeps the lanes' top-to-bottom order (ordered points on two
|
||||
// parallel lines, joined in order, cannot cross). The count is measured and returned, never assumed.
|
||||
// A real photo (never the generic placeholder) sits on the OUTSIDE of its name when the format allows photos.
|
||||
function sideLanesLayout(pinsIn) {
|
||||
const X0 = MAP_X, X1 = MAP_X + MAP_W;
|
||||
const BY0 = LABEL_TOP ?? FRAME_Y, BY1 = LABEL_BOT ?? (FRAME_Y + FRAME_H);
|
||||
const PXI = PW / FMT.pageWidthIn;
|
||||
const pins = pinsIn.map((p) => ({ ...p, px: p._px, py: p._py }));
|
||||
// A photo only from the known people-group photo hosts, over https, and never the generic placeholder.
|
||||
const PHOTO_HOSTS = /^https:\/\/(media\.joshuaproject\.net|joshuaproject\.net|images\.peoplegroups\.org|peoplegroupspublic\.blob\.core\.windows\.net)\//;
|
||||
const photoOf = (p) => (FMT.photos && typeof p.image_url === 'string' && PHOTO_HOSTS.test(p.image_url) && !/no-photo-images/.test(p.image_url) ? p.image_url : null);
|
||||
const CL_R = 14, clusters = [];
|
||||
for (const p of pins) {
|
||||
const c = clusters.find((q) => Math.hypot(q.x - p.px, q.y - p.py) <= CL_R);
|
||||
if (c) { c.items.push(p); c.x += (p.px - c.x) / c.items.length; c.y += (p.py - c.y) / c.items.length; }
|
||||
else clusters.push({ x: p.px, y: p.py, items: [p] });
|
||||
}
|
||||
for (const c of clusters) c.items.sort((a, b) => String(a.name).localeCompare(String(b.name), 'en'));
|
||||
const byX = [...clusters].sort((a, b) => a.x - b.x);
|
||||
let acc = 0, split = byX.length ? byX[0].x : 0;
|
||||
for (const c of byX) { acc += c.items.length; if (acc >= pins.length / 2) { split = c.x; break; } }
|
||||
for (const c of clusters) c.side = c.x <= split ? 'L' : 'R';
|
||||
// THE TYPE: the largest size (capped by nameMaxPt) whose rows fit the band and whose two columns fit the gutter.
|
||||
const FAN = 70, GAP = 10, PHOTO_GAP = 4;
|
||||
const room = GUTTER - (SHEET_EDGE + 8) - FAN - GAP;
|
||||
const photoPx = (f) => Math.floor(f * 1.18) - 1;
|
||||
const longest = (f) => Math.max(...pins.map((p) => textW(String(p.name), f) * 1.06 + (photoOf(p) ? photoPx(f) + PHOTO_GAP : 0)));
|
||||
const lineCount = (sd) => clusters.filter((c) => c.side === sd).reduce((a, c) => a + c.items.length, 0);
|
||||
const maxLines = Math.max(1, lineCount('L'), lineCount('R'));
|
||||
let font = Math.min((FMT.nameMaxPt || 14) / 72 * PXI, (BY1 - BY0) / (maxLines * 0.59 * 1.02));
|
||||
while (font > 5 && 2 * (longest(font) + 8) > room) font -= 0.1;
|
||||
const ROW = font * 1.18 / 2, PHOTO = photoPx(font), COLW = longest(font) + 8;
|
||||
// LANES, farthest from the edge first (see above). PIN_CLEAR: within its safe gap a lane prefers a height
|
||||
// that passes no other pin, at most 7.5 px from its own pin.
|
||||
const LANE_GAP = 1.5, PIN_CLEAR = 4.5;
|
||||
let laneJumps = 0;
|
||||
for (const sd of ['L', 'R']) {
|
||||
const L = sd === 'L';
|
||||
const mine = clusters.filter((c) => c.side === sd).sort((a, b) => (L ? b.x - a.x : a.x - b.x));
|
||||
const lanes = [];
|
||||
for (const c of mine) {
|
||||
let lo = -Infinity, hi = Infinity;
|
||||
for (const ly of lanes) { if (ly < c.y && ly > lo) lo = ly; if (ly >= c.y && ly < hi) hi = ly; }
|
||||
let y = Math.min(Math.max(c.y, lo + LANE_GAP), hi - LANE_GAP);
|
||||
if (lo + LANE_GAP > hi - LANE_GAP) { y = (lo + hi) / 2; laneJumps++; }
|
||||
else {
|
||||
const a0 = L ? X0 : c.x, a1 = L ? c.x : X1;
|
||||
const blockers = clusters.filter((o) => o !== c && o.x > a0 + 1 && o.x < a1 - 1);
|
||||
let best = y, bestHits = Infinity;
|
||||
for (let t = Math.max(lo + LANE_GAP, c.y - 7.5); t <= Math.min(hi - LANE_GAP, c.y + 7.5); t += 0.75) {
|
||||
const hits = blockers.filter((o) => Math.abs(o.y - t) < PIN_CLEAR).length;
|
||||
if (hits < bestHits || (hits === bestHits && Math.abs(t - c.y) < Math.abs(best - c.y))) { best = t; bestHits = hits; }
|
||||
}
|
||||
if (bestHits !== Infinity) y = best;
|
||||
}
|
||||
c.lane = y; lanes.push(y);
|
||||
}
|
||||
}
|
||||
// BLOCKS, stacked top to bottom in lane order, each as near its lane as the rows allow.
|
||||
const rowsAvail = Math.floor((BY1 - BY0) / ROW);
|
||||
let leadersS = '', labelsS = '', dotsS = '', overflow = 0;
|
||||
const placed = [];
|
||||
for (const sd of ['L', 'R']) {
|
||||
const L = sd === 'L';
|
||||
const blocks = clusters.filter((c) => c.side === sd).sort((a, b) => a.lane - b.lane)
|
||||
.map((c) => ({ c, n: c.items.length, want: (c.lane - BY0) / ROW }));
|
||||
const pos = []; let cur = 0;
|
||||
for (const b of blocks) { const q = Math.max(cur, Math.round(b.want - b.n / 2)); pos.push(q); cur = q + b.n; }
|
||||
let over = cur - rowsAvail;
|
||||
for (let k = pos.length - 1; k >= 0 && over > 0; k--) {
|
||||
const lim = k ? pos[k - 1] + blocks[k - 1].n : 0, can = Math.min(over, pos[k] - lim);
|
||||
for (let j = k; j < pos.length; j++) pos[j] -= can;
|
||||
over -= can;
|
||||
}
|
||||
overflow += Math.max(0, over);
|
||||
const edgeX = L ? X0 : X1, portX = L ? X0 - FAN : X1 + FAN;
|
||||
const innerX = L ? portX - 6 : portX + 6, outerX = L ? innerX - COLW : innerX + COLW;
|
||||
blocks.forEach((b, k) => {
|
||||
const c = b.c, r0 = pos[k], midY = BY0 + (r0 + b.n / 2) * ROW;
|
||||
leadersS += `<path class="slead" d="M${c.x.toFixed(1)} ${c.y.toFixed(1)}L${c.x.toFixed(1)} ${c.lane.toFixed(2)}L${edgeX.toFixed(1)} ${c.lane.toFixed(2)}L${portX.toFixed(1)} ${midY.toFixed(1)}"/>`;
|
||||
if (b.n > 1) leadersS += `<path class="sbr" d="M${portX.toFixed(1)} ${(BY0 + r0 * ROW + 1).toFixed(1)}V${(BY0 + (r0 + b.n) * ROW - 1).toFixed(1)}"/>`;
|
||||
dotsS += `<circle class="spin" cx="${c.x.toFixed(1)}" cy="${c.y.toFixed(1)}" r="${b.n > 1 ? 4.4 : 3.2}"/>`;
|
||||
c.items.forEach((g, j) => {
|
||||
const r = r0 + j, rowY = BY0 + (r + 0.5) * ROW, x = r % 2 === 0 ? innerX : outerX;
|
||||
if (r % 2 === 1) leadersS += `<path class="srow" d="M${innerX.toFixed(1)} ${rowY.toFixed(1)}H${(L ? outerX + 3 : outerX - 3).toFixed(1)}"/>`;
|
||||
labelsS += `<text class="snm" x="${x.toFixed(1)}" y="${(rowY + font * 0.34).toFixed(1)}" font-size="${font.toFixed(2)}"${L ? ' text-anchor="end"' : ''}>${esc(String(g.name))}</text>`;
|
||||
const ph = photoOf(g);
|
||||
if (ph) {
|
||||
const tw = textW(String(g.name), font) * 1.06;
|
||||
const px = L ? x - tw - PHOTO_GAP - PHOTO : x + tw + PHOTO_GAP;
|
||||
labelsS += `<image href="${esc(ph)}" x="${px.toFixed(1)}" y="${(rowY - PHOTO / 2).toFixed(1)}" width="${PHOTO}" height="${PHOTO}" preserveAspectRatio="xMidYMid slice"/>`;
|
||||
}
|
||||
placed.push({ g, side: sd, r, photo: !!ph });
|
||||
});
|
||||
});
|
||||
}
|
||||
// MEASURED, not assumed: leader crossings (every segment pair of different leaders).
|
||||
const polys = [...leadersS.matchAll(/class="slead" d="M([^"]+)"/g)].map((m) => m[1].split('L').map((q) => q.trim().split(' ').map(Number)));
|
||||
const segsOf = polys.map((pl) => pl.slice(1).map((q, k) => [pl[k], q]));
|
||||
const sx = (p, q, r) => (r[1] - p[1]) * (q[0] - p[0]) - (q[1] - p[1]) * (r[0] - p[0]);
|
||||
const segX = (a, b) => (sx(a[0], a[1], b[0]) > 0) !== (sx(a[0], a[1], b[1]) > 0) && (sx(b[0], b[1], a[0]) > 0) !== (sx(b[0], b[1], a[1]) > 0);
|
||||
let crossingsS = 0;
|
||||
for (let i = 0; i < segsOf.length; i++) for (let j = i + 1; j < segsOf.length; j++) if (segsOf[i].some((a) => segsOf[j].some((b) => segX(a, b)))) crossingsS++;
|
||||
const stats = { names: placed.length, pins: pins.length, clusters: clusters.length, crossings: crossingsS, overflowRows: overflow,
|
||||
laneJumps, photos: placed.filter((p) => p.photo).length, namePt: +(font / PXI * 72).toFixed(1), photoMm: FMT.photos ? +(PHOTO / PXI * 25.4).toFixed(1) : 0 };
|
||||
return { leaders: leadersS, labels: labelsS, pinDots: dotsS, crossings: crossingsS, pins, stats };
|
||||
}
|
||||
|
||||
const laid = FMT.sideLanes ? [] : layoutLabels(settled, proj);
|
||||
const sides = FMT.sideLanes ? sideLanesLayout(settled) : null;
|
||||
// What the city layer must keep clear of: the drawn pins (laid out, or true positions on a side-lane sheet).
|
||||
const avoidPins = sides ? sides.pins : laid;
|
||||
|
||||
// ── CAPITALS + MAJOR CITIES ────────────────────────────────────────────────
|
||||
// Orientation marks, deliberately SUBORDINATE to the people-group pins. Imhof's
|
||||
|
|
@ -1365,7 +1503,7 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// projected point is not where the badge is drawn. Measuring against the
|
||||
// source coordinate let Saravan's label land 1.4px from a badge while the
|
||||
// guard believed it was clear. This block therefore runs AFTER layoutLabels.
|
||||
for (const pin of laid) {
|
||||
for (const pin of avoidPins) {
|
||||
const px = pin.px, py = pin.py;
|
||||
const dx = px < x0 ? x0 - px : (px > x1 ? px - x1 : 0);
|
||||
const dy = py < yTop ? yTop - py : (py > yBot ? py - yBot : 0);
|
||||
|
|
@ -1695,8 +1833,9 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
const tx = it.laneX + it.dirOut * 5;
|
||||
labels += `<text x="${tx.toFixed(1)}" y="${(it.labelY + it.font * 0.34).toFixed(1)}" text-anchor="${it.tanchor}" font-size="${it.font.toFixed(1)}" class="lbl">${esc(it.name)}</text>`;
|
||||
});
|
||||
if (sides) { leaders += sides.leaders; pinDots += sides.pinDots; labels += sides.labels; }
|
||||
if (!octiOK) console.warn(` ⚠ non-octilinear segment in ${zone.zoneKey}`);
|
||||
const crossings = countPolyCrossings(routes);
|
||||
const crossings = sides ? sides.crossings : countPolyCrossings(routes);
|
||||
const diagFrac = bestD;
|
||||
|
||||
// ── TITLE vs SUBTITLE ──────────────────────────────────────────────────────
|
||||
|
|
@ -2676,8 +2815,8 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
page. Collapse the flow height to the scaled height. */
|
||||
html,body{width:${(FMT.pageWidthIn * 96).toFixed(0)}px;height:${(FMT.pageWidthIn * 96 * FMT.h / FMT.w).toFixed(0)}px;overflow:hidden}
|
||||
}
|
||||
</style></head><body><div class="stage">${svg}</div></body></html>`;
|
||||
return { html, title, octiOK, crossings, diagFrac };
|
||||
${FMT.sideLanes ? SIDE_LANES_CSS + '\n' : ''}</style></head><body><div class="stage">${svg}</div></body></html>`;
|
||||
return sides ? { html, title, octiOK, crossings, diagFrac, sideLanes: sides.stats } : { html, title, octiOK, crossings, diagFrac };
|
||||
}
|
||||
|
||||
return { renderPoster, CITIES, COUNTRIES, FILE_SUFFIX, FMT_KEY, FMT, MAP_ONLY, FORMATS };
|
||||
|
|
|
|||
|
|
@ -240,8 +240,13 @@ export function countryRegionColor(region) {
|
|||
return `hsl(${Math.round((i * 360) / POSTER_REGIONS.length)}, 72%, 56%)`
|
||||
}
|
||||
|
||||
export function countryZone({ id, iso3, label, count, region }) {
|
||||
return { id, zoneKey: id, label, count, color: countryRegionColor(region), adm0: [iso3], selector: { type: 'country_codes', values: [iso3] } }
|
||||
export function countryZone({ id, iso3, label, count, region, adminAreas }) {
|
||||
return {
|
||||
id, zoneKey: id, label, count, color: countryRegionColor(region), adm0: [iso3], selector: { type: 'country_codes', values: [iso3] },
|
||||
// The facts row's "Administrative Areas" cell, when the caller knows it (India: "36 states and union territories");
|
||||
// without it the cell falls back to the country's name, as every country sheet has drawn it so far.
|
||||
...(adminAreas ? { provinces: [adminAreas] } : {}),
|
||||
}
|
||||
}
|
||||
export function countryGeometry(zone, adm0, hasGroups) {
|
||||
const fc = { type: 'FeatureCollection', features: [] }
|
||||
|
|
@ -259,7 +264,9 @@ export function pinsFrom(records) {
|
|||
for (const r of records || []) {
|
||||
const lat = Number(r.latitude), lng = Number(r.longitude)
|
||||
if (!isFinite(lat) || !isFinite(lng)) continue
|
||||
out.push({ name: r.name || r.slug || '', lat, lng, slug: r.slug, country_code: r.country_code, population: r.population })
|
||||
// image_url rides along for the large side-lane sheets, which put a real photo beside each name; every other
|
||||
// format ignores it, so their sheets are unchanged.
|
||||
out.push({ name: r.name || r.slug || '', lat, lng, slug: r.slug, country_code: r.country_code, population: r.population, image_url: r.image_url })
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
14
package.json
14
package.json
|
|
@ -350,7 +350,7 @@
|
|||
"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",
|
||||
"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: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: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",
|
||||
"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: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: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",
|
||||
"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",
|
||||
|
|
@ -414,7 +414,11 @@
|
|||
"check:panel-sheets": "node internal/check-panel-sheets.mjs",
|
||||
"check:poster-wall-host-seams": "node internal/check-poster-wall-host-seams.mjs",
|
||||
"check:shell-header:self-test": "node internal/check-shell-header.mjs --self-test",
|
||||
"check:shell-header": "node internal/check-shell-header.mjs"
|
||||
"check:shell-header": "node internal/check-shell-header.mjs",
|
||||
"check:side-lanes": "node plugin-designs/country-posters/scripts/check-side-lanes.mjs",
|
||||
"check:side-lanes:self-test": "node plugin-designs/country-posters/scripts/check-side-lanes.mjs --self-test",
|
||||
"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"
|
||||
},
|
||||
"//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.",
|
||||
|
|
@ -678,7 +682,11 @@
|
|||
"check:control-inset": "bottomControlInset MEASURES the map's real bottom control band, so the pan reserves the band the controls actually occupy. check:popup-pan-controls proves the ARITHMETIC; this proves the band is real. If the inset silently returned 0 -- a selector typo, an engine renaming its control classes, a control moving outside the container -- panPlanFor would get bottomInset 0, behave exactly as it did before the fix, and check:popup-pan-controls would still pass every case, because its arithmetic is still correct: the bug back with both gates green. 8 cases against a real DOM, running the SHIPPED bottomControlInset read out of the SFC (it is not exported), never a copy: the band equals what the DOM independently says (not merely non-zero); no controls reserves 0; a zero-sized control reserves nothing; the taller of two corners wins; a wrapped attribution reserves more than one line; a control outside the container is ignored; a null container returns 0; and the MAPBOX corners count too, since the component takes a provider prop and every other case renders MapLibre names. Needs a browser: every assertion rests on getBoundingClientRect. 75 = no slot, never a pass.",
|
||||
"check:panel-sheets": "the shared panel geometry (library/components/poster/panelSheets.js) for posters printed on several sheets: 60x96 = 3 × 60×36 stacked and 66x66 = 2 × 36×66 side by side, 6 in joins, and a single sheet; the sheets tile the finished poster, glue slices tile and bleed slices repeat exactly the join, a bleed join prints a guide not a solid band, a screen preview always shows the red band, and the Bless 5x8ft takes its bands and marks from the module. Two-sided (4 planted defects). Headless.",
|
||||
"check:poster-wall-host-seams": "PosterWall's two host seams, and that with neither set every wall behaves as before. headerAction: default null is the Show-on-map TOGGLE (aria-pressed, one window event per click, is-last, no emit); { labelKey, ariaKey } is a plain button that emits header-action with the item, no window event, no aria-pressed, and reads the label alone for an item with no count. previewSrc: default null is the stored previews/<id>.webp and no IntersectionObserver; set, a card renders only once in view of the wall's own scroller, one render at a time, cached per page, and null / a throw / a timeout ends on the preview-unavailable placeholder. Image errors: a default wall retries the tree's own copy before the placeholder; a generated preview goes straight to it. Section heads: a section with people reads \"<n> people groups · <items>\" exactly as before; one with none reads its items alone. sectionItemCountKey lets a wall count one section in its own words ({ key, n }); unnamed sections keep the default. Headless: PosterWall compiled as a client component on a recording renderer; real onClick and a recording IntersectionObserver. 17 mutants each FAIL.",
|
||||
"check:shell-header": "a header-off shell has ONE way to its tabs and ONE language menu, in the right place for the width: VerticalTabsLayout renders the phone bar (☰ + #phone-end) only when narrow AND header-off, #rail-end only when wide AND header-off, and no corner circle; ShellLanguageMenu's pick dispatches exactly one cancelable, composed, bubbling doxa-language-request (detail.language), does not switch when a host cancels, and writes ?lang= only on a tree-owned page; every switcher language has a catalogue and the current language is always listed. Headless (Vite SSR). Run the `check:shell-header:self-test` script for the NEGATIVE test (11 planted defects)"
|
||||
"check:shell-header": "a header-off shell has ONE way to its tabs and ONE language menu, in the right place for the width: VerticalTabsLayout renders the phone bar (☰ + #phone-end) only when narrow AND header-off, #rail-end only when wide AND header-off, and no corner circle; ShellLanguageMenu's pick dispatches exactly one cancelable, composed, bubbling doxa-language-request (detail.language), does not switch when a host cancels, and writes ?lang= only on a tree-owned page; every switcher language has a catalogue and the current language is always listed. Headless (Vite SSR). Run the `check:shell-header:self-test` script for the NEGATIVE test (11 planted defects)",
|
||||
"check:side-lanes": "the large side-lane sheets (60x96, 66x66, 36x60) through the real poster-render.js, on a SEEDED worst case (855 groups, stacked duplicates, tight clusters, real and placeholder photos): every name drawn exactly once, leader crossings 0 (re-measured from the SVG, and equal to the renderer's own count), no overflow rows, photos only where a real one exists and the format allows them.",
|
||||
"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: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."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@capacitor/android": "^7.6.9",
|
||||
|
|
|
|||
BIN
plugin-designs/country-posters/assets/previews/country-ind.webp
Normal file
BIN
plugin-designs/country-posters/assets/previews/country-ind.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -44,7 +44,9 @@ import { getRuntimeLocale, detectLocale } 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 } from '@map/packet/countrySheet.js'
|
||||
import { makeCountrySheet, largeSheet } 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.
|
||||
import { loadIndiaStates } from '@map/data/indiaStates.js'
|
||||
import { makeIndiaStateSheet, statesWithPosters } from './src/indiaStateSheet.js'
|
||||
|
|
@ -145,7 +147,15 @@ const recordsFor = createPeopleGroupRecords({
|
|||
lang: () => getRuntimeLocale() || detectLocale(),
|
||||
profileConfig,
|
||||
})
|
||||
const countryRecords = (c) => (c.indiaState
|
||||
// ★ A LARGE-SHEET COUNTRY (India: one 5x8 sheet naming all its groups; countrySheet.js's LARGE_SHEETS) is DESKTOP-ONLY:
|
||||
// its poster, list, packet and cards (about a thousand pages) are offered on a desktop or laptop, and on a phone the
|
||||
// card says so instead of failing. Same rule as the Regions list, from the same table.
|
||||
const ON_PHONE = isPhonePrint()
|
||||
const phoneBlocked = (c) => !c.indiaState && ON_PHONE && Boolean(largeSheet(c)?.desktopOnly)
|
||||
// A blocked card's list, packet and cards REJECT with the notice (never resolve [], which the print path would
|
||||
// report as "no people groups" on a card that has hundreds), before any record is fetched.
|
||||
const desktopOnly = () => Promise.reject(keyedError('poster.phoneLimitAll', 'This needs a desktop or laptop computer.'))
|
||||
const countryRecords = (c) => (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
|
||||
|
|
@ -219,7 +229,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.') : c.unsupportedReason ? t('messages.countryPosterTooMany') : null)
|
||||
const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : 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)
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ useShadowStyles(PANE_GRID_STYLES + `
|
|||
:label="t('tabs.views')"
|
||||
@select="go"
|
||||
/>
|
||||
<ShareButton :is-dark="isDark" :embed-path="SHARE_EMBED_PATH" />
|
||||
<ShareButton :is-dark="isDark" :embed-path="SHARE_EMBED_PATH" shape="rounded-square" />
|
||||
</header>
|
||||
<div class="stage" ref="stage"></div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -94,11 +94,18 @@ export function armA(files, callers = []) {
|
|||
}
|
||||
|
||||
// ARM B: named expectations for seriesFor over the real data.
|
||||
export function armB(seriesFor, countries) {
|
||||
export function armB(seriesFor, countries, sheetFormatFor = null) {
|
||||
const fails = []
|
||||
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
|
||||
// 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']]
|
||||
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)
|
||||
if (oversized.length > 1) fails.push(`${oversized.length} countries have no sheet size (${oversized.map((c) => c.iso3).join(', ')}): they now share ONE oversized series, so India prints "map 1 of ${oversized.length}". Decide that deliberately, then update this expectation.`)
|
||||
if (!ind) fails.push('India is missing from country-posters.json')
|
||||
|
|
@ -137,6 +144,20 @@ export function armC(H, states, adm1Ind) {
|
|||
// 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 F — A DESKTOP-ONLY CARD ON A PHONE (India): in EACH caller (the wall, the Regions list) its rows REJECT with
|
||||
// poster.phoneLimitAll before any record is fetched, never resolve [] (the print path reads [] as "that list has no
|
||||
// people groups to print", on a card that has 855), and the card shows the same full-block notice.
|
||||
export function armF(srcs) {
|
||||
const fails = []
|
||||
for (const [name, src] of Object.entries(srcs)) {
|
||||
if (!/phoneBlocked\((?:c|item)\) \? (?:desktopOnly\(\)|Promise\.reject\(keyedError\('poster\.phoneLimitAll')/.test(src)) fails.push(`${name}: a phone-blocked card's rows do not reject with poster.phoneLimitAll`)
|
||||
if (/phoneBlocked\((?:c|item)\) \? Promise\.resolve\(\[\]\)/.test(src)) fails.push(`${name}: a phone-blocked card's rows resolve [] (printed as "no people groups")`)
|
||||
if (/desktopOnly\(\)/.test(src) && !/const desktopOnly = \(\) => Promise\.reject\(keyedError\('poster\.phoneLimitAll'/.test(src)) fails.push(`${name}: desktopOnly() does not reject with poster.phoneLimitAll`)
|
||||
if (!/phoneBlocked\(c\) \? t(?:Key)?\('poster\.phoneLimitAll'\)/.test(src)) fails.push(`${name}: a phone-blocked card does not show poster.phoneLimitAll`)
|
||||
}
|
||||
return fails
|
||||
}
|
||||
|
||||
export function armE(countries, states, wallSrc, posterWallSrc, H) {
|
||||
const fails = []
|
||||
if (!/:section-total=/.test(wallSrc)) fails.push('country-poster-wall.vue does not pass :section-total, so India is counted twice')
|
||||
|
|
@ -158,7 +179,7 @@ export function armE(countries, states, wallSrc, posterWallSrc, H) {
|
|||
|
||||
async function main() {
|
||||
const selfTest = process.argv.includes('--self-test')
|
||||
const { seriesFor } = await import(join(ROOT, 'library/packet/countrySheet.js'))
|
||||
const { seriesFor, sheetFormatFor } = await import(join(ROOT, 'library/packet/countrySheet.js'))
|
||||
const data = JSON.parse(readFileSync(join(ROOT, 'library/data/country-posters.json'), 'utf8'))
|
||||
const countries = data.countries || []
|
||||
if (countries.length < 170) { console.log(`NOT MEASURED: country-posters.json parsed ${countries.length} countries (floor 170)`); process.exitCode = 75; return }
|
||||
|
|
@ -171,6 +192,7 @@ async function main() {
|
|||
const files = {}
|
||||
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')
|
||||
if (selfTest) {
|
||||
const wall = KNOWN_CALLERS[0]
|
||||
const cases = [
|
||||
|
|
@ -184,6 +206,10 @@ 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))],
|
||||
['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'") })],
|
||||
['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
|
||||
|
|
@ -202,7 +228,7 @@ 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 fails = [...armA(files, callers), ...armB(seriesFor, countries), ...armC(H, states, adm1Ind), ...E.fails]
|
||||
const fails = [...armA(files, callers), ...armB(seriesFor, countries, sheetFormatFor), ...armC(H, states, adm1Ind), ...E.fails, ...armF({ wall: files['plugin-designs/country-posters/country-poster-wall.vue'], regions: REG })]
|
||||
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 }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env node
|
||||
// check:side-lane-photo-pixels — a BROWSER gate (browser seat only: internal/browser-slot.sh; elsewhere exit 75,
|
||||
// NOT MEASURED). The 60x96 side-lane sheet, rendered by the real poster-render.js, rasterised by the real
|
||||
// poster-raster.js, measured in PIXELS at every photo slot:
|
||||
// - a data: photo (a local red fixture, so it is offline) DRAWS: its slot is red;
|
||||
// - every photo on its own host stays FLAT (the paper shows through): never the broken-image glyph, which is
|
||||
// what an external <image> rasterises as inside an SVG loaded as an image.
|
||||
// --self-test: the same raster WITHOUT stripExternalImages must leave external slots NOT flat (the glyph).
|
||||
// Its source-level twin, always measured, is check:side-lanes' photo arms.
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import zlib from 'node:zlib'
|
||||
import { dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..')
|
||||
const { chromium } = await import(join(ROOT, 'internal/lib/playwright-guarded.mjs'))
|
||||
const J = (p) => JSON.parse(readFileSync(join(ROOT, p), 'utf8'))
|
||||
// a 4x4 red PNG, built here (no file)
|
||||
function png(w, h, rgb) {
|
||||
const crcT = Array.from({ length: 256 }, (_, n) => { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; return c >>> 0 })
|
||||
const crc = (b) => { let c = 0xffffffff; for (const x of b) c = crcT[(c ^ x) & 255] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0 }
|
||||
const chunk = (t, d) => { const l = Buffer.alloc(4); l.writeUInt32BE(d.length); const td = Buffer.concat([Buffer.from(t), d]); const c = Buffer.alloc(4); c.writeUInt32BE(crc(td)); return Buffer.concat([l, td, c]) }
|
||||
const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(w, 0); ihdr.writeUInt32BE(h, 4); ihdr[8] = 8; ihdr[9] = 2
|
||||
const raw = Buffer.concat(Array.from({ length: h }, () => Buffer.concat([Buffer.from([0]), Buffer.concat(Array.from({ length: w }, () => Buffer.from(rgb)))])))
|
||||
return Buffer.concat([Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), chunk('IHDR', ihdr), chunk('IDAT', zlib.deflateSync(raw)), chunk('IEND', Buffer.alloc(0))])
|
||||
}
|
||||
const RED = 'data:image/png;base64,' + png(4, 4, [230, 20, 20]).toString('base64')
|
||||
const { createPosterGenerator } = await import(join(ROOT, 'library/packet/poster-render.js'))
|
||||
let seed = 7; const rnd = () => ((seed = (seed * 1103515245 + 12345) % 2147483648) / 2147483648)
|
||||
const pins = Array.from({ length: 300 }, (_, i) => ({ name: `Group ${i}`, lat: 8 + rnd() * 27, lng: 68 + rnd() * 28, slug: `g${i}`, country_code: 'IND', population: 1000 + i,
|
||||
image_url: i % 3 === 0 ? `https://media.joshuaproject.net/public/assets/media/profiles/photos/p${10120 + i}.jpg` : 'https://s3.doxa.life/no-photo-images/asia.jpg' }))
|
||||
const adm0 = J('public/dx-maps/assets/boundaries/world-adm0.geojson')
|
||||
const zone = { id: 'country-ind', zoneKey: 'country-ind', label: 'India', count: pins.length, color: '#d4a640', adm0: ['IND'], selector: { type: 'country_codes', values: ['IND'] } }
|
||||
const gen = createPosterGenerator({ env: { FORMAT: '60x96', SPARSE: '1', POSTER_DATE: '2026-10-01' }, SERIES_DEF: J('library/scripts/posters/series.json').country, ISO3_NAME: { IND: 'India' }, adm0, adm1Ind: null, adm1Chn: null, nePlaces: [], series: { totalGroups: pins.length, totalLists: 1, index: 1 } })
|
||||
let { html } = await gen.renderPoster(zone, pins, { type: 'FeatureCollection', features: adm0.features.filter((f) => f.properties?.iso3 === 'IND') })
|
||||
html = html.replace(/(<image href=")https:[^"]+(")/, `$1${RED}$2`) // the FIRST photo becomes the local fixture
|
||||
const slots = [...html.matchAll(/<image href="([^"]+)" x="([\d.]+)" y="([\d.]+)" width="([\d.]+)" height="([\d.]+)"/g)].map((m) => ({ data: m[1].startsWith('data:'), x: +m[2], y: +m[3], s: +m[4] }))
|
||||
const vb = html.match(/<svg[^>]*viewBox="0 0 ([\d.]+) ([\d.]+)"/); const W = vb ? +vb[1] : 2500
|
||||
const cur = readFileSync(join(ROOT, 'library/packet/poster-raster.js'), 'utf8')
|
||||
const poison = cur.replace('stripExternalImages(inlineSvg(posterHtml))', 'inlineSvg(posterHtml)')
|
||||
if (poison === cur) throw new Error('poison anchor moved')
|
||||
console.log(`sheet: ${slots.length} photo slots (${slots.filter((s) => s.data).length} data: fixture, ${slots.filter((s) => !s.data).length} external), viewBox width ${W}`)
|
||||
const selfTest = process.argv.includes('--self-test')
|
||||
const b = await chromium.launch()
|
||||
const page = await b.newPage({ viewport: { width: 800, height: 600 }, deviceScaleFactor: 1 })
|
||||
let verdict = 0
|
||||
try {
|
||||
await page.setContent('<!doctype html><html><body></body></html>')
|
||||
for (const [label, src] of selfTest ? [['POISON (no strip)', poison]] : [['CURRENT', cur]]) {
|
||||
const r = await page.evaluate(async ({ src, html, slots, W }) => {
|
||||
const mod = await import(URL.createObjectURL(new Blob([src], { type: 'text/javascript' })))
|
||||
const blob = await mod.rasterPoster(html, { scale: 0.5, type: 'image/png' })
|
||||
const bmp = await createImageBitmap(blob); const c = document.createElement('canvas'); c.width = bmp.width; c.height = bmp.height
|
||||
const ctx = c.getContext('2d'); ctx.drawImage(bmp, 0, 0); const k = bmp.width / W
|
||||
const stat = (s) => { const x = Math.round((s.x + s.s * 0.2) * k), y = Math.round((s.y + s.s * 0.2) * k), n = Math.max(2, Math.round(s.s * 0.6 * k))
|
||||
const d = ctx.getImageData(x, y, n, n).data; let r = 0, g = 0, b = 0, v = 0; const px = d.length / 4
|
||||
for (let i = 0; i < d.length; i += 4) { r += d[i]; g += d[i + 1]; b += d[i + 2] } r /= px; g /= px; b /= px
|
||||
for (let i = 0; i < d.length; i += 4) v += Math.abs(d[i] - r) + Math.abs(d[i + 1] - g) + Math.abs(d[i + 2] - b)
|
||||
return { r: Math.round(r), g: Math.round(g), b: Math.round(b), varr: +(v / px).toFixed(1) } }
|
||||
const bg = stat({ x: 2, y: 2, s: 30 })
|
||||
const fix = slots.filter((s) => s.data).map(stat)
|
||||
const ext = slots.filter((s) => !s.data).map(stat)
|
||||
const near = (a) => a.varr <= 6 // flat: nothing drawn in the slot (paper shows through)
|
||||
return { size: [bmp.width, bmp.height], bg, fixture: fix, extBackground: ext.filter(near).length, extTotal: ext.length, extSample: ext.filter((a) => !near(a)).slice(0, 3) }
|
||||
}, { src, html, slots, W })
|
||||
const redOk = r.fixture.length && r.fixture.every((a) => a.r > 180 && a.g < 80 && a.b < 80)
|
||||
const flatAll = r.extBackground === r.extTotal && r.extTotal > 0
|
||||
if (selfTest) verdict = r.extBackground === 0 ? 0 : 1
|
||||
else verdict = redOk && flatAll ? 0 : 1
|
||||
console.log(`${label}: raster ${r.size.join('x')}, background rgb(${r.bg.r},${r.bg.g},${r.bg.b}); fixture slot ${JSON.stringify(r.fixture)} -> ${redOk ? 'DRAWS (red)' : 'NOT drawn'}; external slots FLAT (nothing drawn): ${r.extBackground}/${r.extTotal}${r.extSample.length ? ' non-background e.g. ' + JSON.stringify(r.extSample) : ''}`)
|
||||
}
|
||||
} finally { await b.close() }
|
||||
if (selfTest) console.log(verdict ? '✗ MISSED: external slots stayed flat without the strip' : '✓ caught: without the strip every external slot draws (the glyph)')
|
||||
else console.log(verdict ? 'FAIL check:side-lane-photo-pixels' : 'PASS check:side-lane-photo-pixels — the data: photo draws; every external photo slot is flat (no broken-image glyph)')
|
||||
process.exitCode = verdict
|
||||
152
plugin-designs/country-posters/scripts/check-side-lanes.mjs
Normal file
152
plugin-designs/country-posters/scripts/check-side-lanes.mjs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/env node
|
||||
// check:side-lanes — the large side-lane sheets (60x96 / 66x66 / 36x60) name every group once, horizontally, with
|
||||
// leaders that never cross, on a DETERMINISTIC worst case: a seeded synthetic country of 855 groups with stacked
|
||||
// duplicates and tight clusters (district geocodes are like that), long and short names, real photos on an allowed
|
||||
// host, the generic placeholder, and photos on a host that is NOT allowed (none of the last two may be drawn).
|
||||
// Rendered through the real poster-render.js (renderPoster), so it measures what prints.
|
||||
//
|
||||
// Asserts per format: every name drawn exactly once; leader crossings 0 (the renderer's own count, re-measured here
|
||||
// from the SVG); no overflow rows; photos = the pins with a real photo (60x96/66x66) or none (36x60).
|
||||
// PHOTOS ARE NEVER RASTERISED: a sheet whose photos sit on their own hosts must (a) reach the raster with NO external
|
||||
// <image> (poster-raster.js strips them: inside an SVG loaded as an image they can only draw the broken-image glyph,
|
||||
// and no photo host sends CORS), and (b) PRINT as its own document from PosterWall, where the photos load. The pixel
|
||||
// side of this (a data: photo draws, an external slot stays flat) is check:side-lane-photo-pixels, a browser gate.
|
||||
// --self-test: a renderer copy WITHOUT the lane clamp must FAIL the crossing arm, a raster that keeps external
|
||||
// images must FAIL arm (a), and a PosterWall print that rasterises them must FAIL arm (b) (else the arms prove nothing).
|
||||
// Exit 0 pass, 1 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 J = (p) => JSON.parse(readFileSync(join(ROOT, p), 'utf8'))
|
||||
const RENDER = join(ROOT, 'library/packet/poster-render.js')
|
||||
|
||||
function synthetic(n = 855) {
|
||||
let seed = 20261001
|
||||
const rnd = () => ((seed = (seed * 1103515245 + 12345) % 2147483648) / 2147483648)
|
||||
const syll = ['ka', 'ra', 'mu', 'thi', 'ban', 'dho', 'li', 'sa', 'ga', 'pa', 'rwa', 'kot', 'ni', 'al', 'u']
|
||||
const centres = Array.from({ length: 60 }, () => [68 + rnd() * 28, 8 + rnd() * 27])
|
||||
const pins = []
|
||||
for (let i = 0; i < n; i++) {
|
||||
const c = centres[Math.floor(rnd() * centres.length)]
|
||||
const stacked = rnd() < 0.15 && pins.length // an exact duplicate of an earlier position
|
||||
const lng = stacked ? pins[pins.length - 1].lng : c[0] + (rnd() - 0.5) * 1.6
|
||||
const lat = stacked ? pins[pins.length - 1].lat : c[1] + (rnd() - 0.5) * 1.6
|
||||
const words = 1 + Math.floor(rnd() * (rnd() < 0.05 ? 5 : 2))
|
||||
const name = Array.from({ length: words }, () => Array.from({ length: 2 + Math.floor(rnd() * 3) }, () => syll[Math.floor(rnd() * syll.length)]).join('')).join(' ')
|
||||
.replace(/^./, (m) => m.toUpperCase()) + ` ${i}`
|
||||
// real (an allowed photo host), placeholder, or a non-placeholder on a host that is NOT allowed (must not draw)
|
||||
const roll = rnd()
|
||||
const image_url = roll < 0.18 ? `https://media.joshuaproject.net/public/assets/media/profiles/photos/p${i}.jpg`
|
||||
: roll < 0.22 ? `http://elsewhere.example/p/${i}.jpg`
|
||||
: 'https://s3.doxa.life/no-photo-images/asia.jpg'
|
||||
pins.push({ name, lat, lng, slug: `g-${i}`, country_code: 'IND', population: 1000 + i, image_url })
|
||||
}
|
||||
return pins
|
||||
}
|
||||
|
||||
async function measure(createPosterGenerator, format, pins) {
|
||||
const adm0 = J('public/dx-maps/assets/boundaries/world-adm0.geojson')
|
||||
const series = J('library/scripts/posters/series.json')
|
||||
const zone = { id: 'country-ind', zoneKey: 'country-ind', label: 'India', count: pins.length, color: '#d4a640', adm0: ['IND'], selector: { type: 'country_codes', values: ['IND'] } }
|
||||
const zoneFC = { type: 'FeatureCollection', features: adm0.features.filter((f) => f.properties?.iso3 === 'IND') }
|
||||
const gen = createPosterGenerator({ env: { FORMAT: format, SPARSE: '1', POSTER_DATE: '2026-10-01' }, SERIES_DEF: series.country, ISO3_NAME: { IND: 'India' }, adm0, adm1Ind: null, adm1Chn: null, nePlaces: [], series: { totalGroups: pins.length, totalLists: 1, index: 1 } })
|
||||
const out = await gen.renderPoster(zone, pins, zoneFC)
|
||||
const html = out.html || ''
|
||||
const names = [...html.matchAll(/<text class="snm"[^>]*>([^<]*)<\/text>/g)].map((m) => m[1])
|
||||
const photos = (html.match(/<image href="/g) || []).length
|
||||
// re-measure crossings from the SVG itself, independent of the renderer's own count
|
||||
const polys = [...html.matchAll(/class="slead" d="M([^"]+)"/g)].map((m) => m[1].split('L').map((q) => q.trim().split(' ').map(Number)))
|
||||
const segs = polys.map((pl) => pl.slice(1).map((q, k) => [pl[k], q]))
|
||||
const sx = (p, q, r) => (r[1] - p[1]) * (q[0] - p[0]) - (q[1] - p[1]) * (r[0] - p[0])
|
||||
const X = (a, b) => (sx(a[0], a[1], b[0]) > 0) !== (sx(a[0], a[1], b[1]) > 0) && (sx(b[0], b[1], a[0]) > 0) !== (sx(b[0], b[1], a[1]) > 0)
|
||||
let crossings = 0
|
||||
for (let i = 0; i < segs.length; i++) for (let j = i + 1; j < segs.length; j++) if (segs[i].some((a) => segs[j].some((b) => X(a, b)))) crossings++
|
||||
return { stats: out.sideLanes, names, photos, crossings, leaders: polys.length }
|
||||
}
|
||||
|
||||
function judge(format, m, pins, wantPhotos) {
|
||||
const fails = []
|
||||
const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
||||
const want = new Map(pins.map((p) => [esc(p.name), 0]))
|
||||
for (const n of m.names) if (want.has(n)) want.set(n, want.get(n) + 1)
|
||||
const missing = [...want].filter(([, v]) => v === 0).length, dup = [...want].filter(([, v]) => v > 1).length
|
||||
if (!m.stats) fails.push(`${format}: renderPoster returned no sideLanes stats (not a side-lane format?)`)
|
||||
if (m.names.length !== pins.length || missing || dup) fails.push(`${format}: ${m.names.length} names drawn for ${pins.length} groups (${missing} missing, ${dup} duplicated)`)
|
||||
if (m.crossings !== 0) fails.push(`${format}: ${m.crossings} leader crossings (re-measured from the SVG)`)
|
||||
if (m.stats && m.stats.crossings !== m.crossings) fails.push(`${format}: the renderer reports ${m.stats.crossings} crossings, the SVG has ${m.crossings}`)
|
||||
if (m.stats && m.stats.overflowRows) fails.push(`${format}: ${m.stats.overflowRows} rows overflow the band`)
|
||||
const real = pins.filter((p) => /^https:\/\/media\.joshuaproject\.net\//.test(p.image_url)).length // the allowed ones only
|
||||
if (m.photos !== (wantPhotos ? real : 0)) fails.push(`${format}: ${m.photos} photos drawn, expected ${wantPhotos ? real : 0}`)
|
||||
return fails
|
||||
}
|
||||
|
||||
// (a) + (b), on the 60x96 sheet's HTML. rasterSrc / wallSrc are the source texts, so a poison can swap them.
|
||||
async function photoArms(html, rasterSrc, wallSrc) {
|
||||
const fails = []
|
||||
const m = await import('data:text/javascript;base64,' + Buffer.from(rasterSrc).toString('base64'))
|
||||
const ext = (t) => (t.match(/<image\b[^>]*?\bhref="(?!data:)/g) || []).length
|
||||
if (!ext(html)) fails.push('photos: the 60x96 sheet carries no external photo, so these arms measure nothing')
|
||||
if (typeof m.hasExternalImages !== 'function' || !m.hasExternalImages(html)) fails.push('photos: poster-raster.js hasExternalImages() does not see the sheet\'s photos')
|
||||
// what rasterPoster hands to new Image(): its own first line, inlineSvg + whatever it applies
|
||||
const body = (rasterSrc.match(/export async function rasterPoster[\s\S]*?const url = /) || [''])[0]
|
||||
const prep = (body.match(/const svg = ([^;]+);/) || [])[1] || ''
|
||||
const svg = prep ? new Function('inlineSvg', 'stripExternalImages', 'posterHtml', `return ${prep}`)(m.inlineSvg, m.stripExternalImages, html) : ''
|
||||
if (!prep) fails.push('photos: could not find what rasterPoster rasterises (const svg = ...)')
|
||||
else if (ext(svg)) fails.push(`photos: rasterPoster would rasterise ${ext(svg)} external <image> (each one prints the broken-image glyph)`)
|
||||
// (b) the wall's generated-sheet branch prints a sheet with external images as a document, before any raster
|
||||
const branch = (wallSrc.match(/if \(props\.sheet\) \{[\s\S]*?rasterPoster\(html/) || [''])[0]
|
||||
if (!/posterAction === 'print' && hasExternalImages\(html\)\) \{ await printSheetDocument\(html\); return \}/.test(branch)) fails.push('photos: PosterWall prints a sheet with photos through the raster (they cannot draw there)')
|
||||
if (!/async function printSheetDocument\(html\)[\s\S]{0,400}printDocument\(html,/.test(wallSrc)) fails.push('photos: printSheetDocument does not hand the sheet itself to printDocument')
|
||||
return fails
|
||||
}
|
||||
const RASTER = join(ROOT, 'library/packet/poster-raster.js'), WALL = join(ROOT, 'library/components/poster/PosterWall.vue')
|
||||
async function sheetHtml(createPosterGenerator, pins) {
|
||||
const adm0 = J('public/dx-maps/assets/boundaries/world-adm0.geojson')
|
||||
const gen = createPosterGenerator({ env: { FORMAT: '60x96', SPARSE: '1', POSTER_DATE: '2026-10-01' }, SERIES_DEF: J('library/scripts/posters/series.json').country, ISO3_NAME: { IND: 'India' }, adm0, adm1Ind: null, adm1Chn: null, nePlaces: [], series: { totalGroups: pins.length, totalLists: 1, index: 1 } })
|
||||
const zone = { id: 'country-ind', zoneKey: 'country-ind', label: 'India', count: pins.length, color: '#d4a640', adm0: ['IND'], selector: { type: 'country_codes', values: ['IND'] } }
|
||||
return (await gen.renderPoster(zone, pins, { type: 'FeatureCollection', features: adm0.features.filter((f) => f.properties?.iso3 === 'IND') })).html || ''
|
||||
}
|
||||
|
||||
const selfTest = process.argv.includes('--self-test')
|
||||
const src = readFileSync(RENDER, 'utf8')
|
||||
const pins = synthetic()
|
||||
if (selfTest) {
|
||||
// POISON: drop the lane clamp (each lane at its own pin's height); stubs/runs/fans must then cross on this data
|
||||
const a = 'let y = Math.min(Math.max(c.y, lo + LANE_GAP), hi - LANE_GAP);'
|
||||
if (!src.includes(a)) { console.log('NOT MEASURED: the lane clamp line moved; update the poison'); process.exitCode = 75 }
|
||||
else {
|
||||
const poisoned = src.replace(a, 'let y = c.y; lo = -Infinity; hi = Infinity;')
|
||||
const { createPosterGenerator } = await import('data:text/javascript;base64,' + Buffer.from(poisoned).toString('base64'))
|
||||
const m = await measure(createPosterGenerator, '60x96', pins)
|
||||
const f = judge('60x96', m, pins, true)
|
||||
console.log(f.some((x) => /crossings/.test(x)) ? `✓ caught: no lane clamp -> ${m.crossings} crossings` : '✗ MISSED: the crossing arm stayed green without the lane clamp')
|
||||
process.exitCode = f.some((x) => /crossings/.test(x)) ? 0 : 1
|
||||
}
|
||||
const { createPosterGenerator } = await import(RENDER)
|
||||
const html = await sheetHtml(createPosterGenerator, pins)
|
||||
const rs = readFileSync(RASTER, 'utf8'), ws = readFileSync(WALL, 'utf8')
|
||||
for (const [name, r, w, rx] of [
|
||||
['a raster that keeps external images', rs.replace('stripExternalImages(inlineSvg(posterHtml))', 'inlineSvg(posterHtml)'), ws, /rasterise \d+ external/],
|
||||
['a wall print that rasterises a sheet with photos', rs, ws.replace("if (props.posterAction === 'print' && hasExternalImages(html)) { await printSheetDocument(html); return }", ''), /through the raster/],
|
||||
]) {
|
||||
if (r === rs && w === ws) { console.log(`NOT MEASURED: the poison anchor for "${name}" moved`); process.exitCode = 75; continue }
|
||||
const f = await photoArms(html, r, w)
|
||||
const ok = f.some((x) => rx.test(x)); console.log(ok ? `✓ caught: ${name} -> ${f.find((x) => rx.test(x))}` : `✗ MISSED: ${name}`)
|
||||
if (!ok) process.exitCode = 1
|
||||
}
|
||||
} else {
|
||||
const { createPosterGenerator } = await import(RENDER)
|
||||
const fails = [], lines = []
|
||||
for (const [format, photos] of [['60x96', true], ['66x66', true], ['36x60', false]]) {
|
||||
const m = await measure(createPosterGenerator, format, pins)
|
||||
fails.push(...judge(format, m, pins, photos))
|
||||
lines.push(`${format}: ${m.names.length} names, ${m.leaders} leaders, ${m.crossings} crossings, ${m.photos} photos, ${m.stats?.namePt} pt`)
|
||||
}
|
||||
const html = await sheetHtml(createPosterGenerator, pins)
|
||||
const pf = await photoArms(html, readFileSync(RASTER, 'utf8'), readFileSync(WALL, 'utf8'))
|
||||
fails.push(...pf); if (!pf.length) lines.push(`photos: ${(html.match(/<image\b[^>]*?\bhref="(?!data:)/g) || []).length} external, 0 reach the raster, the wall prints the sheet as a document`)
|
||||
if (fails.length) { fails.forEach((f) => console.log('FAIL ' + f)); process.exitCode = 1 }
|
||||
else console.log(`PASS check:side-lanes — synthetic ${pins.length} groups (stacked + clustered); ${lines.join(' | ')}`)
|
||||
}
|
||||
|
|
@ -49,7 +49,8 @@ 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)
|
||||
|
||||
// ── drawing a configuration from the library ──────────────────────────────────────────────────
|
||||
const sample = (countryData.countries || []).find((c) => c.id === SAMPLE_COUNTRY_ID)
|
||||
const countryById = (id) => (countryData.countries || []).find((c) => c.id === id)
|
||||
const sample = countryById(SAMPLE_COUNTRY_ID)
|
||||
const recordsFor = createPeopleGroupRecords({
|
||||
apiBase: () => profileConfig.value?.apiBaseUrl || getApiBaseUrl(),
|
||||
lang: () => getRuntimeLocale() || detectLocale(),
|
||||
|
|
@ -58,7 +59,11 @@ const recordsFor = createPeopleGroupRecords({
|
|||
// The ONE path every surface draws a country sheet through (the Country Posters wall, the Regions tab),
|
||||
// so the sample carries its region hue and its "map N of M" exactly as the wall prints it.
|
||||
const sheetFor = makeCountrySheet({ records: recordsFor, assetsBase: () => assetsBase.value, countries: countryData.countries || [] })
|
||||
const countrySheet = (format) => (sample ? sheetFor(sample, format) : Promise.resolve(''))
|
||||
// A country's sheet at a format, through the shared path; the large side-lane sheets use their own sample (India).
|
||||
const countrySheet = (format, id = SAMPLE_COUNTRY_ID) => {
|
||||
const c = countryById(id)
|
||||
return c ? sheetFor(c, format) : Promise.resolve('')
|
||||
}
|
||||
const ctx = { countrySheet }
|
||||
|
||||
// The printed document's size, measured when it is drawn: what "file size" means in the details.
|
||||
|
|
|
|||
|
|
@ -23,8 +23,14 @@ export const SAMPLE_COUNTRY_ID = 'country-npl'
|
|||
// The library's poster formats (library/packet/poster-render.js FORMATS). The generator is served as a
|
||||
// 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.
|
||||
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' },
|
||||
'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] },
|
||||
|
|
@ -51,8 +57,8 @@ function sheetEntry(format) {
|
|||
legacy: !!f.legacy,
|
||||
renderer: 'library/packet/posterSheets.js generateCountrySheet → library/packet/poster-render.js',
|
||||
settings: { format },
|
||||
sample: SAMPLE_COUNTRY_ID,
|
||||
render: (ctx) => ctx.countrySheet(format),
|
||||
sample: f.sample || SAMPLE_COUNTRY_ID,
|
||||
render: (ctx) => ctx.countrySheet(format, f.sample || SAMPLE_COUNTRY_ID),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ import PosterWall from '@map/components/poster/PosterWall.vue'
|
|||
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 } from '@map/packet/countrySheet.js'
|
||||
import { makeCountrySheet, largeSheet } 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 { onBeforeUnmount } from 'vue'
|
||||
import { cyrillicHeading } from '@map/styles/portedGlobals.js'
|
||||
|
|
@ -366,7 +368,15 @@ function postersFor(region) {
|
|||
// 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().
|
||||
const posterUnsupported = (c) => (c.unsupportedReason ? tKey('messages.countryPosterTooMany') : null)
|
||||
// ★ 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) => (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).
|
||||
const posterRows = (item) => (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.
|
||||
|
|
@ -585,7 +595,7 @@ useShadowStyles(
|
|||
:min-card-px="150"
|
||||
:item-noun-plural="t('Countries')"
|
||||
item-count-key="regions.countryCount"
|
||||
:records="countryRecords ? (item) => countryRecords({ type: 'country', values: [item.iso3] }) : null"
|
||||
:records="countryRecords ? posterRows : null"
|
||||
:sheet="countrySheet"
|
||||
:card-stocks="true"
|
||||
:collapsible="false"
|
||||
|
|
|
|||
|
|
@ -261,8 +261,22 @@ const FORMATS = {
|
|||
// about 1,600px of height against the letter sheet's ~1,200, which is what lets the names grow.
|
||||
// Output: <zone>.packet.html beside the posters (see FILE_SUFFIX), never over them.
|
||||
packet: { w: 1500, h: 1941, gutter: 330, page: 'letter portrait', pageWidthIn: 8.5, mapOnly: true, header: 250, footer: 90 },
|
||||
// ★ LARGE SHEETS WITH SIDE LANES (`sideLanes`): a whole country's hundreds of groups, every one named horizontally
|
||||
// in two staggered columns per side, ONE leader per cluster of co-located groups, crossing-free by construction
|
||||
// (see sideLanesLayout). Same 41.67 px/in as the 3x3, so the header, pins and footer keep their physical size.
|
||||
// `nameMaxPt` caps the names' print size; the layout solves the largest size that fits the rows and columns.
|
||||
'60x96': { w: 2500, h: 4000, gutter: 572, page: '60in 96in', pageWidthIn: 60, sideLanes: true, nameMaxPt: 20.7, photos: true },
|
||||
'66x66': { w: 2750, h: 2750, gutter: 470, page: '66in 66in', pageWidthIn: 66, sideLanes: true, nameMaxPt: 18, photos: true },
|
||||
'36x60': { w: 1500, h: 2500, gutter: 400, page: '36in 60in', pageWidthIn: 36, sideLanes: true, nameMaxPt: 14, photos: false },
|
||||
};
|
||||
const FMT_KEY = FORMATS[process.env.FORMAT] ? process.env.FORMAT : '3x4';
|
||||
// The side-lane marks' styles: emitted ONLY on a sideLanes sheet, so every other format's page is byte-identical.
|
||||
const SIDE_LANES_CSS = `
|
||||
.slead{stroke:#5a5246;stroke-width:0.9;fill:none;opacity:.5}
|
||||
.sbr{stroke:#5a5246;stroke-width:1.1;fill:none}
|
||||
.srow{stroke:#b9ac8c;stroke-width:.3;fill:none;opacity:.7}
|
||||
.spin{fill:#1c1c17;stroke:#f7f4ec;stroke-width:.8}
|
||||
.snm{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-weight:700;fill:#1c1c17}`;
|
||||
const FMT = FORMATS[FMT_KEY];
|
||||
// Map-only sheets (packet page 1) skip the poster's header and footer furniture — see FORMATS.packet.
|
||||
const MAP_ONLY = FMT.mapOnly === true;
|
||||
|
|
@ -854,7 +868,9 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
const floorDeg = CITIES ? _floor(Math.max(_w, _h)) : 1;
|
||||
// PER-AXIS floor. A collinear cluster (zero span on one axis) must not be padded
|
||||
// as if it were as wide as the other axis — that is what produced a 0.0% spread.
|
||||
const pad = CITIES
|
||||
// A side-lane sheet is the COUNTRY at its largest: a thin margin, since the names, not neighbours, fill the sides.
|
||||
const pad = FMT.sideLanes ? [_w * 0.04, _h * 0.04]
|
||||
: CITIES
|
||||
? [_w * 0.22 + _floor(Math.max(_w, _h * 0.35)), _h * 0.22 + _floor(Math.max(_h, _w * 0.35))]
|
||||
: [_w * 0.22 + floorDeg, _h * 0.22 + floorDeg];
|
||||
const viewBbox = [bbox[0] - pad[0], bbox[1] - pad[1], bbox[2] + pad[0], bbox[3] + pad[1]];
|
||||
|
|
@ -884,12 +900,14 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// frame is exactly what shows them.
|
||||
// FULL SHEET HEIGHT — the map runs from just under the header to just above the
|
||||
// footer rule, so there is no dead band anywhere on the page.
|
||||
const FRAME_H = MAP_H;
|
||||
// A side-lane sheet fits the frame to the country's own shape and centres it: its names run the sheet's full height
|
||||
// beside it, so the frame need not, and a full-height frame would only shrink the country to show its neighbours.
|
||||
const FRAME_H = FMT.sideLanes ? Math.min(MAP_H, FRAME_W / zoneAspect) : MAP_H;
|
||||
// OPTICAL CENTRING, not geometric. A block centred by arithmetic looks like it
|
||||
// is falling off the bottom of the sheet — the optical centre sits ABOVE the
|
||||
// geometric one, which is why framers cut a bottom mat 1.25-1.4x the top. Here
|
||||
// the remainder is split 42/58 so the bottom margin is the larger one.
|
||||
const FRAME_Y = MAP_Y;
|
||||
const FRAME_Y = FMT.sideLanes ? MAP_Y + (MAP_H - FRAME_H) / 2 : MAP_Y;
|
||||
// PROJECT FROM viewBbox, NOT bbox.
|
||||
// These two had drifted apart: `viewBbox` (bbox + 22% context pad) decides WHICH
|
||||
// countries are drawn, while the projection scaled from the bare `bbox` — so the
|
||||
|
|
@ -1247,7 +1265,8 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
//
|
||||
// This is what makes leaders viable again: leaders can only be routed without
|
||||
// crossing if their endpoints are distinct, and the endpoints are these pins.
|
||||
{
|
||||
// A side-lane sheet keeps every pin at its TRUE position: co-located groups share one leader instead.
|
||||
if (!FMT.sideLanes) {
|
||||
// MIN_SEP MUST TRACK THE BADGE SIZE. It was a hardcoded 25 tuned against the
|
||||
// old r=10.8 disc; leaving it fixed while the badge shrinks would keep pushing
|
||||
// pins just as far apart and defeat the whole point of the smaller badge.
|
||||
|
|
@ -1294,7 +1313,126 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// Feed the settled pixel positions back as lng/lat-free overrides.
|
||||
const settled = prePins.map((p) => ({ ...p, _fixed: true }));
|
||||
|
||||
const laid = layoutLabels(settled, proj);
|
||||
// ── SIDE LANES: a whole country's groups on one large sheet (FMT.sideLanes) ───────────────────────────
|
||||
// Every name HORIZONTAL, on the left and right of the map, in two STAGGERED columns per side: the rows alternate
|
||||
// inner/outer at half the line pitch, so each column keeps a full pitch and each row holds exactly ONE name.
|
||||
// Co-located groups (within CL_R px) form a CLUSTER with ONE leader, a bracket and a block of names.
|
||||
// CROSSING-FREE BY CONSTRUCTION, which the numbered design below could not reach at this density:
|
||||
// · one vertical split (the weighted median x) sends each cluster to the nearer side, so runs never meet;
|
||||
// · each leader is a short vertical stub to its own LANE, a horizontal run to the map edge, then a straight fan
|
||||
// to its block; lanes are placed FARTHEST-FROM-THE-EDGE first, each clamped between its neighbouring lanes,
|
||||
// so a stub never jumps a lane, and the fan keeps the lanes' top-to-bottom order (ordered points on two
|
||||
// parallel lines, joined in order, cannot cross). The count is measured and returned, never assumed.
|
||||
// A real photo (never the generic placeholder) sits on the OUTSIDE of its name when the format allows photos.
|
||||
function sideLanesLayout(pinsIn) {
|
||||
const X0 = MAP_X, X1 = MAP_X + MAP_W;
|
||||
const BY0 = LABEL_TOP ?? FRAME_Y, BY1 = LABEL_BOT ?? (FRAME_Y + FRAME_H);
|
||||
const PXI = PW / FMT.pageWidthIn;
|
||||
const pins = pinsIn.map((p) => ({ ...p, px: p._px, py: p._py }));
|
||||
// A photo only from the known people-group photo hosts, over https, and never the generic placeholder.
|
||||
const PHOTO_HOSTS = /^https:\/\/(media\.joshuaproject\.net|joshuaproject\.net|images\.peoplegroups\.org|peoplegroupspublic\.blob\.core\.windows\.net)\//;
|
||||
const photoOf = (p) => (FMT.photos && typeof p.image_url === 'string' && PHOTO_HOSTS.test(p.image_url) && !/no-photo-images/.test(p.image_url) ? p.image_url : null);
|
||||
const CL_R = 14, clusters = [];
|
||||
for (const p of pins) {
|
||||
const c = clusters.find((q) => Math.hypot(q.x - p.px, q.y - p.py) <= CL_R);
|
||||
if (c) { c.items.push(p); c.x += (p.px - c.x) / c.items.length; c.y += (p.py - c.y) / c.items.length; }
|
||||
else clusters.push({ x: p.px, y: p.py, items: [p] });
|
||||
}
|
||||
for (const c of clusters) c.items.sort((a, b) => String(a.name).localeCompare(String(b.name), 'en'));
|
||||
const byX = [...clusters].sort((a, b) => a.x - b.x);
|
||||
let acc = 0, split = byX.length ? byX[0].x : 0;
|
||||
for (const c of byX) { acc += c.items.length; if (acc >= pins.length / 2) { split = c.x; break; } }
|
||||
for (const c of clusters) c.side = c.x <= split ? 'L' : 'R';
|
||||
// THE TYPE: the largest size (capped by nameMaxPt) whose rows fit the band and whose two columns fit the gutter.
|
||||
const FAN = 70, GAP = 10, PHOTO_GAP = 4;
|
||||
const room = GUTTER - (SHEET_EDGE + 8) - FAN - GAP;
|
||||
const photoPx = (f) => Math.floor(f * 1.18) - 1;
|
||||
const longest = (f) => Math.max(...pins.map((p) => textW(String(p.name), f) * 1.06 + (photoOf(p) ? photoPx(f) + PHOTO_GAP : 0)));
|
||||
const lineCount = (sd) => clusters.filter((c) => c.side === sd).reduce((a, c) => a + c.items.length, 0);
|
||||
const maxLines = Math.max(1, lineCount('L'), lineCount('R'));
|
||||
let font = Math.min((FMT.nameMaxPt || 14) / 72 * PXI, (BY1 - BY0) / (maxLines * 0.59 * 1.02));
|
||||
while (font > 5 && 2 * (longest(font) + 8) > room) font -= 0.1;
|
||||
const ROW = font * 1.18 / 2, PHOTO = photoPx(font), COLW = longest(font) + 8;
|
||||
// LANES, farthest from the edge first (see above). PIN_CLEAR: within its safe gap a lane prefers a height
|
||||
// that passes no other pin, at most 7.5 px from its own pin.
|
||||
const LANE_GAP = 1.5, PIN_CLEAR = 4.5;
|
||||
let laneJumps = 0;
|
||||
for (const sd of ['L', 'R']) {
|
||||
const L = sd === 'L';
|
||||
const mine = clusters.filter((c) => c.side === sd).sort((a, b) => (L ? b.x - a.x : a.x - b.x));
|
||||
const lanes = [];
|
||||
for (const c of mine) {
|
||||
let lo = -Infinity, hi = Infinity;
|
||||
for (const ly of lanes) { if (ly < c.y && ly > lo) lo = ly; if (ly >= c.y && ly < hi) hi = ly; }
|
||||
let y = Math.min(Math.max(c.y, lo + LANE_GAP), hi - LANE_GAP);
|
||||
if (lo + LANE_GAP > hi - LANE_GAP) { y = (lo + hi) / 2; laneJumps++; }
|
||||
else {
|
||||
const a0 = L ? X0 : c.x, a1 = L ? c.x : X1;
|
||||
const blockers = clusters.filter((o) => o !== c && o.x > a0 + 1 && o.x < a1 - 1);
|
||||
let best = y, bestHits = Infinity;
|
||||
for (let t = Math.max(lo + LANE_GAP, c.y - 7.5); t <= Math.min(hi - LANE_GAP, c.y + 7.5); t += 0.75) {
|
||||
const hits = blockers.filter((o) => Math.abs(o.y - t) < PIN_CLEAR).length;
|
||||
if (hits < bestHits || (hits === bestHits && Math.abs(t - c.y) < Math.abs(best - c.y))) { best = t; bestHits = hits; }
|
||||
}
|
||||
if (bestHits !== Infinity) y = best;
|
||||
}
|
||||
c.lane = y; lanes.push(y);
|
||||
}
|
||||
}
|
||||
// BLOCKS, stacked top to bottom in lane order, each as near its lane as the rows allow.
|
||||
const rowsAvail = Math.floor((BY1 - BY0) / ROW);
|
||||
let leadersS = '', labelsS = '', dotsS = '', overflow = 0;
|
||||
const placed = [];
|
||||
for (const sd of ['L', 'R']) {
|
||||
const L = sd === 'L';
|
||||
const blocks = clusters.filter((c) => c.side === sd).sort((a, b) => a.lane - b.lane)
|
||||
.map((c) => ({ c, n: c.items.length, want: (c.lane - BY0) / ROW }));
|
||||
const pos = []; let cur = 0;
|
||||
for (const b of blocks) { const q = Math.max(cur, Math.round(b.want - b.n / 2)); pos.push(q); cur = q + b.n; }
|
||||
let over = cur - rowsAvail;
|
||||
for (let k = pos.length - 1; k >= 0 && over > 0; k--) {
|
||||
const lim = k ? pos[k - 1] + blocks[k - 1].n : 0, can = Math.min(over, pos[k] - lim);
|
||||
for (let j = k; j < pos.length; j++) pos[j] -= can;
|
||||
over -= can;
|
||||
}
|
||||
overflow += Math.max(0, over);
|
||||
const edgeX = L ? X0 : X1, portX = L ? X0 - FAN : X1 + FAN;
|
||||
const innerX = L ? portX - 6 : portX + 6, outerX = L ? innerX - COLW : innerX + COLW;
|
||||
blocks.forEach((b, k) => {
|
||||
const c = b.c, r0 = pos[k], midY = BY0 + (r0 + b.n / 2) * ROW;
|
||||
leadersS += `<path class="slead" d="M${c.x.toFixed(1)} ${c.y.toFixed(1)}L${c.x.toFixed(1)} ${c.lane.toFixed(2)}L${edgeX.toFixed(1)} ${c.lane.toFixed(2)}L${portX.toFixed(1)} ${midY.toFixed(1)}"/>`;
|
||||
if (b.n > 1) leadersS += `<path class="sbr" d="M${portX.toFixed(1)} ${(BY0 + r0 * ROW + 1).toFixed(1)}V${(BY0 + (r0 + b.n) * ROW - 1).toFixed(1)}"/>`;
|
||||
dotsS += `<circle class="spin" cx="${c.x.toFixed(1)}" cy="${c.y.toFixed(1)}" r="${b.n > 1 ? 4.4 : 3.2}"/>`;
|
||||
c.items.forEach((g, j) => {
|
||||
const r = r0 + j, rowY = BY0 + (r + 0.5) * ROW, x = r % 2 === 0 ? innerX : outerX;
|
||||
if (r % 2 === 1) leadersS += `<path class="srow" d="M${innerX.toFixed(1)} ${rowY.toFixed(1)}H${(L ? outerX + 3 : outerX - 3).toFixed(1)}"/>`;
|
||||
labelsS += `<text class="snm" x="${x.toFixed(1)}" y="${(rowY + font * 0.34).toFixed(1)}" font-size="${font.toFixed(2)}"${L ? ' text-anchor="end"' : ''}>${esc(String(g.name))}</text>`;
|
||||
const ph = photoOf(g);
|
||||
if (ph) {
|
||||
const tw = textW(String(g.name), font) * 1.06;
|
||||
const px = L ? x - tw - PHOTO_GAP - PHOTO : x + tw + PHOTO_GAP;
|
||||
labelsS += `<image href="${esc(ph)}" x="${px.toFixed(1)}" y="${(rowY - PHOTO / 2).toFixed(1)}" width="${PHOTO}" height="${PHOTO}" preserveAspectRatio="xMidYMid slice"/>`;
|
||||
}
|
||||
placed.push({ g, side: sd, r, photo: !!ph });
|
||||
});
|
||||
});
|
||||
}
|
||||
// MEASURED, not assumed: leader crossings (every segment pair of different leaders).
|
||||
const polys = [...leadersS.matchAll(/class="slead" d="M([^"]+)"/g)].map((m) => m[1].split('L').map((q) => q.trim().split(' ').map(Number)));
|
||||
const segsOf = polys.map((pl) => pl.slice(1).map((q, k) => [pl[k], q]));
|
||||
const sx = (p, q, r) => (r[1] - p[1]) * (q[0] - p[0]) - (q[1] - p[1]) * (r[0] - p[0]);
|
||||
const segX = (a, b) => (sx(a[0], a[1], b[0]) > 0) !== (sx(a[0], a[1], b[1]) > 0) && (sx(b[0], b[1], a[0]) > 0) !== (sx(b[0], b[1], a[1]) > 0);
|
||||
let crossingsS = 0;
|
||||
for (let i = 0; i < segsOf.length; i++) for (let j = i + 1; j < segsOf.length; j++) if (segsOf[i].some((a) => segsOf[j].some((b) => segX(a, b)))) crossingsS++;
|
||||
const stats = { names: placed.length, pins: pins.length, clusters: clusters.length, crossings: crossingsS, overflowRows: overflow,
|
||||
laneJumps, photos: placed.filter((p) => p.photo).length, namePt: +(font / PXI * 72).toFixed(1), photoMm: FMT.photos ? +(PHOTO / PXI * 25.4).toFixed(1) : 0 };
|
||||
return { leaders: leadersS, labels: labelsS, pinDots: dotsS, crossings: crossingsS, pins, stats };
|
||||
}
|
||||
|
||||
const laid = FMT.sideLanes ? [] : layoutLabels(settled, proj);
|
||||
const sides = FMT.sideLanes ? sideLanesLayout(settled) : null;
|
||||
// What the city layer must keep clear of: the drawn pins (laid out, or true positions on a side-lane sheet).
|
||||
const avoidPins = sides ? sides.pins : laid;
|
||||
|
||||
// ── CAPITALS + MAJOR CITIES ────────────────────────────────────────────────
|
||||
// Orientation marks, deliberately SUBORDINATE to the people-group pins. Imhof's
|
||||
|
|
@ -1367,7 +1505,7 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// projected point is not where the badge is drawn. Measuring against the
|
||||
// source coordinate let Saravan's label land 1.4px from a badge while the
|
||||
// guard believed it was clear. This block therefore runs AFTER layoutLabels.
|
||||
for (const pin of laid) {
|
||||
for (const pin of avoidPins) {
|
||||
const px = pin.px, py = pin.py;
|
||||
const dx = px < x0 ? x0 - px : (px > x1 ? px - x1 : 0);
|
||||
const dy = py < yTop ? yTop - py : (py > yBot ? py - yBot : 0);
|
||||
|
|
@ -1697,8 +1835,9 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
const tx = it.laneX + it.dirOut * 5;
|
||||
labels += `<text x="${tx.toFixed(1)}" y="${(it.labelY + it.font * 0.34).toFixed(1)}" text-anchor="${it.tanchor}" font-size="${it.font.toFixed(1)}" class="lbl">${esc(it.name)}</text>`;
|
||||
});
|
||||
if (sides) { leaders += sides.leaders; pinDots += sides.pinDots; labels += sides.labels; }
|
||||
if (!octiOK) console.warn(` ⚠ non-octilinear segment in ${zone.zoneKey}`);
|
||||
const crossings = countPolyCrossings(routes);
|
||||
const crossings = sides ? sides.crossings : countPolyCrossings(routes);
|
||||
const diagFrac = bestD;
|
||||
|
||||
// ── TITLE vs SUBTITLE ──────────────────────────────────────────────────────
|
||||
|
|
@ -2678,8 +2817,8 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
page. Collapse the flow height to the scaled height. */
|
||||
html,body{width:${(FMT.pageWidthIn * 96).toFixed(0)}px;height:${(FMT.pageWidthIn * 96 * FMT.h / FMT.w).toFixed(0)}px;overflow:hidden}
|
||||
}
|
||||
</style></head><body><div class="stage">${svg}</div></body></html>`;
|
||||
return { html, title, octiOK, crossings, diagFrac };
|
||||
${FMT.sideLanes ? SIDE_LANES_CSS + '\n' : ''}</style></head><body><div class="stage">${svg}</div></body></html>`;
|
||||
return sides ? { html, title, octiOK, crossings, diagFrac, sideLanes: sides.stats } : { html, title, octiOK, crossings, diagFrac };
|
||||
}
|
||||
|
||||
return { renderPoster, CITIES, COUNTRIES, FILE_SUFFIX, FMT_KEY, FMT, MAP_ONLY, FORMATS };
|
||||
|
|
|
|||
|
|
@ -259,8 +259,22 @@ const FORMATS = {
|
|||
// about 1,600px of height against the letter sheet's ~1,200, which is what lets the names grow.
|
||||
// Output: <zone>.packet.html beside the posters (see FILE_SUFFIX), never over them.
|
||||
packet: { w: 1500, h: 1941, gutter: 330, page: 'letter portrait', pageWidthIn: 8.5, mapOnly: true, header: 250, footer: 90 },
|
||||
// ★ LARGE SHEETS WITH SIDE LANES (`sideLanes`): a whole country's hundreds of groups, every one named horizontally
|
||||
// in two staggered columns per side, ONE leader per cluster of co-located groups, crossing-free by construction
|
||||
// (see sideLanesLayout). Same 41.67 px/in as the 3x3, so the header, pins and footer keep their physical size.
|
||||
// `nameMaxPt` caps the names' print size; the layout solves the largest size that fits the rows and columns.
|
||||
'60x96': { w: 2500, h: 4000, gutter: 572, page: '60in 96in', pageWidthIn: 60, sideLanes: true, nameMaxPt: 20.7, photos: true },
|
||||
'66x66': { w: 2750, h: 2750, gutter: 470, page: '66in 66in', pageWidthIn: 66, sideLanes: true, nameMaxPt: 18, photos: true },
|
||||
'36x60': { w: 1500, h: 2500, gutter: 400, page: '36in 60in', pageWidthIn: 36, sideLanes: true, nameMaxPt: 14, photos: false },
|
||||
};
|
||||
const FMT_KEY = FORMATS[process.env.FORMAT] ? process.env.FORMAT : '3x4';
|
||||
// The side-lane marks' styles: emitted ONLY on a sideLanes sheet, so every other format's page is byte-identical.
|
||||
const SIDE_LANES_CSS = `
|
||||
.slead{stroke:#5a5246;stroke-width:0.9;fill:none;opacity:.5}
|
||||
.sbr{stroke:#5a5246;stroke-width:1.1;fill:none}
|
||||
.srow{stroke:#b9ac8c;stroke-width:.3;fill:none;opacity:.7}
|
||||
.spin{fill:#1c1c17;stroke:#f7f4ec;stroke-width:.8}
|
||||
.snm{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-weight:700;fill:#1c1c17}`;
|
||||
const FMT = FORMATS[FMT_KEY];
|
||||
// Map-only sheets (packet page 1) skip the poster's header and footer furniture — see FORMATS.packet.
|
||||
const MAP_ONLY = FMT.mapOnly === true;
|
||||
|
|
@ -852,7 +866,9 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
const floorDeg = CITIES ? _floor(Math.max(_w, _h)) : 1;
|
||||
// PER-AXIS floor. A collinear cluster (zero span on one axis) must not be padded
|
||||
// as if it were as wide as the other axis — that is what produced a 0.0% spread.
|
||||
const pad = CITIES
|
||||
// A side-lane sheet is the COUNTRY at its largest: a thin margin, since the names, not neighbours, fill the sides.
|
||||
const pad = FMT.sideLanes ? [_w * 0.04, _h * 0.04]
|
||||
: CITIES
|
||||
? [_w * 0.22 + _floor(Math.max(_w, _h * 0.35)), _h * 0.22 + _floor(Math.max(_h, _w * 0.35))]
|
||||
: [_w * 0.22 + floorDeg, _h * 0.22 + floorDeg];
|
||||
const viewBbox = [bbox[0] - pad[0], bbox[1] - pad[1], bbox[2] + pad[0], bbox[3] + pad[1]];
|
||||
|
|
@ -882,12 +898,14 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// frame is exactly what shows them.
|
||||
// FULL SHEET HEIGHT — the map runs from just under the header to just above the
|
||||
// footer rule, so there is no dead band anywhere on the page.
|
||||
const FRAME_H = MAP_H;
|
||||
// A side-lane sheet fits the frame to the country's own shape and centres it: its names run the sheet's full height
|
||||
// beside it, so the frame need not, and a full-height frame would only shrink the country to show its neighbours.
|
||||
const FRAME_H = FMT.sideLanes ? Math.min(MAP_H, FRAME_W / zoneAspect) : MAP_H;
|
||||
// OPTICAL CENTRING, not geometric. A block centred by arithmetic looks like it
|
||||
// is falling off the bottom of the sheet — the optical centre sits ABOVE the
|
||||
// geometric one, which is why framers cut a bottom mat 1.25-1.4x the top. Here
|
||||
// the remainder is split 42/58 so the bottom margin is the larger one.
|
||||
const FRAME_Y = MAP_Y;
|
||||
const FRAME_Y = FMT.sideLanes ? MAP_Y + (MAP_H - FRAME_H) / 2 : MAP_Y;
|
||||
// PROJECT FROM viewBbox, NOT bbox.
|
||||
// These two had drifted apart: `viewBbox` (bbox + 22% context pad) decides WHICH
|
||||
// countries are drawn, while the projection scaled from the bare `bbox` — so the
|
||||
|
|
@ -1245,7 +1263,8 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
//
|
||||
// This is what makes leaders viable again: leaders can only be routed without
|
||||
// crossing if their endpoints are distinct, and the endpoints are these pins.
|
||||
{
|
||||
// A side-lane sheet keeps every pin at its TRUE position: co-located groups share one leader instead.
|
||||
if (!FMT.sideLanes) {
|
||||
// MIN_SEP MUST TRACK THE BADGE SIZE. It was a hardcoded 25 tuned against the
|
||||
// old r=10.8 disc; leaving it fixed while the badge shrinks would keep pushing
|
||||
// pins just as far apart and defeat the whole point of the smaller badge.
|
||||
|
|
@ -1292,7 +1311,126 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// Feed the settled pixel positions back as lng/lat-free overrides.
|
||||
const settled = prePins.map((p) => ({ ...p, _fixed: true }));
|
||||
|
||||
const laid = layoutLabels(settled, proj);
|
||||
// ── SIDE LANES: a whole country's groups on one large sheet (FMT.sideLanes) ───────────────────────────
|
||||
// Every name HORIZONTAL, on the left and right of the map, in two STAGGERED columns per side: the rows alternate
|
||||
// inner/outer at half the line pitch, so each column keeps a full pitch and each row holds exactly ONE name.
|
||||
// Co-located groups (within CL_R px) form a CLUSTER with ONE leader, a bracket and a block of names.
|
||||
// CROSSING-FREE BY CONSTRUCTION, which the numbered design below could not reach at this density:
|
||||
// · one vertical split (the weighted median x) sends each cluster to the nearer side, so runs never meet;
|
||||
// · each leader is a short vertical stub to its own LANE, a horizontal run to the map edge, then a straight fan
|
||||
// to its block; lanes are placed FARTHEST-FROM-THE-EDGE first, each clamped between its neighbouring lanes,
|
||||
// so a stub never jumps a lane, and the fan keeps the lanes' top-to-bottom order (ordered points on two
|
||||
// parallel lines, joined in order, cannot cross). The count is measured and returned, never assumed.
|
||||
// A real photo (never the generic placeholder) sits on the OUTSIDE of its name when the format allows photos.
|
||||
function sideLanesLayout(pinsIn) {
|
||||
const X0 = MAP_X, X1 = MAP_X + MAP_W;
|
||||
const BY0 = LABEL_TOP ?? FRAME_Y, BY1 = LABEL_BOT ?? (FRAME_Y + FRAME_H);
|
||||
const PXI = PW / FMT.pageWidthIn;
|
||||
const pins = pinsIn.map((p) => ({ ...p, px: p._px, py: p._py }));
|
||||
// A photo only from the known people-group photo hosts, over https, and never the generic placeholder.
|
||||
const PHOTO_HOSTS = /^https:\/\/(media\.joshuaproject\.net|joshuaproject\.net|images\.peoplegroups\.org|peoplegroupspublic\.blob\.core\.windows\.net)\//;
|
||||
const photoOf = (p) => (FMT.photos && typeof p.image_url === 'string' && PHOTO_HOSTS.test(p.image_url) && !/no-photo-images/.test(p.image_url) ? p.image_url : null);
|
||||
const CL_R = 14, clusters = [];
|
||||
for (const p of pins) {
|
||||
const c = clusters.find((q) => Math.hypot(q.x - p.px, q.y - p.py) <= CL_R);
|
||||
if (c) { c.items.push(p); c.x += (p.px - c.x) / c.items.length; c.y += (p.py - c.y) / c.items.length; }
|
||||
else clusters.push({ x: p.px, y: p.py, items: [p] });
|
||||
}
|
||||
for (const c of clusters) c.items.sort((a, b) => String(a.name).localeCompare(String(b.name), 'en'));
|
||||
const byX = [...clusters].sort((a, b) => a.x - b.x);
|
||||
let acc = 0, split = byX.length ? byX[0].x : 0;
|
||||
for (const c of byX) { acc += c.items.length; if (acc >= pins.length / 2) { split = c.x; break; } }
|
||||
for (const c of clusters) c.side = c.x <= split ? 'L' : 'R';
|
||||
// THE TYPE: the largest size (capped by nameMaxPt) whose rows fit the band and whose two columns fit the gutter.
|
||||
const FAN = 70, GAP = 10, PHOTO_GAP = 4;
|
||||
const room = GUTTER - (SHEET_EDGE + 8) - FAN - GAP;
|
||||
const photoPx = (f) => Math.floor(f * 1.18) - 1;
|
||||
const longest = (f) => Math.max(...pins.map((p) => textW(String(p.name), f) * 1.06 + (photoOf(p) ? photoPx(f) + PHOTO_GAP : 0)));
|
||||
const lineCount = (sd) => clusters.filter((c) => c.side === sd).reduce((a, c) => a + c.items.length, 0);
|
||||
const maxLines = Math.max(1, lineCount('L'), lineCount('R'));
|
||||
let font = Math.min((FMT.nameMaxPt || 14) / 72 * PXI, (BY1 - BY0) / (maxLines * 0.59 * 1.02));
|
||||
while (font > 5 && 2 * (longest(font) + 8) > room) font -= 0.1;
|
||||
const ROW = font * 1.18 / 2, PHOTO = photoPx(font), COLW = longest(font) + 8;
|
||||
// LANES, farthest from the edge first (see above). PIN_CLEAR: within its safe gap a lane prefers a height
|
||||
// that passes no other pin, at most 7.5 px from its own pin.
|
||||
const LANE_GAP = 1.5, PIN_CLEAR = 4.5;
|
||||
let laneJumps = 0;
|
||||
for (const sd of ['L', 'R']) {
|
||||
const L = sd === 'L';
|
||||
const mine = clusters.filter((c) => c.side === sd).sort((a, b) => (L ? b.x - a.x : a.x - b.x));
|
||||
const lanes = [];
|
||||
for (const c of mine) {
|
||||
let lo = -Infinity, hi = Infinity;
|
||||
for (const ly of lanes) { if (ly < c.y && ly > lo) lo = ly; if (ly >= c.y && ly < hi) hi = ly; }
|
||||
let y = Math.min(Math.max(c.y, lo + LANE_GAP), hi - LANE_GAP);
|
||||
if (lo + LANE_GAP > hi - LANE_GAP) { y = (lo + hi) / 2; laneJumps++; }
|
||||
else {
|
||||
const a0 = L ? X0 : c.x, a1 = L ? c.x : X1;
|
||||
const blockers = clusters.filter((o) => o !== c && o.x > a0 + 1 && o.x < a1 - 1);
|
||||
let best = y, bestHits = Infinity;
|
||||
for (let t = Math.max(lo + LANE_GAP, c.y - 7.5); t <= Math.min(hi - LANE_GAP, c.y + 7.5); t += 0.75) {
|
||||
const hits = blockers.filter((o) => Math.abs(o.y - t) < PIN_CLEAR).length;
|
||||
if (hits < bestHits || (hits === bestHits && Math.abs(t - c.y) < Math.abs(best - c.y))) { best = t; bestHits = hits; }
|
||||
}
|
||||
if (bestHits !== Infinity) y = best;
|
||||
}
|
||||
c.lane = y; lanes.push(y);
|
||||
}
|
||||
}
|
||||
// BLOCKS, stacked top to bottom in lane order, each as near its lane as the rows allow.
|
||||
const rowsAvail = Math.floor((BY1 - BY0) / ROW);
|
||||
let leadersS = '', labelsS = '', dotsS = '', overflow = 0;
|
||||
const placed = [];
|
||||
for (const sd of ['L', 'R']) {
|
||||
const L = sd === 'L';
|
||||
const blocks = clusters.filter((c) => c.side === sd).sort((a, b) => a.lane - b.lane)
|
||||
.map((c) => ({ c, n: c.items.length, want: (c.lane - BY0) / ROW }));
|
||||
const pos = []; let cur = 0;
|
||||
for (const b of blocks) { const q = Math.max(cur, Math.round(b.want - b.n / 2)); pos.push(q); cur = q + b.n; }
|
||||
let over = cur - rowsAvail;
|
||||
for (let k = pos.length - 1; k >= 0 && over > 0; k--) {
|
||||
const lim = k ? pos[k - 1] + blocks[k - 1].n : 0, can = Math.min(over, pos[k] - lim);
|
||||
for (let j = k; j < pos.length; j++) pos[j] -= can;
|
||||
over -= can;
|
||||
}
|
||||
overflow += Math.max(0, over);
|
||||
const edgeX = L ? X0 : X1, portX = L ? X0 - FAN : X1 + FAN;
|
||||
const innerX = L ? portX - 6 : portX + 6, outerX = L ? innerX - COLW : innerX + COLW;
|
||||
blocks.forEach((b, k) => {
|
||||
const c = b.c, r0 = pos[k], midY = BY0 + (r0 + b.n / 2) * ROW;
|
||||
leadersS += `<path class="slead" d="M${c.x.toFixed(1)} ${c.y.toFixed(1)}L${c.x.toFixed(1)} ${c.lane.toFixed(2)}L${edgeX.toFixed(1)} ${c.lane.toFixed(2)}L${portX.toFixed(1)} ${midY.toFixed(1)}"/>`;
|
||||
if (b.n > 1) leadersS += `<path class="sbr" d="M${portX.toFixed(1)} ${(BY0 + r0 * ROW + 1).toFixed(1)}V${(BY0 + (r0 + b.n) * ROW - 1).toFixed(1)}"/>`;
|
||||
dotsS += `<circle class="spin" cx="${c.x.toFixed(1)}" cy="${c.y.toFixed(1)}" r="${b.n > 1 ? 4.4 : 3.2}"/>`;
|
||||
c.items.forEach((g, j) => {
|
||||
const r = r0 + j, rowY = BY0 + (r + 0.5) * ROW, x = r % 2 === 0 ? innerX : outerX;
|
||||
if (r % 2 === 1) leadersS += `<path class="srow" d="M${innerX.toFixed(1)} ${rowY.toFixed(1)}H${(L ? outerX + 3 : outerX - 3).toFixed(1)}"/>`;
|
||||
labelsS += `<text class="snm" x="${x.toFixed(1)}" y="${(rowY + font * 0.34).toFixed(1)}" font-size="${font.toFixed(2)}"${L ? ' text-anchor="end"' : ''}>${esc(String(g.name))}</text>`;
|
||||
const ph = photoOf(g);
|
||||
if (ph) {
|
||||
const tw = textW(String(g.name), font) * 1.06;
|
||||
const px = L ? x - tw - PHOTO_GAP - PHOTO : x + tw + PHOTO_GAP;
|
||||
labelsS += `<image href="${esc(ph)}" x="${px.toFixed(1)}" y="${(rowY - PHOTO / 2).toFixed(1)}" width="${PHOTO}" height="${PHOTO}" preserveAspectRatio="xMidYMid slice"/>`;
|
||||
}
|
||||
placed.push({ g, side: sd, r, photo: !!ph });
|
||||
});
|
||||
});
|
||||
}
|
||||
// MEASURED, not assumed: leader crossings (every segment pair of different leaders).
|
||||
const polys = [...leadersS.matchAll(/class="slead" d="M([^"]+)"/g)].map((m) => m[1].split('L').map((q) => q.trim().split(' ').map(Number)));
|
||||
const segsOf = polys.map((pl) => pl.slice(1).map((q, k) => [pl[k], q]));
|
||||
const sx = (p, q, r) => (r[1] - p[1]) * (q[0] - p[0]) - (q[1] - p[1]) * (r[0] - p[0]);
|
||||
const segX = (a, b) => (sx(a[0], a[1], b[0]) > 0) !== (sx(a[0], a[1], b[1]) > 0) && (sx(b[0], b[1], a[0]) > 0) !== (sx(b[0], b[1], a[1]) > 0);
|
||||
let crossingsS = 0;
|
||||
for (let i = 0; i < segsOf.length; i++) for (let j = i + 1; j < segsOf.length; j++) if (segsOf[i].some((a) => segsOf[j].some((b) => segX(a, b)))) crossingsS++;
|
||||
const stats = { names: placed.length, pins: pins.length, clusters: clusters.length, crossings: crossingsS, overflowRows: overflow,
|
||||
laneJumps, photos: placed.filter((p) => p.photo).length, namePt: +(font / PXI * 72).toFixed(1), photoMm: FMT.photos ? +(PHOTO / PXI * 25.4).toFixed(1) : 0 };
|
||||
return { leaders: leadersS, labels: labelsS, pinDots: dotsS, crossings: crossingsS, pins, stats };
|
||||
}
|
||||
|
||||
const laid = FMT.sideLanes ? [] : layoutLabels(settled, proj);
|
||||
const sides = FMT.sideLanes ? sideLanesLayout(settled) : null;
|
||||
// What the city layer must keep clear of: the drawn pins (laid out, or true positions on a side-lane sheet).
|
||||
const avoidPins = sides ? sides.pins : laid;
|
||||
|
||||
// ── CAPITALS + MAJOR CITIES ────────────────────────────────────────────────
|
||||
// Orientation marks, deliberately SUBORDINATE to the people-group pins. Imhof's
|
||||
|
|
@ -1365,7 +1503,7 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
// projected point is not where the badge is drawn. Measuring against the
|
||||
// source coordinate let Saravan's label land 1.4px from a badge while the
|
||||
// guard believed it was clear. This block therefore runs AFTER layoutLabels.
|
||||
for (const pin of laid) {
|
||||
for (const pin of avoidPins) {
|
||||
const px = pin.px, py = pin.py;
|
||||
const dx = px < x0 ? x0 - px : (px > x1 ? px - x1 : 0);
|
||||
const dy = py < yTop ? yTop - py : (py > yBot ? py - yBot : 0);
|
||||
|
|
@ -1695,8 +1833,9 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
const tx = it.laneX + it.dirOut * 5;
|
||||
labels += `<text x="${tx.toFixed(1)}" y="${(it.labelY + it.font * 0.34).toFixed(1)}" text-anchor="${it.tanchor}" font-size="${it.font.toFixed(1)}" class="lbl">${esc(it.name)}</text>`;
|
||||
});
|
||||
if (sides) { leaders += sides.leaders; pinDots += sides.pinDots; labels += sides.labels; }
|
||||
if (!octiOK) console.warn(` ⚠ non-octilinear segment in ${zone.zoneKey}`);
|
||||
const crossings = countPolyCrossings(routes);
|
||||
const crossings = sides ? sides.crossings : countPolyCrossings(routes);
|
||||
const diagFrac = bestD;
|
||||
|
||||
// ── TITLE vs SUBTITLE ──────────────────────────────────────────────────────
|
||||
|
|
@ -2676,8 +2815,8 @@ async function renderPoster(zone, pins, zoneFC) {
|
|||
page. Collapse the flow height to the scaled height. */
|
||||
html,body{width:${(FMT.pageWidthIn * 96).toFixed(0)}px;height:${(FMT.pageWidthIn * 96 * FMT.h / FMT.w).toFixed(0)}px;overflow:hidden}
|
||||
}
|
||||
</style></head><body><div class="stage">${svg}</div></body></html>`;
|
||||
return { html, title, octiOK, crossings, diagFrac };
|
||||
${FMT.sideLanes ? SIDE_LANES_CSS + '\n' : ''}</style></head><body><div class="stage">${svg}</div></body></html>`;
|
||||
return sides ? { html, title, octiOK, crossings, diagFrac, sideLanes: sides.stats } : { html, title, octiOK, crossings, diagFrac };
|
||||
}
|
||||
|
||||
return { renderPoster, CITIES, COUNTRIES, FILE_SUFFIX, FMT_KEY, FMT, MAP_ONLY, FORMATS };
|
||||
|
|
|
|||
BIN
public/dx-maps/assets/posters/country-ind.webp
Normal file
BIN
public/dx-maps/assets/posters/country-ind.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
BIN
public/dx-maps/assets/previews/country-ind.webp
Normal file
BIN
public/dx-maps/assets/previews/country-ind.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa" 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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -81,7 +81,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4" crossorigin="anonymous"></script>
|
||||
<script src="../country-posters.js" integrity="sha384-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -200,7 +200,7 @@
|
|||
"bundle": "country-posters",
|
||||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"output": "dx-maps/country-posters/country-posters.js",
|
||||
"integrity": "sha384-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4",
|
||||
"integrity": "sha384-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa",
|
||||
"elements": [
|
||||
"country-posters-map"
|
||||
],
|
||||
|
|
@ -223,8 +223,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -240,7 +240,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -256,8 +256,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -418,7 +418,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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo",
|
||||
"integrity": "sha384-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ",
|
||||
"elements": [
|
||||
"doxa-research-map"
|
||||
],
|
||||
|
|
@ -437,8 +437,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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo\" 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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo\" 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-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ\" 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-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ\" 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": [
|
||||
{
|
||||
|
|
@ -882,7 +882,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t",
|
||||
"integrity": "sha384-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz",
|
||||
"elements": [
|
||||
"my-upg-100-list-map"
|
||||
],
|
||||
|
|
@ -905,7 +905,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -921,8 +921,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -938,8 +938,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1070,7 +1070,7 @@
|
|||
"bundle": "poster-design",
|
||||
"scriptUrl": "./poster-design/poster-design.js",
|
||||
"output": "dx-maps/poster-design/poster-design.js",
|
||||
"integrity": "sha384-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy",
|
||||
"integrity": "sha384-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE",
|
||||
"elements": [
|
||||
"poster-design"
|
||||
],
|
||||
|
|
@ -1093,7 +1093,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-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy\" 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-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1109,7 +1109,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-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy\" 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-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1125,7 +1125,7 @@
|
|||
"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-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy\" 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-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1244,7 +1244,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "dx-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG",
|
||||
"integrity": "sha384-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -1267,8 +1267,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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1284,7 +1284,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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1300,8 +1300,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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1435,7 +1435,7 @@
|
|||
"bundle": "upg-neighbor",
|
||||
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
|
||||
"output": "dx-maps/upg-neighbor/upg-neighbor.js",
|
||||
"integrity": "sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV",
|
||||
"integrity": "sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5",
|
||||
"elements": [
|
||||
"upg-neighbor"
|
||||
],
|
||||
|
|
@ -1454,8 +1454,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-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV\" 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-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV\" 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-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5\" 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-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5\" 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": [
|
||||
{
|
||||
|
|
|
|||
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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo" crossorigin="anonymous"></script>
|
||||
<script src="../doxa-research-map.js" integrity="sha384-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ" 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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo" crossorigin="anonymous"></script>
|
||||
<script src="../../doxa-research-map.js" integrity="sha384-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@
|
|||
"bundle": "country-posters",
|
||||
"scriptUrl": "./country-posters/country-posters.js",
|
||||
"output": "dx-maps/country-posters/country-posters.js",
|
||||
"integrity": "sha384-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4",
|
||||
"integrity": "sha384-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa",
|
||||
"elements": [
|
||||
"country-posters-map"
|
||||
],
|
||||
|
|
@ -223,8 +223,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -240,7 +240,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -256,8 +256,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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-doJ/wcJUzZZxH+2YMk/HXNMQZBj0xbz2lPjdYdN1MELUAtg9TVIaQuECnK89fFj4\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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-j2tAKqLk1st1HqELMPQM87d2coCSU5hC1dUSPtH7PHzlM6szgNgZO3N2riSLi/sa\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -418,7 +418,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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo",
|
||||
"integrity": "sha384-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ",
|
||||
"elements": [
|
||||
"doxa-research-map"
|
||||
],
|
||||
|
|
@ -437,8 +437,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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo\" 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-IKH+tz4B3gaLjBGLwUEOjdUv80EN5fehxlz3WvmVcLA5b5SJkWBadXbRvVrOH8Qo\" 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-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ\" 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-98nCFq2fbwwHraxb0U1B1hLp5SvI7gtAWeYtO1/Y9rR3segQDa+EhW9ttb+6V0JJ\" 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": [
|
||||
{
|
||||
|
|
@ -882,7 +882,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t",
|
||||
"integrity": "sha384-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz",
|
||||
"elements": [
|
||||
"my-upg-100-list-map"
|
||||
],
|
||||
|
|
@ -905,7 +905,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -921,8 +921,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -938,8 +938,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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1070,7 +1070,7 @@
|
|||
"bundle": "poster-design",
|
||||
"scriptUrl": "./poster-design/poster-design.js",
|
||||
"output": "dx-maps/poster-design/poster-design.js",
|
||||
"integrity": "sha384-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy",
|
||||
"integrity": "sha384-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE",
|
||||
"elements": [
|
||||
"poster-design"
|
||||
],
|
||||
|
|
@ -1093,7 +1093,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-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy\" 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-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1109,7 +1109,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-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy\" 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-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1125,7 +1125,7 @@
|
|||
"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-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy\" 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-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1244,7 +1244,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "dx-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG",
|
||||
"integrity": "sha384-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -1267,8 +1267,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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1284,7 +1284,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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1300,8 +1300,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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -1435,7 +1435,7 @@
|
|||
"bundle": "upg-neighbor",
|
||||
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
|
||||
"output": "dx-maps/upg-neighbor/upg-neighbor.js",
|
||||
"integrity": "sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV",
|
||||
"integrity": "sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5",
|
||||
"elements": [
|
||||
"upg-neighbor"
|
||||
],
|
||||
|
|
@ -1454,8 +1454,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-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV\" 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-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV\" 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-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5\" 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-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5\" 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": [
|
||||
{
|
||||
|
|
|
|||
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 <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz" 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 <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <my-upg-100-list-map> custom element. -->
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-AIu4wkHSSNAY2veTjSgoN5TTj+DEGhBt9e6Oo/0kzBdza8wlfgH1RJNQ0F1T8K1t" crossorigin="anonymous"></script>
|
||||
<script src="../my-upg-100-list.js" integrity="sha384-NtqDdNmbGo7qKrs7eR5nhRpDo38s6kUR/7pqzLKS4ZEDQnUxsI2BDIxFRjqzhZGz" 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 <poster-design> custom element. -->
|
||||
<script src="../poster-design.js" integrity="sha384-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy" crossorigin="anonymous"></script>
|
||||
<script src="../poster-design.js" integrity="sha384-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE" 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 <poster-design> custom element. -->
|
||||
<script src="../poster-design.js" integrity="sha384-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy" crossorigin="anonymous"></script>
|
||||
<script src="../poster-design.js" integrity="sha384-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE" 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 <poster-design> custom element. -->
|
||||
<script src="../poster-design.js" integrity="sha384-kxKGb9vfzkFOaVMv8rx+eOITZVBLLlZzEFAJGmdK2RqRmG26cjpsPJwk5fNkFIsy" crossorigin="anonymous"></script>
|
||||
<script src="../poster-design.js" integrity="sha384-CKmfFD79SRXxQB1Gm++Dv65XvYG3jOrxCZEOqyZseYmcJGHFmUyeG3SHaehJDPOE" 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 <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh" 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 <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-dp6rKvmSGk4JOJHAoxuVQ4FRH0A2kXUTFgE8LNti5nZNfiEVUktB+dE7+KoOoMgG" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-NT0EdIwg591zhmVkjlb/pIAfdxAtmbrFdZfThIGlAYW/cqjDA/3I3rkUit9TUguh" 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 <upg-neighbor> custom element. -->
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV" crossorigin="anonymous"></script>
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5" 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 <upg-neighbor> custom element. -->
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV" crossorigin="anonymous"></script>
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5" 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 <upg-neighbor> custom element. -->
|
||||
<script src="../upg-neighbor.js" integrity="sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV" crossorigin="anonymous"></script>
|
||||
<script src="../upg-neighbor.js" integrity="sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5" 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 <upg-neighbor> custom element. -->
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV" crossorigin="anonymous"></script>
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5" 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 <upg-neighbor> custom element. -->
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV" crossorigin="anonymous"></script>
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5" 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 <upg-neighbor> custom element. -->
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-0Pf4NY4Rf/H8EAOdha+6sHazDxWfsjYAZFfiELHHrGhMez2Kj9ttu9elpucQdfRV" crossorigin="anonymous"></script>
|
||||
<script src="../../upg-neighbor.js" integrity="sha384-HFwN9jNuAInFIxFkxcVeQ4OoA44EtBENMaOM9NkrJDLCpFccbRPtnNdSJ/VqC/o5" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue