@escape-game-over/atlas 0.1.109 → 0.1.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/analytics/storage.ts +13 -3
- package/src/astro/BookingWidget.astro +13 -0
- package/src/astro/EscapeAll.astro +62 -0
- package/src/astro/escapeall-calendar.ts +32 -0
- package/src/booking/escapeall.ts +86 -0
- package/src/booking/widget.ts +4 -1
- package/src/index.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@escape-game-over/atlas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Typed, data-driven machinery for static multi-locale, multi-deployment Astro sites.",
|
|
6
6
|
"private": false,
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./astro/consent-banner": "./src/astro/ConsentBanner.astro",
|
|
23
23
|
"./astro/document": "./src/astro/Document.astro",
|
|
24
24
|
"./astro/element": "./src/astro/AtlasElement.astro",
|
|
25
|
+
"./astro/escapeall": "./src/astro/EscapeAll.astro",
|
|
25
26
|
"./astro/escape-navigator": "./src/astro/EscapeNavigator.astro",
|
|
26
27
|
"./astro/four-escape": "./src/astro/FourEscape.astro",
|
|
27
28
|
"./astro/google-map": "./src/astro/GoogleMap.astro",
|
package/src/analytics/storage.ts
CHANGED
|
@@ -53,6 +53,7 @@ export const STORAGE_VENDOR_NAMES = {
|
|
|
53
53
|
clarity: "Microsoft Clarity",
|
|
54
54
|
"4escape": "4escape",
|
|
55
55
|
escapenavigator: "Escape Navigator",
|
|
56
|
+
escapeall: "EscapeAll",
|
|
56
57
|
stripe: "Stripe",
|
|
57
58
|
} as const satisfies Record<StorageVendor | BookingVendor | "stripe", string>;
|
|
58
59
|
|
|
@@ -314,6 +315,13 @@ const BOOKING = {
|
|
|
314
315
|
stripe: true,
|
|
315
316
|
loads: ["google-analytics", "google-ads", "meta"],
|
|
316
317
|
},
|
|
318
|
+
// Read from its script 2026-11-12: nothing as it renders; the phone
|
|
319
|
+
// confirmation while a visitor books. Its queue cookie waits for its own
|
|
320
|
+
// consent cookie, which a site of ours never sets.
|
|
321
|
+
escapeall: {
|
|
322
|
+
kept: ["ea-cell-confirmation"],
|
|
323
|
+
tab: [],
|
|
324
|
+
},
|
|
317
325
|
} as const satisfies Record<BookingVendor, BookingStorage>;
|
|
318
326
|
|
|
319
327
|
/**
|
|
@@ -452,7 +460,7 @@ export function storedItems(
|
|
|
452
460
|
let stripe = false;
|
|
453
461
|
for (const widget of new Set(booking)) {
|
|
454
462
|
const entry: BookingStorage = BOOKING[widget];
|
|
455
|
-
|
|
463
|
+
const own: StoredItem[] = [
|
|
456
464
|
{
|
|
457
465
|
id: widget,
|
|
458
466
|
names: entry.kept,
|
|
@@ -466,8 +474,10 @@ export function storedItems(
|
|
|
466
474
|
provider: widget,
|
|
467
475
|
lasts: "tab",
|
|
468
476
|
consent: "none",
|
|
469
|
-
}
|
|
470
|
-
|
|
477
|
+
},
|
|
478
|
+
];
|
|
479
|
+
// A widget that keeps nothing for the tab has no row for it.
|
|
480
|
+
widgetRows.push(...own.filter((row) => row.names.length > 0));
|
|
471
481
|
for (const vendor of entry.loads ?? []) vendors.add(vendor);
|
|
472
482
|
if (entry.stripe) stripe = true;
|
|
473
483
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { HTMLAttributes } from "astro/types";
|
|
16
16
|
import type { BookingWidget, BookingWidgetVars } from "../booking/widget.ts";
|
|
17
|
+
import EscapeAll from "./EscapeAll.astro";
|
|
17
18
|
import EscapeNavigator from "./EscapeNavigator.astro";
|
|
18
19
|
import FourEscape from "./FourEscape.astro";
|
|
19
20
|
|
|
@@ -39,6 +40,18 @@ const { widget, locale, vars, colorScheme, ...attrs } = Astro.props;
|
|
|
39
40
|
vars={vars}
|
|
40
41
|
/>
|
|
41
42
|
)}
|
|
43
|
+
{widget.vendor === "escapeall" && (
|
|
44
|
+
<EscapeAll
|
|
45
|
+
{...attrs}
|
|
46
|
+
host={widget.host}
|
|
47
|
+
companyId={widget.companyId}
|
|
48
|
+
serviceId={widget.serviceId}
|
|
49
|
+
template={widget.template}
|
|
50
|
+
skin={widget.skin}
|
|
51
|
+
players={widget.players}
|
|
52
|
+
locale={locale}
|
|
53
|
+
/>
|
|
54
|
+
)}
|
|
42
55
|
{widget.vendor === "escapenavigator" && (
|
|
43
56
|
<EscapeNavigator
|
|
44
57
|
{...attrs}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* An EscapeAll booking calendar: one room's, or every room's. See
|
|
4
|
+
* `escapeall.ts` for what it loads and keeps.
|
|
5
|
+
*
|
|
6
|
+
* `locale` is the page's. Attributes go on the calendar's element.
|
|
7
|
+
*
|
|
8
|
+
* ```astro
|
|
9
|
+
* <EscapeAll host="nl.escapeall.com" companyId="…" serviceId="…" template={9} locale={locale} />
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import type { HTMLAttributes } from "astro/types";
|
|
13
|
+
import {
|
|
14
|
+
checkEscapeAll,
|
|
15
|
+
type EscapeAllWidget,
|
|
16
|
+
escapeAllAssets,
|
|
17
|
+
escapeAllLanguage,
|
|
18
|
+
} from "../booking/escapeall.ts";
|
|
19
|
+
|
|
20
|
+
type Props = HTMLAttributes<"div"> &
|
|
21
|
+
EscapeAllWidget & {
|
|
22
|
+
readonly locale: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
host,
|
|
27
|
+
companyId,
|
|
28
|
+
serviceId,
|
|
29
|
+
template,
|
|
30
|
+
skin,
|
|
31
|
+
players,
|
|
32
|
+
locale,
|
|
33
|
+
id,
|
|
34
|
+
...attrs
|
|
35
|
+
} = Astro.props;
|
|
36
|
+
const widget = { host, companyId, serviceId, template, skin, players };
|
|
37
|
+
|
|
38
|
+
checkEscapeAll(widget, Astro.url.pathname);
|
|
39
|
+
|
|
40
|
+
const { stylesheets, script } = escapeAllAssets(widget);
|
|
41
|
+
// EscapeAll's script builds its selectors from the one it is given, so the
|
|
42
|
+
// calendar is found by id: the page's own, or one from the room or account.
|
|
43
|
+
const elementId = id ?? `escapeall-${serviceId ?? companyId}`;
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
{stylesheets.map((href) => (
|
|
47
|
+
<link
|
|
48
|
+
rel='stylesheet'
|
|
49
|
+
href={href}
|
|
50
|
+
/>
|
|
51
|
+
))}
|
|
52
|
+
<script src="./escapeall-calendar.ts"></script>
|
|
53
|
+
<div
|
|
54
|
+
{...attrs}
|
|
55
|
+
id={elementId}
|
|
56
|
+
data-escapeall-company={companyId}
|
|
57
|
+
data-escapeall-service={serviceId}
|
|
58
|
+
data-escapeall-template={template}
|
|
59
|
+
data-escapeall-players={players}
|
|
60
|
+
data-escapeall-language={escapeAllLanguage(locale)}
|
|
61
|
+
data-escapeall-script={script}
|
|
62
|
+
></div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { appendScript } from "./append-script.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Draws each EscapeAll calendar on the page, once EscapeAll's script has
|
|
5
|
+
* loaded: the call its store scripts make, with the settings the component
|
|
6
|
+
* wrote on the element.
|
|
7
|
+
*/
|
|
8
|
+
// Its script builds selectors by appending to the one it is given, so the
|
|
9
|
+
// calendar is named by its id, not handed over as an element.
|
|
10
|
+
type Calendar = (selector: string, settings: Record<string, unknown>) => void;
|
|
11
|
+
|
|
12
|
+
const host = window as unknown as { GetBookingCalendra?: Calendar };
|
|
13
|
+
|
|
14
|
+
const elements = [...document.querySelectorAll("[data-escapeall-company]")];
|
|
15
|
+
const [first] = elements;
|
|
16
|
+
if (first instanceof HTMLElement && first.dataset.escapeallScript) {
|
|
17
|
+
appendScript(first.dataset.escapeallScript).addEventListener("load", () => {
|
|
18
|
+
for (const element of elements) {
|
|
19
|
+
if (!(element instanceof HTMLElement)) continue;
|
|
20
|
+
const { escapeallCompany, escapeallService, escapeallLanguage } =
|
|
21
|
+
element.dataset;
|
|
22
|
+
host.GetBookingCalendra?.(`#${CSS.escape(element.id)}`, {
|
|
23
|
+
CompanyId: [escapeallCompany],
|
|
24
|
+
...(escapeallService ? { ServiceId: [escapeallService] } : {}),
|
|
25
|
+
lang: escapeallLanguage,
|
|
26
|
+
Template: Number(element.dataset.escapeallTemplate),
|
|
27
|
+
InitialQuantity: Number(element.dataset.escapeallPlayers ?? 2),
|
|
28
|
+
FirstWeekDay: "Monday",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { invalidId, UUID, type Uuid } from "../analytics/ids.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EscapeAll's booking calendar: a store's rooms and their free slots, booked
|
|
5
|
+
* on the page. One room's calendar when `serviceId` names it (EscapeAll calls
|
|
6
|
+
* a room a service), or every room's.
|
|
7
|
+
*
|
|
8
|
+
* EscapeAll's embed is a store script of its own that waits for EscapeAll's
|
|
9
|
+
* jQuery, then calls `GetBookingCalendra` from the shared `all.js` with the
|
|
10
|
+
* store's settings. The component makes that call itself, with the page's
|
|
11
|
+
* language, so it needs no store script. Read 2026-11-12 from Aruba's.
|
|
12
|
+
*
|
|
13
|
+
* It stores nothing as it renders, and loads nothing from anyone else; while
|
|
14
|
+
* a visitor books it keeps a phone confirmation in local storage. Its queue
|
|
15
|
+
* cookie is written only once the page sets EscapeAll's own consent cookie,
|
|
16
|
+
* which ours never does. All of it is in `storedItems`.
|
|
17
|
+
*/
|
|
18
|
+
export interface EscapeAllWidget {
|
|
19
|
+
/** EscapeAll's regional site the account is on: `nl.escapeall.com`. */
|
|
20
|
+
readonly host: string;
|
|
21
|
+
/** The account's id, from EscapeAll: the store script's `CompanyId`. */
|
|
22
|
+
readonly companyId: Uuid;
|
|
23
|
+
/** One room's id, for its calendar alone; every room's without it. */
|
|
24
|
+
readonly serviceId?: Uuid;
|
|
25
|
+
/** The calendar's layout, by EscapeAll's number: the store's `Template`. */
|
|
26
|
+
readonly template: number;
|
|
27
|
+
/** The store's stylesheet, as EscapeAll hosts it: `CustomCss/<skin>.css`. */
|
|
28
|
+
readonly skin?: string;
|
|
29
|
+
/** The group size the calendar opens on: the store's `InitialQuantity`. */
|
|
30
|
+
readonly players?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** EscapeAll's own CSS variables: none it documents, so none taken. */
|
|
34
|
+
export type EscapeAllVars = Readonly<Partial<Record<string, never>>>;
|
|
35
|
+
|
|
36
|
+
/** The languages the calendar is translated into, as its paths take them. */
|
|
37
|
+
export const ESCAPEALL_LANGUAGES = ["en", "nl", "de"] as const;
|
|
38
|
+
|
|
39
|
+
export type EscapeAllLanguage = (typeof ESCAPEALL_LANGUAGES)[number];
|
|
40
|
+
|
|
41
|
+
/** EscapeAll's regional sites: `nl.escapeall.com`. */
|
|
42
|
+
const ESCAPEALL_HOST = /^[a-z]{2}\.escapeall\.com$/;
|
|
43
|
+
|
|
44
|
+
/** Fails the build on a calendar that would render nothing. */
|
|
45
|
+
export function checkEscapeAll(widget: EscapeAllWidget, at: string): void {
|
|
46
|
+
if (!ESCAPEALL_HOST.test(widget.host)) {
|
|
47
|
+
invalidId(
|
|
48
|
+
at,
|
|
49
|
+
`EscapeAll host "${widget.host}" is not one of EscapeAll's regional sites, like "nl.escapeall.com".`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
for (const [name, id] of [
|
|
53
|
+
["company", widget.companyId],
|
|
54
|
+
["service", widget.serviceId],
|
|
55
|
+
] as const) {
|
|
56
|
+
if (id !== undefined && !UUID.test(id)) {
|
|
57
|
+
invalidId(
|
|
58
|
+
at,
|
|
59
|
+
`EscapeAll ${name} id "${id}" is not a UUID, so the calendar would render nothing.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The stylesheets and script a calendar on `host` needs. */
|
|
66
|
+
export function escapeAllAssets(widget: EscapeAllWidget) {
|
|
67
|
+
const base = `https://${widget.host}`;
|
|
68
|
+
return {
|
|
69
|
+
stylesheets: [
|
|
70
|
+
`${base}/Content/API/styles.css`,
|
|
71
|
+
...(widget.skin === undefined
|
|
72
|
+
? []
|
|
73
|
+
: [`${base}/CustomCss/${widget.skin}.css`]),
|
|
74
|
+
],
|
|
75
|
+
script: `${base}/Scripts/API/all.js`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The calendar's language for a page's locale: English for one it is not
|
|
81
|
+
* translated into, as EscapeAll's own embeds fall back.
|
|
82
|
+
*/
|
|
83
|
+
export function escapeAllLanguage(locale: string): EscapeAllLanguage {
|
|
84
|
+
const { language } = new Intl.Locale(locale);
|
|
85
|
+
return ESCAPEALL_LANGUAGES.find((known) => known === language) ?? "en";
|
|
86
|
+
}
|
package/src/booking/widget.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
EscapeNavigatorVars,
|
|
3
3
|
EscapeNavigatorWidget,
|
|
4
4
|
} from "./escape-navigator.ts";
|
|
5
|
+
import type { EscapeAllVars, EscapeAllWidget } from "./escapeall.ts";
|
|
5
6
|
import type { FourEscapeVars, FourEscapeWidget } from "./four-escape.ts";
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -10,7 +11,8 @@ import type { FourEscapeVars, FourEscapeWidget } from "./four-escape.ts";
|
|
|
10
11
|
*/
|
|
11
12
|
export type BookingWidget =
|
|
12
13
|
| ({ readonly vendor: "4escape" } & FourEscapeWidget)
|
|
13
|
-
| ({ readonly vendor: "escapenavigator" } & EscapeNavigatorWidget)
|
|
14
|
+
| ({ readonly vendor: "escapenavigator" } & EscapeNavigatorWidget)
|
|
15
|
+
| ({ readonly vendor: "escapeall" } & EscapeAllWidget);
|
|
14
16
|
|
|
15
17
|
/** Who makes a booking widget: what `storedItems` takes for their rows. */
|
|
16
18
|
export type BookingVendor = BookingWidget["vendor"];
|
|
@@ -19,6 +21,7 @@ export type BookingVendor = BookingWidget["vendor"];
|
|
|
19
21
|
export interface BookingVendorVars {
|
|
20
22
|
readonly "4escape": FourEscapeVars;
|
|
21
23
|
readonly escapenavigator: EscapeNavigatorVars;
|
|
24
|
+
readonly escapeall: EscapeAllVars;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/** The CSS variables a widget takes: its own vendor's, and no other's. */
|
package/src/index.ts
CHANGED
|
@@ -62,6 +62,15 @@ export {
|
|
|
62
62
|
type EscapeNavigatorWidget,
|
|
63
63
|
escapeNavigatorLanguage,
|
|
64
64
|
} from "./booking/escape-navigator.ts";
|
|
65
|
+
export {
|
|
66
|
+
checkEscapeAll,
|
|
67
|
+
ESCAPEALL_LANGUAGES,
|
|
68
|
+
type EscapeAllLanguage,
|
|
69
|
+
type EscapeAllVars,
|
|
70
|
+
type EscapeAllWidget,
|
|
71
|
+
escapeAllAssets,
|
|
72
|
+
escapeAllLanguage,
|
|
73
|
+
} from "./booking/escapeall.ts";
|
|
65
74
|
export {
|
|
66
75
|
checkFourEscape,
|
|
67
76
|
FOUR_ESCAPE_LANGUAGES,
|