@seatlayer/js 0.47.2 → 0.48.1
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/README.md +16 -1
- package/dist/{channelsMode-33ZLORVF.js → channelsMode-NCH5GYHN.js} +3 -3
- package/dist/{chunk-X2R4AQSZ.js → chunk-H4OJF6LE.js} +176 -7
- package/dist/chunk-H4OJF6LE.js.map +1 -0
- package/dist/{chunk-DMEFXZIL.js → chunk-KNMZQZXR.js} +2 -2
- package/dist/{chunk-CF5MS7UR.js → chunk-Q5QT3YLA.js} +186 -59
- package/dist/chunk-Q5QT3YLA.js.map +1 -0
- package/dist/{hostedCheckout-SHQCWN5Y.js → hostedCheckout-F6C6VCCG.js} +1 -1
- package/dist/hostedCheckout-F6C6VCCG.js.map +1 -0
- package/dist/index.cjs +612 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -5
- package/dist/index.d.ts +37 -5
- package/dist/index.js +258 -62
- package/dist/index.js.map +1 -1
- package/dist/{manager-CAIPkUYl.d.cts → manager-DEjbKiqJ.d.cts} +200 -17
- package/dist/{manager-CAIPkUYl.d.ts → manager-DEjbKiqJ.d.ts} +200 -17
- package/dist/manager.cjs +358 -62
- package/dist/manager.cjs.map +1 -1
- package/dist/manager.d.cts +1 -1
- package/dist/manager.d.ts +1 -1
- package/dist/manager.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-CF5MS7UR.js.map +0 -1
- package/dist/chunk-X2R4AQSZ.js.map +0 -1
- package/dist/hostedCheckout-SHQCWN5Y.js.map +0 -1
- /package/dist/{channelsMode-33ZLORVF.js.map → channelsMode-NCH5GYHN.js.map} +0 -0
- /package/dist/{chunk-DMEFXZIL.js.map → chunk-KNMZQZXR.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SeatManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Q5QT3YLA.js";
|
|
4
4
|
import {
|
|
5
5
|
ACCESS_LINK_DEFAULTS,
|
|
6
6
|
ChannelsMode,
|
|
@@ -28,11 +28,11 @@ import {
|
|
|
28
28
|
selectionSources,
|
|
29
29
|
stateBadge,
|
|
30
30
|
suggestMarker
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-KNMZQZXR.js";
|
|
32
32
|
import {
|
|
33
33
|
ManageApi,
|
|
34
34
|
ManageApiError
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-H4OJF6LE.js";
|
|
36
36
|
import {
|
|
37
37
|
__privateAdd,
|
|
38
38
|
__privateGet,
|
|
@@ -524,9 +524,54 @@ var PubApi = class {
|
|
|
524
524
|
}
|
|
525
525
|
return data;
|
|
526
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* Binary counterpart to `request`. Buyer media needs the same in-memory
|
|
529
|
+
* bearer/refresh rules as JSON, but returns bytes that the picker turns into
|
|
530
|
+
* a blob URL. The bearer stays in the Authorization header and is never
|
|
531
|
+
* appended to `path`.
|
|
532
|
+
*/
|
|
533
|
+
async requestBlob(path, retried = {}) {
|
|
534
|
+
const headers = {};
|
|
535
|
+
const authorization = await this.access?.authorization(retried.auth ? "unauthorized" : "initial");
|
|
536
|
+
if (authorization) headers.Authorization = authorization;
|
|
537
|
+
const res = await fetch(`${this.base}${path}`, { method: "GET", headers, credentials: "omit" });
|
|
538
|
+
if (res.ok) return res.blob();
|
|
539
|
+
const isJson = (res.headers.get("content-type") ?? "").includes("application/json");
|
|
540
|
+
const data = isJson ? await res.json().catch(() => null) : null;
|
|
541
|
+
const code = data?.code ?? data?.error;
|
|
542
|
+
if (this.access?.configured && (res.status === 401 || res.status === 403 || res.status === 422)) {
|
|
543
|
+
const refreshed = await this.access.handleFailure(res.status, code);
|
|
544
|
+
if (refreshed && !retried.auth) return this.requestBlob(path, { ...retried, auth: true });
|
|
545
|
+
}
|
|
546
|
+
let retryAfterS;
|
|
547
|
+
if (res.status === 429) {
|
|
548
|
+
retryAfterS = parseRetryAfter(res.headers.get("Retry-After"), data?.retryAfterSeconds) ?? DEFAULT_RATE_LIMIT_WAIT_S;
|
|
549
|
+
if (!retried.rateLimit && retryAfterS <= MAX_RATE_LIMIT_WAIT_S) {
|
|
550
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterS * 1e3));
|
|
551
|
+
return this.requestBlob(path, { ...retried, rateLimit: true });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
throw new ApiError(
|
|
555
|
+
res.status,
|
|
556
|
+
data?.error ?? `request_failed_${res.status}`,
|
|
557
|
+
code,
|
|
558
|
+
void 0,
|
|
559
|
+
void 0,
|
|
560
|
+
retryAfterS
|
|
561
|
+
);
|
|
562
|
+
}
|
|
527
563
|
chart(key) {
|
|
528
564
|
return this.request(`/pub/events/${encodeURIComponent(key)}/chart`);
|
|
529
565
|
}
|
|
566
|
+
/** Authenticated bytes for an Event-scoped authored view image. */
|
|
567
|
+
asset(key, asset) {
|
|
568
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(asset)) {
|
|
569
|
+
return Promise.reject(new ApiError(404, "not_found", "not_found"));
|
|
570
|
+
}
|
|
571
|
+
return this.requestBlob(
|
|
572
|
+
`/pub/events/${encodeURIComponent(key)}/assets/${encodeURIComponent(asset)}`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
530
575
|
objects(key) {
|
|
531
576
|
return this.request(`/pub/events/${encodeURIComponent(key)}/objects?compact=1`);
|
|
532
577
|
}
|
|
@@ -629,12 +674,11 @@ var PubApi = class {
|
|
|
629
674
|
/**
|
|
630
675
|
* What PickerController opens its own socket with.
|
|
631
676
|
*
|
|
632
|
-
* Empty for an access-scoped client: a
|
|
677
|
+
* Empty for an access-scoped client: a scoped audience authenticates with a
|
|
633
678
|
* subprotocol ticket, which a URL-only constructor cannot carry, so the SDK's
|
|
634
679
|
* BuyerRealtimeClient owns that socket instead and the controller skips its
|
|
635
680
|
* own (an empty URL is its documented "no live feed" contract). A tokenless
|
|
636
|
-
* public client returns exactly the URL it always has
|
|
637
|
-
* public picker's realtime path changes.
|
|
681
|
+
* Managed public client returns exactly the URL it always has.
|
|
638
682
|
*/
|
|
639
683
|
socketUrl(key) {
|
|
640
684
|
return this.accessScoped ? "" : this.subscribeUrl(key);
|
|
@@ -1994,6 +2038,81 @@ import {
|
|
|
1994
2038
|
schedulePanoramaUpgrade
|
|
1995
2039
|
} from "@seatlayer/core/view/panoramaDelivery";
|
|
1996
2040
|
|
|
2041
|
+
// src/buyerAssets.ts
|
|
2042
|
+
var SAFE_ASSET = /^[a-zA-Z0-9._-]+$/;
|
|
2043
|
+
function buyerEventAssetReference(value) {
|
|
2044
|
+
let url;
|
|
2045
|
+
try {
|
|
2046
|
+
url = new URL(value, "https://seatlayer.invalid");
|
|
2047
|
+
} catch {
|
|
2048
|
+
return null;
|
|
2049
|
+
}
|
|
2050
|
+
if (url.search || url.hash) return null;
|
|
2051
|
+
const match = /^\/pub\/events\/([^/]+)\/assets\/([^/]+)$/.exec(url.pathname);
|
|
2052
|
+
if (!match) return null;
|
|
2053
|
+
try {
|
|
2054
|
+
const eventKey = decodeURIComponent(match[1]);
|
|
2055
|
+
const asset = decodeURIComponent(match[2]);
|
|
2056
|
+
if (!eventKey || !SAFE_ASSET.test(asset)) return null;
|
|
2057
|
+
return { eventKey, asset };
|
|
2058
|
+
} catch {
|
|
2059
|
+
return null;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function looksLikeBuyerAsset(value) {
|
|
2063
|
+
try {
|
|
2064
|
+
return /^\/pub\/events\/[^/]+\/assets(?:\/|$)/.test(
|
|
2065
|
+
new URL(value, "https://seatlayer.invalid").pathname
|
|
2066
|
+
);
|
|
2067
|
+
} catch {
|
|
2068
|
+
return false;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
var BuyerAssetObjectUrls = class {
|
|
2072
|
+
constructor(eventKey, load) {
|
|
2073
|
+
this.eventKey = eventKey;
|
|
2074
|
+
this.load = load;
|
|
2075
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
2076
|
+
this.created = /* @__PURE__ */ new Set();
|
|
2077
|
+
this.disposed = false;
|
|
2078
|
+
}
|
|
2079
|
+
/**
|
|
2080
|
+
* External organizer/CDN URLs pass through unchanged. SeatLayer event assets
|
|
2081
|
+
* never do: they require the transport, and a reference for another Event is
|
|
2082
|
+
* refused instead of being loaded anonymously.
|
|
2083
|
+
*/
|
|
2084
|
+
resolve(reference) {
|
|
2085
|
+
const parsed = buyerEventAssetReference(reference);
|
|
2086
|
+
if (!parsed) {
|
|
2087
|
+
return Promise.resolve(looksLikeBuyerAsset(reference) ? null : reference);
|
|
2088
|
+
}
|
|
2089
|
+
if (parsed.eventKey !== this.eventKey || !this.load || this.disposed) return Promise.resolve(null);
|
|
2090
|
+
const existing = this.pending.get(reference);
|
|
2091
|
+
if (existing) return existing;
|
|
2092
|
+
const task = this.load(this.eventKey, parsed.asset).then((blob) => {
|
|
2093
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
2094
|
+
if (this.disposed) {
|
|
2095
|
+
URL.revokeObjectURL(objectUrl);
|
|
2096
|
+
return null;
|
|
2097
|
+
}
|
|
2098
|
+
this.created.add(objectUrl);
|
|
2099
|
+
return objectUrl;
|
|
2100
|
+
}).catch((error) => {
|
|
2101
|
+
this.pending.delete(reference);
|
|
2102
|
+
throw error;
|
|
2103
|
+
});
|
|
2104
|
+
this.pending.set(reference, task);
|
|
2105
|
+
return task;
|
|
2106
|
+
}
|
|
2107
|
+
dispose() {
|
|
2108
|
+
if (this.disposed) return;
|
|
2109
|
+
this.disposed = true;
|
|
2110
|
+
for (const url of this.created) URL.revokeObjectURL(url);
|
|
2111
|
+
this.created.clear();
|
|
2112
|
+
this.pending.clear();
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
|
|
1997
2116
|
// src/offerAvailability.ts
|
|
1998
2117
|
var SALE_STATES = [
|
|
1999
2118
|
"on-sale",
|
|
@@ -2209,7 +2328,7 @@ async function loadHostedCheckout() {
|
|
|
2209
2328
|
cdnChunkUrl("seatlayer-checkout.mjs")
|
|
2210
2329
|
);
|
|
2211
2330
|
}
|
|
2212
|
-
return import("./hostedCheckout-
|
|
2331
|
+
return import("./hostedCheckout-F6C6VCCG.js");
|
|
2213
2332
|
}
|
|
2214
2333
|
function paymentsOffReason(reason) {
|
|
2215
2334
|
return reason === "unavailable_for_event" || reason === "payments_off_for_event" ? reason : "not_configured";
|
|
@@ -2251,6 +2370,16 @@ var CSS = (
|
|
|
2251
2370
|
.sl-close.on{display:inline-flex}
|
|
2252
2371
|
.sl-close svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
|
|
2253
2372
|
|
|
2373
|
+
/* A page or popup may already own the event heading. In that case only the
|
|
2374
|
+
duplicate identity disappears; operational header controls stay available.
|
|
2375
|
+
Zero block padding lets an otherwise-empty header collapse completely. */
|
|
2376
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head{padding-block:0;border-bottom-width:0}
|
|
2377
|
+
.sl-picker[data-event-details-hidden="true"] .sl-logo,
|
|
2378
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head-info{display:none!important}
|
|
2379
|
+
.sl-picker[data-event-details-hidden="true"] .sl-hold-pill.on,
|
|
2380
|
+
.sl-picker[data-event-details-hidden="true"] .sl-closed-pill.on,
|
|
2381
|
+
.sl-picker[data-event-details-hidden="true"] .sl-close.on{margin-block:10px}
|
|
2382
|
+
|
|
2254
2383
|
/* body */
|
|
2255
2384
|
.sl-body{display:flex;flex:1;min-height:0}
|
|
2256
2385
|
.sl-map{position:relative;flex:1;min-width:0}
|
|
@@ -2268,7 +2397,7 @@ var CSS = (
|
|
|
2268
2397
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
|
|
2269
2398
|
flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
|
|
2270
2399
|
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(252px,52%)}
|
|
2271
|
-
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:
|
|
2400
|
+
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:76px;overflow:hidden}
|
|
2272
2401
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
|
|
2273
2402
|
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
|
|
2274
2403
|
.sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
|
|
@@ -2303,9 +2432,10 @@ var CSS = (
|
|
|
2303
2432
|
head is the tap/swipe toggle target (min 44px), so it reads as one control. */
|
|
2304
2433
|
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
|
|
2305
2434
|
cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
|
|
2306
|
-
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex}
|
|
2307
|
-
.sl-sheet-
|
|
2308
|
-
.sl-sheet-
|
|
2435
|
+
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:64px;padding:4px 10px 6px}
|
|
2436
|
+
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-sheet-head{height:100%}
|
|
2437
|
+
.sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:1px auto 5px}
|
|
2438
|
+
.sl-sheet-bar{display:flex;align-items:center;gap:8px;min-height:44px}
|
|
2309
2439
|
.sl-sheet-peek{display:flex;align-items:center;gap:7px;flex:1;min-width:0;font-size:13px;font-weight:700;color:var(--sl-text);
|
|
2310
2440
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
2311
2441
|
.sl-sheet-peek .sub{color:var(--sl-muted);font-weight:600}
|
|
@@ -2333,7 +2463,7 @@ var CSS = (
|
|
|
2333
2463
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec.has,
|
|
2334
2464
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters.has{display:none}
|
|
2335
2465
|
/* Accessibility and colour-safety controls must remain reachable on phones.
|
|
2336
|
-
Keep them out of the
|
|
2466
|
+
Keep them out of the collapsed peek, then reveal their consolidated row whenever
|
|
2337
2467
|
the buyer explicitly opens the ticket panel. */
|
|
2338
2468
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filtersec.has{display:block!important}
|
|
2339
2469
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filters.has{display:flex!important}
|
|
@@ -2476,15 +2606,10 @@ var CSS = (
|
|
|
2476
2606
|
.sl-picker .sl-cta:disabled{background:var(--sl-surface);color:var(--sl-muted);opacity:1;
|
|
2477
2607
|
cursor:not-allowed;filter:none;transform:none}
|
|
2478
2608
|
|
|
2479
|
-
/* Chrome anchor regions (Feature 6) \u2014 every
|
|
2609
|
+
/* Chrome anchor regions (Feature 6) \u2014 every interactive map overlay is APPENDED
|
|
2480
2610
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
2481
|
-
two
|
|
2482
|
-
|
|
2483
|
-
/* align-items:center, not the flex default of stretch. Without it a short pill
|
|
2484
|
-
beside a taller control (TEST MODE next to the Map/3D toggle) is stretched to
|
|
2485
|
-
the row's height, so two pills that should read as a matched pair end up
|
|
2486
|
-
different shapes on different baselines. Column regions set their own
|
|
2487
|
-
cross-axis alignment below and are unaffected. */
|
|
2611
|
+
two controls free-float on top of each other. Regions never overlap: the top
|
|
2612
|
+
strip splits into left/center/right; rails + corners own their edge. */
|
|
2488
2613
|
.sl-anchor{position:absolute;z-index:5;display:flex;align-items:center;gap:8px;pointer-events:none}
|
|
2489
2614
|
.sl-anchor > *{pointer-events:auto}
|
|
2490
2615
|
.sl-anchor[data-region="top-left"]{top:12px;left:12px;flex-wrap:wrap;max-width:38%}
|
|
@@ -2500,18 +2625,17 @@ var CSS = (
|
|
|
2500
2625
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-left"]{max-width:30%}
|
|
2501
2626
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
|
|
2502
2627
|
|
|
2503
|
-
/* TEST MODE
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:0 8px;font-size:8.5px;letter-spacing:.06em}
|
|
2628
|
+
/* TEST MODE is environment context, not an action. A clipped corner ribbon
|
|
2629
|
+
keeps it persistent without impersonating a button or competing with Map/3D.
|
|
2630
|
+
The top-left interactive region moves below it only on test events. */
|
|
2631
|
+
.sl-testbadge{position:absolute;top:17px;left:-38px;z-index:6;width:142px;padding:5px 0;
|
|
2632
|
+
transform:rotate(-45deg);text-align:center;pointer-events:none;
|
|
2633
|
+
font-size:9.5px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;
|
|
2634
|
+
white-space:nowrap;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
2635
|
+
box-shadow:0 2px 8px rgba(0,0,0,.28)}
|
|
2636
|
+
.sl-picker[data-event-mode="test"] .sl-anchor[data-region="top-left"]{top:96px}
|
|
2637
|
+
.sl-picker[data-layout="narrow"] .sl-testbadge{top:14px;left:-35px;width:128px;font-size:8.5px}
|
|
2638
|
+
.sl-picker[data-layout="narrow"][data-event-mode="test"] .sl-anchor[data-region="top-left"]{top:88px}
|
|
2515
2639
|
|
|
2516
2640
|
/* zoom column (flows within the bottom-right region) */
|
|
2517
2641
|
.sl-zoom{display:flex;flex-direction:column;gap:6px}
|
|
@@ -2746,10 +2870,15 @@ var CSS = (
|
|
|
2746
2870
|
.sl-ba-actions{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:7px}
|
|
2747
2871
|
.sl-ba-actions button{min-height:36px;border-radius:9px;border:1px solid var(--sl-line);font-size:11.5px;font-weight:800}
|
|
2748
2872
|
.sl-ba-actions .replace{border-color:var(--sl-accent);background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
2749
|
-
.sl-picker[data-layout="narrow"] .sl-ba{padding:
|
|
2873
|
+
.sl-picker[data-layout="narrow"] .sl-ba{padding:9px;gap:6px}
|
|
2874
|
+
.sl-picker[data-layout="narrow"] .sl-ba::after{display:none}
|
|
2875
|
+
.sl-picker[data-layout="narrow"] .sl-ba-title{font-size:12.5px}
|
|
2876
|
+
.sl-picker[data-layout="narrow"] .sl-ba-copy{display:none}
|
|
2750
2877
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
2751
2878
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
2752
|
-
.sl-picker[data-layout="narrow"] .sl-ba select{min-height:
|
|
2879
|
+
.sl-picker[data-layout="narrow"] .sl-ba select{min-height:40px}
|
|
2880
|
+
.sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
|
|
2881
|
+
.sl-picker[data-layout="narrow"] .sl-ba-go{min-height:40px}
|
|
2753
2882
|
|
|
2754
2883
|
/* screen-reader live region */
|
|
2755
2884
|
.sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
@@ -3157,6 +3286,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3157
3286
|
this.secCardEl = null;
|
|
3158
3287
|
this.viewEl = null;
|
|
3159
3288
|
this.viewCleanup = null;
|
|
3289
|
+
/** Supersedes an older authored-view byte request when another seat is opened. */
|
|
3290
|
+
this.seatViewGen = 0;
|
|
3160
3291
|
this.allSeatsCache = null;
|
|
3161
3292
|
// F3 minimap
|
|
3162
3293
|
this.miniCanvas = null;
|
|
@@ -3190,6 +3321,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3190
3321
|
this.fsFallback = false;
|
|
3191
3322
|
this.fsChangeHandler = null;
|
|
3192
3323
|
this.fsEscHandler = null;
|
|
3324
|
+
/** Host-level event chrome owns the duplicate identity outside full screen. */
|
|
3325
|
+
this.eventDetailsHidden = false;
|
|
3193
3326
|
/** True once we've asked the host page to pin us fullscreen (framed, no native). */
|
|
3194
3327
|
this.framedFs = false;
|
|
3195
3328
|
/** Last height (px) posted to a host frame; dedupes redundant reports. */
|
|
@@ -3212,6 +3345,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3212
3345
|
if (!options.event || typeof options.event !== "string") throw new Error("seatmap: `event` key is required");
|
|
3213
3346
|
if (!options.container) throw new Error("seatmap: `container` is required (or use SeatPicker.open())");
|
|
3214
3347
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
3348
|
+
this.eventDetailsHidden = !!options.hideEventDetails;
|
|
3215
3349
|
this.hostPricing = options.pricing;
|
|
3216
3350
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
3217
3351
|
this.access = options.transport ? null : createBuyerAccessContext(options, {
|
|
@@ -3229,6 +3363,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3229
3363
|
onObjectUnavailable: (event) => this.opts.onSelectedObjectUnavailable?.(event)
|
|
3230
3364
|
});
|
|
3231
3365
|
this.api = options.transport ?? this.pubApi;
|
|
3366
|
+
this.buyerAssetUrls = new BuyerAssetObjectUrls(
|
|
3367
|
+
options.event,
|
|
3368
|
+
this.api.asset ? (key, asset) => this.api.asset(key, asset) : void 0
|
|
3369
|
+
);
|
|
3232
3370
|
if (options.checkout === "hosted" && !this.pubApi) {
|
|
3233
3371
|
console.warn(
|
|
3234
3372
|
'seatlayer: checkout: "hosted" needs the widget\'s own transport \u2014 a custom `transport` owns its backend, so the picker is staying on onCheckout for this mount.'
|
|
@@ -3357,7 +3495,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3357
3495
|
}
|
|
3358
3496
|
}
|
|
3359
3497
|
const sightHtml = hasStage && distance != null ? `<div class="sl-confirm-sight">${t2("picker.sightline", { m: distance })}</div>` : "";
|
|
3360
|
-
const viewBtn = realPhoto ? `<button type="button" class="sl-confirm-view sl-confirm-thumbwrap" aria-label="${t2("picker.viewFromSeat", { label: seat.label })}"><img class="sl-confirm-thumb"
|
|
3498
|
+
const viewBtn = realPhoto ? `<button type="button" class="sl-confirm-view sl-confirm-thumbwrap" aria-label="${t2("picker.viewFromSeat", { label: seat.label })}"><img class="sl-confirm-thumb" alt="" /><span class="sl-confirm-thumb-badge">\u{1F52D} ${this.tf("picker.viewFromHere", "View from here")}</span></button>` : `<button type="button" class="sl-confirm-view sl-confirm-viewbtn" aria-label="${t2("picker.viewFromSeat", { label: seat.label })}"><span aria-hidden="true">\u{1F52D}</span><span>${this.tf("picker.viewFromHere", "View from here")}</span></button>`;
|
|
3361
3499
|
return viewBtn + sightHtml;
|
|
3362
3500
|
}
|
|
3363
3501
|
/** "See it in 3D" (2D) / "View from this seat" (already in 3D) action for the
|
|
@@ -3492,6 +3630,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
3492
3630
|
}
|
|
3493
3631
|
syncFullscreenButtons() {
|
|
3494
3632
|
const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
|
|
3633
|
+
const hideEventDetails = this.eventDetailsHidden && !active;
|
|
3634
|
+
this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
|
|
3635
|
+
this.els.logo?.toggleAttribute("hidden", hideEventDetails);
|
|
3636
|
+
this.els.headInfo?.toggleAttribute("hidden", hideEventDetails);
|
|
3495
3637
|
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
3496
3638
|
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
3497
3639
|
}
|
|
@@ -3635,7 +3777,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3635
3777
|
root.innerHTML = `
|
|
3636
3778
|
<div class="sl-head">
|
|
3637
3779
|
<div class="sl-logo" data-ref="logo"></div>
|
|
3638
|
-
<div class="sl-head-info">
|
|
3780
|
+
<div class="sl-head-info" data-ref="headInfo">
|
|
3639
3781
|
<div class="sl-head-name" data-ref="name"></div>
|
|
3640
3782
|
<div class="sl-head-meta" data-ref="meta"></div>
|
|
3641
3783
|
</div>
|
|
@@ -3697,6 +3839,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3697
3839
|
this.els[el.dataset.ref] = el;
|
|
3698
3840
|
});
|
|
3699
3841
|
this.mapHost = this.els.map;
|
|
3842
|
+
this.syncFullscreenButtons();
|
|
3700
3843
|
void this.refreshOfferAvailability(false);
|
|
3701
3844
|
if (this.api.availability) {
|
|
3702
3845
|
this.offerVisibilityHandler = () => {
|
|
@@ -3808,7 +3951,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
3808
3951
|
}
|
|
3809
3952
|
this.els.boot.remove();
|
|
3810
3953
|
this.startRealtime();
|
|
3811
|
-
this.salesClosed = !!info.salesClosed;
|
|
3954
|
+
this.salesClosed = !!info.salesClosed || !!this.opts.readOnly;
|
|
3955
|
+
root.dataset.eventMode = info.mode === "test" ? "test" : "live";
|
|
3812
3956
|
this.controller.setViewMode(this.normalizeInitialView(this.opts.initialView));
|
|
3813
3957
|
this.buildRegions();
|
|
3814
3958
|
this.regions["bottom-right"].appendChild(this.els.zoom);
|
|
@@ -3818,7 +3962,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
3818
3962
|
badge.className = "sl-testbadge";
|
|
3819
3963
|
badge.textContent = t2("picker.testMode");
|
|
3820
3964
|
badge.setAttribute("aria-label", t2("picker.testMode"));
|
|
3821
|
-
this.
|
|
3965
|
+
this.els.map.appendChild(badge);
|
|
3822
3966
|
}
|
|
3823
3967
|
const chartTheme = this.controller.doc?.theme;
|
|
3824
3968
|
Object.entries(resolveTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
@@ -4055,8 +4199,9 @@ var SeatPicker = class _SeatPicker {
|
|
|
4055
4199
|
* idempotent DOM apply used at load and on transition.
|
|
4056
4200
|
*/
|
|
4057
4201
|
setSalesClosed(closed) {
|
|
4058
|
-
|
|
4059
|
-
this.salesClosed
|
|
4202
|
+
const next = closed || !!this.opts.readOnly;
|
|
4203
|
+
if (this.salesClosed === next) return;
|
|
4204
|
+
this.salesClosed = next;
|
|
4060
4205
|
this.applySalesClosed();
|
|
4061
4206
|
}
|
|
4062
4207
|
applySalesClosed() {
|
|
@@ -4940,6 +5085,13 @@ var SeatPicker = class _SeatPicker {
|
|
|
4940
5085
|
el.innerHTML = `<div class="sl-confirm-grid">` + identityFields + `</div><div class="sl-confirm-cat"><span class="sl-dot" style="background:${cat?.color ?? "#6e7bff"}"></span><span class="sl-confirm-cat-name">${safe(details?.categoryLabel ?? cat?.label ?? seat.categoryKey)}</span>` + (price != null ? `<span class="sl-confirm-price">${this.money(price)}</span>` : "") + `</div><div class="sl-confirm-body">` + this.wheelchairConfirmHtml(details?.wheelchairSpaceType) + this.commercialConfirmHtml(seat.commercial) + (this.seatViewEnabled() ? this.confirmThumbHtml(seat) : "") + this.see3dConfirmHtml() + `<div class="sl-confirm-row"><button type="button" class="sl-confirm-cancel">Cancel</button><button type="button" class="sl-confirm-add"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4 4L19 7"/></svg>Select</button></div></div>`;
|
|
4941
5086
|
this.els.map.appendChild(el);
|
|
4942
5087
|
this.confirmEl = el;
|
|
5088
|
+
const thumb = el.querySelector(".sl-confirm-thumb");
|
|
5089
|
+
if (thumb && seat.viewUrl) {
|
|
5090
|
+
const thumbReference = seat.viewMeta?.previewUrl ?? seat.viewUrl;
|
|
5091
|
+
void this.buyerAssetUrls.resolve(thumbReference).then((url) => {
|
|
5092
|
+
if (url && el.isConnected && this.confirmEl === el) thumb.src = url;
|
|
5093
|
+
}).catch((error) => this.opts.onError?.(error));
|
|
5094
|
+
}
|
|
4943
5095
|
this.reanchorConfirm();
|
|
4944
5096
|
el.querySelector(".sl-confirm-view")?.addEventListener("click", () => void this.openSeatView(seat));
|
|
4945
5097
|
el.querySelector(".sl-confirm-3d")?.addEventListener("click", () => {
|
|
@@ -5020,6 +5172,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
5020
5172
|
*/
|
|
5021
5173
|
async openSeatView(seat) {
|
|
5022
5174
|
if (!this.root || !this.seatViewEnabled()) return;
|
|
5175
|
+
const generation = ++this.seatViewGen;
|
|
5023
5176
|
const doc = this.controller.doc;
|
|
5024
5177
|
const activeId = this.controller.getActiveFloorId();
|
|
5025
5178
|
const focal = seat.focalPoint ?? doc?.floors?.find((f) => f.id === activeId)?.focalPoint ?? doc?.focalPoint ?? { x: 0, y: 0 };
|
|
@@ -5027,9 +5180,24 @@ var SeatPicker = class _SeatPicker {
|
|
|
5027
5180
|
let caption;
|
|
5028
5181
|
let real = false;
|
|
5029
5182
|
if (seat.viewUrl) {
|
|
5183
|
+
let resolvedUrl;
|
|
5184
|
+
let resolvedPreviewUrl = null;
|
|
5185
|
+
try {
|
|
5186
|
+
const previewReference = seat.viewMeta?.previewUrl;
|
|
5187
|
+
if (previewReference && previewReference !== seat.viewUrl) {
|
|
5188
|
+
resolvedPreviewUrl = await this.buyerAssetUrls.resolve(previewReference);
|
|
5189
|
+
resolvedUrl = seat.viewUrl;
|
|
5190
|
+
} else {
|
|
5191
|
+
resolvedUrl = await this.buyerAssetUrls.resolve(seat.viewUrl);
|
|
5192
|
+
}
|
|
5193
|
+
} catch (error) {
|
|
5194
|
+
if (generation === this.seatViewGen) this.opts.onError?.(error);
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
5197
|
+
if (generation !== this.seatViewGen || !resolvedUrl || seat.viewMeta?.previewUrl && seat.viewMeta.previewUrl !== seat.viewUrl && !resolvedPreviewUrl || !this.root || !this.seatViewEnabled()) return;
|
|
5030
5198
|
const view = {
|
|
5031
|
-
url:
|
|
5032
|
-
...
|
|
5199
|
+
url: resolvedUrl,
|
|
5200
|
+
...resolvedPreviewUrl ? { previewUrl: resolvedPreviewUrl } : {},
|
|
5033
5201
|
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
5034
5202
|
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
5035
5203
|
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
@@ -5047,14 +5215,14 @@ var SeatPicker = class _SeatPicker {
|
|
|
5047
5215
|
const { generateSeatPanorama } = await loadPanorama();
|
|
5048
5216
|
pano2 = generateSeatPanorama(seat, focal, this.allSeats());
|
|
5049
5217
|
} catch (err) {
|
|
5050
|
-
this.opts.onError?.(err);
|
|
5218
|
+
if (generation === this.seatViewGen) this.opts.onError?.(err);
|
|
5051
5219
|
return;
|
|
5052
5220
|
}
|
|
5053
|
-
if (!this.root || !this.seatViewEnabled()) return;
|
|
5221
|
+
if (generation !== this.seatViewGen || !this.root || !this.seatViewEnabled()) return;
|
|
5054
5222
|
panoSource = { url: pano2.url, generated: true };
|
|
5055
5223
|
caption = t2("picker.illustrationCaption", { m: pano2.distanceM });
|
|
5056
5224
|
}
|
|
5057
|
-
this.closeSeatView();
|
|
5225
|
+
this.closeSeatView(false);
|
|
5058
5226
|
const el = document.createElement("div");
|
|
5059
5227
|
el.className = "sl-view";
|
|
5060
5228
|
el.setAttribute("role", "dialog");
|
|
@@ -5070,9 +5238,12 @@ var SeatPicker = class _SeatPicker {
|
|
|
5070
5238
|
};
|
|
5071
5239
|
if (delivery.upgradeUrl) {
|
|
5072
5240
|
cancelUpgrade = schedulePanoramaUpgrade(() => {
|
|
5073
|
-
void
|
|
5074
|
-
if (!
|
|
5075
|
-
|
|
5241
|
+
void this.buyerAssetUrls.resolve(delivery.upgradeUrl).then((url) => {
|
|
5242
|
+
if (!url || loadAbort.signal.aborted) return null;
|
|
5243
|
+
return loadPanoramaImage(url, loadAbort.signal).then(() => url);
|
|
5244
|
+
}).then((url) => {
|
|
5245
|
+
if (!url || !el.isConnected || loadAbort.signal.aborted) return;
|
|
5246
|
+
pano.style.backgroundImage = `url("${url}")`;
|
|
5076
5247
|
}).catch(() => {
|
|
5077
5248
|
});
|
|
5078
5249
|
});
|
|
@@ -5148,7 +5319,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
5148
5319
|
el.removeEventListener("keydown", onKey);
|
|
5149
5320
|
};
|
|
5150
5321
|
}
|
|
5151
|
-
closeSeatView() {
|
|
5322
|
+
closeSeatView(cancelPending = true) {
|
|
5323
|
+
if (cancelPending) this.seatViewGen += 1;
|
|
5152
5324
|
this.viewCleanup?.();
|
|
5153
5325
|
this.viewCleanup = null;
|
|
5154
5326
|
this.viewEl?.remove();
|
|
@@ -6080,6 +6252,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
6080
6252
|
this.opts.theme = { ...this.opts.theme ?? {}, map: map ?? void 0 };
|
|
6081
6253
|
this.controller.setMapTheme(map);
|
|
6082
6254
|
}
|
|
6255
|
+
/**
|
|
6256
|
+
* Let a host suppress duplicate event identity after mount without remounting
|
|
6257
|
+
* the live picker (and therefore without disturbing a selection or hold).
|
|
6258
|
+
* Full-screen mode still restores the identity until the buyer exits it.
|
|
6259
|
+
*/
|
|
6260
|
+
setEventDetailsHidden(hidden) {
|
|
6261
|
+
this.eventDetailsHidden = hidden;
|
|
6262
|
+
this.syncFullscreenButtons();
|
|
6263
|
+
}
|
|
6083
6264
|
/**
|
|
6084
6265
|
* Replace the host pricing override AFTER mount, and repaint everything that
|
|
6085
6266
|
* shows a price.
|
|
@@ -6231,19 +6412,33 @@ var SeatPicker = class _SeatPicker {
|
|
|
6231
6412
|
async seatViewFor3d(seatId) {
|
|
6232
6413
|
const seat = this.allSeats().find((s) => s.id === seatId);
|
|
6233
6414
|
if (!seat) return null;
|
|
6234
|
-
if (seat.viewUrl)
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6415
|
+
if (seat.viewUrl) {
|
|
6416
|
+
try {
|
|
6417
|
+
const previewReference = seat.viewMeta?.previewUrl;
|
|
6418
|
+
const progressive = !!previewReference && previewReference !== seat.viewUrl;
|
|
6419
|
+
const previewUrl = progressive ? await this.buyerAssetUrls.resolve(previewReference) : null;
|
|
6420
|
+
const url = progressive ? seat.viewUrl : await this.buyerAssetUrls.resolve(seat.viewUrl);
|
|
6421
|
+
if (!url) return null;
|
|
6422
|
+
if (progressive && !previewUrl) return null;
|
|
6423
|
+
return {
|
|
6424
|
+
url,
|
|
6425
|
+
...previewUrl ? { previewUrl } : {},
|
|
6426
|
+
...progressive ? { resolveUrl: (reference) => this.buyerAssetUrls.resolve(reference) } : {},
|
|
6427
|
+
...seat.viewMeta?.sourceWidth !== void 0 ? { sourceWidth: seat.viewMeta.sourceWidth } : {},
|
|
6428
|
+
...seat.viewMeta?.sourceHeight !== void 0 ? { sourceHeight: seat.viewMeta.sourceHeight } : {},
|
|
6429
|
+
...seat.viewMeta?.previewWidth !== void 0 ? { previewWidth: seat.viewMeta.previewWidth } : {},
|
|
6430
|
+
...seat.viewMeta?.previewHeight !== void 0 ? { previewHeight: seat.viewMeta.previewHeight } : {},
|
|
6431
|
+
...seat.viewMeta?.initialBearingDeg !== void 0 ? { initialBearingDeg: seat.viewMeta.initialBearingDeg } : {},
|
|
6432
|
+
...seat.viewMeta?.initialPitchDeg !== void 0 ? { initialPitchDeg: seat.viewMeta.initialPitchDeg } : {},
|
|
6433
|
+
...seat.viewMeta?.coverage ? { coverage: seat.viewMeta.coverage } : {},
|
|
6434
|
+
...seat.viewMeta?.capturedAt ? { capturedAt: seat.viewMeta.capturedAt } : {},
|
|
6435
|
+
...seat.viewMeta?.sourceLabel ? { sourceLabel: seat.viewMeta.sourceLabel } : {}
|
|
6436
|
+
};
|
|
6437
|
+
} catch (error) {
|
|
6438
|
+
this.opts.onError?.(error);
|
|
6439
|
+
return null;
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6247
6442
|
const doc = this.controller.doc;
|
|
6248
6443
|
if (!doc) return null;
|
|
6249
6444
|
const activeId = this.controller.getActiveFloorId();
|
|
@@ -6826,6 +7021,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
6826
7021
|
this.closeSeatView();
|
|
6827
7022
|
this.closeCheckoutPanel();
|
|
6828
7023
|
this.exit3d();
|
|
7024
|
+
this.buyerAssetUrls.dispose();
|
|
6829
7025
|
this.stopHoldTimer();
|
|
6830
7026
|
if (this.toastTimer) clearTimeout(this.toastTimer);
|
|
6831
7027
|
if (this.liveTimer) clearTimeout(this.liveTimer);
|