@seatlayer/core 0.52.0 → 0.53.0
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/LICENSE +16 -17
- package/README.md +8 -18
- package/dist/chunk-54A7LIL5.js +1 -0
- package/dist/chunk-IJI4WU57.js +1 -0
- package/dist/chunk-XPPT3OGQ.js +1 -0
- package/dist/core/seatConfidence.cjs +1 -76
- package/dist/core/seatConfidence.js +1 -51
- package/dist/de-75NWRKC5.js +1 -0
- package/dist/es-BM7EJ7PW.js +1 -0
- package/dist/fr-HPXQHIB4.js +1 -0
- package/dist/index.cjs +1 -12717
- package/dist/index.d.cts +38 -53
- package/dist/index.d.ts +38 -53
- package/dist/index.js +1 -10235
- package/dist/picker/minimapGeometry.cjs +1 -80
- package/dist/picker/minimapGeometry.js +1 -52
- package/dist/view/panoramaDelivery.cjs +1 -126
- package/dist/view/panoramaDelivery.js +1 -17
- package/dist/view3d/crossfade/panorama.cjs +1 -459
- package/dist/view3d/crossfade/panorama.d.cts +1 -3
- package/dist/view3d/crossfade/panorama.d.ts +1 -3
- package/dist/view3d/crossfade/panorama.js +1 -30
- package/dist/view3d/index.cjs +27 -7929
- package/dist/view3d/index.d.cts +62 -21
- package/dist/view3d/index.d.ts +62 -21
- package/dist/view3d/index.js +27 -7062
- package/package.json +2 -7
- package/dist/chunk-AZODENEL.js +0 -97
- package/dist/chunk-AZODENEL.js.map +0 -1
- package/dist/chunk-BE3F7CT3.js +0 -341
- package/dist/chunk-BE3F7CT3.js.map +0 -1
- package/dist/chunk-UID7KN44.js +0 -1569
- package/dist/chunk-UID7KN44.js.map +0 -1
- package/dist/core/seatConfidence.cjs.map +0 -1
- package/dist/core/seatConfidence.js.map +0 -1
- package/dist/de-YGODYJ3E.js +0 -292
- package/dist/de-YGODYJ3E.js.map +0 -1
- package/dist/es-VMBTGVFQ.js +0 -292
- package/dist/es-VMBTGVFQ.js.map +0 -1
- package/dist/fr-SLBE2ULB.js +0 -292
- package/dist/fr-SLBE2ULB.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/picker/minimapGeometry.cjs.map +0 -1
- package/dist/picker/minimapGeometry.js.map +0 -1
- package/dist/view/panoramaDelivery.cjs.map +0 -1
- package/dist/view/panoramaDelivery.js.map +0 -1
- package/dist/view3d/crossfade/panorama.cjs.map +0 -1
- package/dist/view3d/crossfade/panorama.js.map +0 -1
- package/dist/view3d/index.cjs.map +0 -1
- package/dist/view3d/index.js.map +0 -1
|
@@ -1,459 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/view3d/crossfade/panorama.ts
|
|
21
|
-
var panorama_exports = {};
|
|
22
|
-
__export(panorama_exports, {
|
|
23
|
-
MAX_PITCH_DEG: () => MAX_PITCH_DEG,
|
|
24
|
-
MAX_VFOV_DEG: () => MAX_VFOV_DEG,
|
|
25
|
-
MIN_VFOV_DEG: () => MIN_VFOV_DEG,
|
|
26
|
-
VFOV_DEG: () => VFOV_DEG,
|
|
27
|
-
bearingToOffsetPx: () => bearingToOffsetPx,
|
|
28
|
-
clampPanoramaFov: () => clampPanoramaFov,
|
|
29
|
-
clampPitchPx: () => clampPitchPx,
|
|
30
|
-
horizonOffsetPy: () => horizonOffsetPy,
|
|
31
|
-
isAuthoredSeatView: () => isAuthoredSeatView,
|
|
32
|
-
mountPanorama: () => mountPanorama,
|
|
33
|
-
seatViewDisclosure: () => seatViewDisclosure,
|
|
34
|
-
windowedBgHeight: () => windowedBgHeight
|
|
35
|
-
});
|
|
36
|
-
module.exports = __toCommonJS(panorama_exports);
|
|
37
|
-
|
|
38
|
-
// src/view/panoramaDelivery.ts
|
|
39
|
-
function estimatePanoramaTextureBytes(width, height) {
|
|
40
|
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return 0;
|
|
41
|
-
return Math.ceil(width * height * 4 * (4 / 3));
|
|
42
|
-
}
|
|
43
|
-
function panoramaTextureBudgetBytes(deviceMemoryGb) {
|
|
44
|
-
const mib = deviceMemoryGb !== void 0 && deviceMemoryGb <= 2 ? 32 : deviceMemoryGb !== void 0 && deviceMemoryGb >= 8 ? 192 : 96;
|
|
45
|
-
return mib * 1024 * 1024;
|
|
46
|
-
}
|
|
47
|
-
function browserPanoramaConstraints(maxTextureSize) {
|
|
48
|
-
const nav = typeof navigator === "undefined" ? void 0 : navigator;
|
|
49
|
-
return {
|
|
50
|
-
saveData: nav?.connection?.saveData === true,
|
|
51
|
-
maxTextureSize,
|
|
52
|
-
maxDecodedBytes: panoramaTextureBudgetBytes(nav?.deviceMemory)
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
function planPanoramaDelivery(source, constraints = {}) {
|
|
56
|
-
const previewUrl = source.previewUrl?.trim();
|
|
57
|
-
if (!previewUrl || previewUrl === source.url) {
|
|
58
|
-
return {
|
|
59
|
-
initialUrl: source.url,
|
|
60
|
-
initialWidth: source.sourceWidth,
|
|
61
|
-
initialHeight: source.sourceHeight,
|
|
62
|
-
reason: "full-only"
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
const initial = {
|
|
66
|
-
initialUrl: previewUrl,
|
|
67
|
-
initialWidth: source.previewWidth,
|
|
68
|
-
initialHeight: source.previewHeight
|
|
69
|
-
};
|
|
70
|
-
if (constraints.saveData) return { ...initial, reason: "save-data" };
|
|
71
|
-
if (constraints.maxTextureSize !== void 0 && source.sourceWidth !== void 0 && source.sourceHeight !== void 0 && Math.max(source.sourceWidth, source.sourceHeight) > constraints.maxTextureSize) {
|
|
72
|
-
return { ...initial, reason: "texture-limit" };
|
|
73
|
-
}
|
|
74
|
-
if (constraints.maxDecodedBytes !== void 0 && source.sourceWidth !== void 0 && source.sourceHeight !== void 0 && estimatePanoramaTextureBytes(source.sourceWidth, source.sourceHeight) > constraints.maxDecodedBytes) {
|
|
75
|
-
return { ...initial, reason: "memory-limit" };
|
|
76
|
-
}
|
|
77
|
-
return { ...initial, upgradeUrl: source.url, reason: "progressive" };
|
|
78
|
-
}
|
|
79
|
-
function abortError() {
|
|
80
|
-
return new DOMException("Panorama image load aborted", "AbortError");
|
|
81
|
-
}
|
|
82
|
-
function loadPanoramaImage(url, signal) {
|
|
83
|
-
return new Promise((resolve, reject) => {
|
|
84
|
-
if (signal?.aborted) {
|
|
85
|
-
reject(abortError());
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const image = new Image();
|
|
89
|
-
image.crossOrigin = "anonymous";
|
|
90
|
-
let settled = false;
|
|
91
|
-
const cleanup = () => {
|
|
92
|
-
image.onload = null;
|
|
93
|
-
image.onerror = null;
|
|
94
|
-
signal?.removeEventListener("abort", onAbort);
|
|
95
|
-
};
|
|
96
|
-
const finish = (error) => {
|
|
97
|
-
if (settled) return;
|
|
98
|
-
settled = true;
|
|
99
|
-
cleanup();
|
|
100
|
-
if (error) reject(error);
|
|
101
|
-
else resolve(image);
|
|
102
|
-
};
|
|
103
|
-
const onAbort = () => {
|
|
104
|
-
image.src = "";
|
|
105
|
-
finish(abortError());
|
|
106
|
-
};
|
|
107
|
-
image.onload = () => {
|
|
108
|
-
const decoded = typeof image.decode === "function" ? image.decode() : Promise.resolve();
|
|
109
|
-
void decoded.then(() => finish(), () => finish());
|
|
110
|
-
};
|
|
111
|
-
image.onerror = () => finish(new Error("panorama_image_failed"));
|
|
112
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
113
|
-
image.src = url;
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
function schedulePanoramaUpgrade(work) {
|
|
117
|
-
const host = globalThis;
|
|
118
|
-
if (host.requestIdleCallback) {
|
|
119
|
-
const id2 = host.requestIdleCallback(work, { timeout: 1200 });
|
|
120
|
-
return () => host.cancelIdleCallback?.(id2);
|
|
121
|
-
}
|
|
122
|
-
const id = globalThis.setTimeout(work, 32);
|
|
123
|
-
return () => globalThis.clearTimeout(id);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// src/view3d/crossfade/panorama.ts
|
|
127
|
-
function seatViewDisclosure(view) {
|
|
128
|
-
const demo = view.mediaKind === "demo-render";
|
|
129
|
-
const coverage = view.generated || view.mediaKind === "model" ? "Live 3D \xB7 chart-derived seat-eye" : demo ? view.coverage === "exact-seat" ? "AI-generated exact-seat demo" : view.coverage === "row-representative" ? "AI-generated representative row demo" : view.coverage === "section-representative" ? "AI-generated representative section demo" : "AI-generated illustrative venue demo" : view.coverage === "exact-seat" ? "Exact seat photo" : view.coverage === "row-representative" ? "Representative row view" : view.coverage === "section-representative" ? "Representative section view" : view.coverage === "venue-representative" ? "Representative venue view" : "Venue photo";
|
|
130
|
-
const year = view.capturedAt && /^\d{4}/.test(view.capturedAt) ? view.capturedAt.slice(0, 4) : "";
|
|
131
|
-
return [coverage, year ? `${demo ? "created" : "captured"} ${year}` : "", view.sourceLabel ?? ""].filter(Boolean).join(" \xB7 ");
|
|
132
|
-
}
|
|
133
|
-
function isAuthoredSeatView(view) {
|
|
134
|
-
if (view.generated || view.mediaKind === "model" || view.mediaKind === "demo-render") return false;
|
|
135
|
-
const source = view.sourceLabel?.trim() ?? "";
|
|
136
|
-
return !/^(?:AI[- ]generated|generated\s+(?:demo|preview))/i.test(source);
|
|
137
|
-
}
|
|
138
|
-
var VFOV_DEG = 70;
|
|
139
|
-
var MIN_VFOV_DEG = 35;
|
|
140
|
-
var MAX_VFOV_DEG = 90;
|
|
141
|
-
function clampPanoramaFov(fovDeg) {
|
|
142
|
-
return Math.max(MIN_VFOV_DEG, Math.min(MAX_VFOV_DEG, fovDeg));
|
|
143
|
-
}
|
|
144
|
-
var MAX_PITCH_DEG = 35;
|
|
145
|
-
function bearingToOffsetPx(bearingDeg, viewportW, bgW) {
|
|
146
|
-
const col = (0.5 + bearingDeg / 360) * bgW;
|
|
147
|
-
return viewportW / 2 - col;
|
|
148
|
-
}
|
|
149
|
-
function windowedBgHeight(viewportH, vfovDeg = VFOV_DEG) {
|
|
150
|
-
return viewportH * (180 / vfovDeg);
|
|
151
|
-
}
|
|
152
|
-
function horizonOffsetPy(viewportH, bgH, pitchPx) {
|
|
153
|
-
return (viewportH - bgH) / 2 + clampPitchPx(pitchPx, bgH);
|
|
154
|
-
}
|
|
155
|
-
function clampPitchPx(pitchPx, bgH) {
|
|
156
|
-
const limit = MAX_PITCH_DEG / 180 * bgH;
|
|
157
|
-
return Math.max(-limit, Math.min(limit, pitchPx));
|
|
158
|
-
}
|
|
159
|
-
function mountPanorama(container, view, opts = {}) {
|
|
160
|
-
const fadeMs = opts.fadeMs ?? 400;
|
|
161
|
-
const bearing = view.initialBearingDeg ?? 0;
|
|
162
|
-
const delivery = planPanoramaDelivery(view, browserPanoramaConstraints());
|
|
163
|
-
const loadAbort = new AbortController();
|
|
164
|
-
const abortFromCaller = () => loadAbort.abort();
|
|
165
|
-
opts.signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
166
|
-
const root = document.createElement("div");
|
|
167
|
-
const priorFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
168
|
-
root.setAttribute("role", "dialog");
|
|
169
|
-
root.setAttribute("aria-modal", "true");
|
|
170
|
-
root.setAttribute("aria-label", opts.seatLabel ? `View from ${opts.seatLabel}` : "View from seat");
|
|
171
|
-
root.tabIndex = -1;
|
|
172
|
-
Object.assign(root.style, {
|
|
173
|
-
position: "absolute",
|
|
174
|
-
inset: "0",
|
|
175
|
-
zIndex: "10",
|
|
176
|
-
opacity: "0",
|
|
177
|
-
transition: `opacity ${fadeMs}ms ease`,
|
|
178
|
-
background: "#05070c",
|
|
179
|
-
overflow: "hidden",
|
|
180
|
-
touchAction: "none"
|
|
181
|
-
});
|
|
182
|
-
const pano = document.createElement("div");
|
|
183
|
-
Object.assign(pano.style, {
|
|
184
|
-
position: "absolute",
|
|
185
|
-
inset: "0",
|
|
186
|
-
backgroundImage: `url("${delivery.initialUrl}")`,
|
|
187
|
-
backgroundRepeat: "repeat-x",
|
|
188
|
-
cursor: "grab"
|
|
189
|
-
});
|
|
190
|
-
root.appendChild(pano);
|
|
191
|
-
const closeBtn = document.createElement("button");
|
|
192
|
-
closeBtn.type = "button";
|
|
193
|
-
closeBtn.setAttribute("aria-label", "Close");
|
|
194
|
-
closeBtn.textContent = "\u2715";
|
|
195
|
-
Object.assign(closeBtn.style, {
|
|
196
|
-
position: "absolute",
|
|
197
|
-
top: "12px",
|
|
198
|
-
right: "12px",
|
|
199
|
-
zIndex: "2",
|
|
200
|
-
width: "44px",
|
|
201
|
-
height: "44px",
|
|
202
|
-
borderRadius: "999px",
|
|
203
|
-
cursor: "pointer",
|
|
204
|
-
border: "1px solid rgba(255,255,255,0.25)",
|
|
205
|
-
background: "rgba(8,12,18,0.6)",
|
|
206
|
-
color: "#e6edf3",
|
|
207
|
-
fontSize: "15px",
|
|
208
|
-
lineHeight: "1"
|
|
209
|
-
});
|
|
210
|
-
root.appendChild(closeBtn);
|
|
211
|
-
if (opts.disclosure) {
|
|
212
|
-
const disclosure = document.createElement("div");
|
|
213
|
-
disclosure.textContent = `${opts.disclosurePrefix ?? "360\xB0 panorama"} \xB7 ${opts.disclosure}`;
|
|
214
|
-
Object.assign(disclosure.style, {
|
|
215
|
-
position: "absolute",
|
|
216
|
-
top: "12px",
|
|
217
|
-
left: "12px",
|
|
218
|
-
zIndex: "2",
|
|
219
|
-
maxWidth: "calc(100% - 88px)",
|
|
220
|
-
padding: "8px 11px",
|
|
221
|
-
borderRadius: "999px",
|
|
222
|
-
overflow: "hidden",
|
|
223
|
-
color: "#dce6f8",
|
|
224
|
-
background: "rgba(8,12,18,0.68)",
|
|
225
|
-
font: "600 11px/1.2 ui-sans-serif, system-ui, sans-serif",
|
|
226
|
-
textOverflow: "ellipsis",
|
|
227
|
-
whiteSpace: "nowrap",
|
|
228
|
-
pointerEvents: "none",
|
|
229
|
-
backdropFilter: "blur(6px)"
|
|
230
|
-
});
|
|
231
|
-
root.appendChild(disclosure);
|
|
232
|
-
}
|
|
233
|
-
const hint = document.createElement("div");
|
|
234
|
-
hint.textContent = "Drag to look \xB7 pinch or scroll to zoom \xB7 Esc to close";
|
|
235
|
-
Object.assign(hint.style, {
|
|
236
|
-
position: "absolute",
|
|
237
|
-
bottom: "12px",
|
|
238
|
-
left: "0",
|
|
239
|
-
right: "0",
|
|
240
|
-
textAlign: "center",
|
|
241
|
-
color: "rgba(230,237,243,0.7)",
|
|
242
|
-
font: "12px ui-sans-serif, system-ui, sans-serif",
|
|
243
|
-
pointerEvents: "none"
|
|
244
|
-
});
|
|
245
|
-
root.appendChild(hint);
|
|
246
|
-
container.appendChild(root);
|
|
247
|
-
closeBtn.focus({ preventScroll: true });
|
|
248
|
-
let bgW = 0;
|
|
249
|
-
let bgH = 0;
|
|
250
|
-
let posX = 0;
|
|
251
|
-
let pitchPx = 0;
|
|
252
|
-
let viewFov = VFOV_DEG;
|
|
253
|
-
const layout = () => {
|
|
254
|
-
const vh = root.clientHeight || 1;
|
|
255
|
-
const vw = root.clientWidth || 1;
|
|
256
|
-
const natW = img.naturalWidth || vw * 2;
|
|
257
|
-
const natH = img.naturalHeight || vh;
|
|
258
|
-
const centredImageRatio = bgW > 0 ? (vw / 2 - posX) / bgW : 0;
|
|
259
|
-
const pitchRatio = bgH > 0 ? pitchPx / bgH : 0;
|
|
260
|
-
bgH = windowedBgHeight(vh, viewFov);
|
|
261
|
-
bgW = bgH * (natW / natH);
|
|
262
|
-
pano.style.backgroundSize = `${bgW}px ${bgH}px`;
|
|
263
|
-
if (!posInitialised) {
|
|
264
|
-
posX = bearingToOffsetPx(bearing, vw, bgW);
|
|
265
|
-
pitchPx = clampPitchPx((view.initialPitchDeg ?? 0) / 180 * bgH, bgH);
|
|
266
|
-
posInitialised = true;
|
|
267
|
-
} else posX = vw / 2 - centredImageRatio * bgW;
|
|
268
|
-
pitchPx = clampPitchPx(pitchRatio * bgH, bgH);
|
|
269
|
-
pano.style.backgroundPosition = `${posX}px ${horizonOffsetPy(vh, bgH, pitchPx)}px`;
|
|
270
|
-
};
|
|
271
|
-
let posInitialised = false;
|
|
272
|
-
const applyPos = () => {
|
|
273
|
-
const vh = root.clientHeight || 1;
|
|
274
|
-
pitchPx = clampPitchPx(pitchPx, bgH);
|
|
275
|
-
pano.style.backgroundPosition = `${posX}px ${horizonOffsetPy(vh, bgH, pitchPx)}px`;
|
|
276
|
-
};
|
|
277
|
-
let img = new Image();
|
|
278
|
-
img.onload = layout;
|
|
279
|
-
img.src = delivery.initialUrl;
|
|
280
|
-
let cancelUpgrade = () => {
|
|
281
|
-
};
|
|
282
|
-
if (delivery.upgradeUrl) {
|
|
283
|
-
cancelUpgrade = schedulePanoramaUpgrade(() => {
|
|
284
|
-
const resolved = view.resolveUrl ? view.resolveUrl(delivery.upgradeUrl) : Promise.resolve(delivery.upgradeUrl);
|
|
285
|
-
void resolved.then((url) => {
|
|
286
|
-
if (!url || loadAbort.signal.aborted) return null;
|
|
287
|
-
return loadPanoramaImage(url, loadAbort.signal).then((full) => ({ full, url }));
|
|
288
|
-
}).then((loaded) => {
|
|
289
|
-
if (!loaded) return;
|
|
290
|
-
if (disposed || loadAbort.signal.aborted) return;
|
|
291
|
-
img = loaded.full;
|
|
292
|
-
pano.style.backgroundImage = `url("${loaded.url}")`;
|
|
293
|
-
layout();
|
|
294
|
-
}).catch(() => {
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
requestAnimationFrame(layout);
|
|
299
|
-
let dragging = false;
|
|
300
|
-
let lastX = 0;
|
|
301
|
-
let lastY = 0;
|
|
302
|
-
let pinchDistance = 0;
|
|
303
|
-
const pointers = /* @__PURE__ */ new Map();
|
|
304
|
-
const distanceBetweenPointers = () => {
|
|
305
|
-
const [a, b] = [...pointers.values()];
|
|
306
|
-
return a && b ? Math.hypot(a.x - b.x, a.y - b.y) : 0;
|
|
307
|
-
};
|
|
308
|
-
const setViewFov = (nextFov) => {
|
|
309
|
-
const clamped = clampPanoramaFov(nextFov);
|
|
310
|
-
if (clamped === viewFov) return;
|
|
311
|
-
viewFov = clamped;
|
|
312
|
-
layout();
|
|
313
|
-
};
|
|
314
|
-
const onDown = (e) => {
|
|
315
|
-
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
316
|
-
dragging = pointers.size === 1;
|
|
317
|
-
lastX = e.clientX;
|
|
318
|
-
lastY = e.clientY;
|
|
319
|
-
if (pointers.size === 2) pinchDistance = distanceBetweenPointers();
|
|
320
|
-
pano.style.cursor = "grabbing";
|
|
321
|
-
try {
|
|
322
|
-
pano.setPointerCapture?.(e.pointerId);
|
|
323
|
-
} catch {
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
const onMove = (e) => {
|
|
327
|
-
if (!pointers.has(e.pointerId)) return;
|
|
328
|
-
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
329
|
-
if (pointers.size >= 2) {
|
|
330
|
-
const nextDistance = distanceBetweenPointers();
|
|
331
|
-
if (pinchDistance > 0 && nextDistance > 0) setViewFov(viewFov * (pinchDistance / nextDistance));
|
|
332
|
-
pinchDistance = nextDistance;
|
|
333
|
-
dragging = false;
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (!dragging) return;
|
|
337
|
-
posX += e.clientX - lastX;
|
|
338
|
-
pitchPx += e.clientY - lastY;
|
|
339
|
-
lastX = e.clientX;
|
|
340
|
-
lastY = e.clientY;
|
|
341
|
-
applyPos();
|
|
342
|
-
};
|
|
343
|
-
const onUp = (e) => {
|
|
344
|
-
pointers.delete(e.pointerId);
|
|
345
|
-
pinchDistance = pointers.size === 2 ? distanceBetweenPointers() : 0;
|
|
346
|
-
const remaining = pointers.values().next().value;
|
|
347
|
-
dragging = pointers.size === 1;
|
|
348
|
-
if (remaining) {
|
|
349
|
-
lastX = remaining.x;
|
|
350
|
-
lastY = remaining.y;
|
|
351
|
-
} else pano.style.cursor = "grab";
|
|
352
|
-
try {
|
|
353
|
-
pano.releasePointerCapture?.(e.pointerId);
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
const onWheel = (e) => {
|
|
358
|
-
e.preventDefault();
|
|
359
|
-
setViewFov(viewFov + e.deltaY * 0.04);
|
|
360
|
-
};
|
|
361
|
-
pano.addEventListener("pointerdown", onDown);
|
|
362
|
-
pano.addEventListener("pointermove", onMove);
|
|
363
|
-
pano.addEventListener("pointerup", onUp);
|
|
364
|
-
pano.addEventListener("pointercancel", onUp);
|
|
365
|
-
pano.addEventListener("wheel", onWheel, { passive: false });
|
|
366
|
-
let closed = false;
|
|
367
|
-
let disposed = false;
|
|
368
|
-
let fadeTimer = 0;
|
|
369
|
-
const removeListeners = () => {
|
|
370
|
-
pano.removeEventListener("pointerdown", onDown);
|
|
371
|
-
pano.removeEventListener("pointermove", onMove);
|
|
372
|
-
pano.removeEventListener("pointerup", onUp);
|
|
373
|
-
pano.removeEventListener("pointercancel", onUp);
|
|
374
|
-
pano.removeEventListener("wheel", onWheel);
|
|
375
|
-
window.removeEventListener("keydown", onKey);
|
|
376
|
-
};
|
|
377
|
-
const teardown = () => {
|
|
378
|
-
cancelUpgrade();
|
|
379
|
-
loadAbort.abort();
|
|
380
|
-
opts.signal?.removeEventListener("abort", abortFromCaller);
|
|
381
|
-
if (fadeTimer) {
|
|
382
|
-
window.clearTimeout(fadeTimer);
|
|
383
|
-
fadeTimer = 0;
|
|
384
|
-
}
|
|
385
|
-
removeListeners();
|
|
386
|
-
if (root.parentNode) root.parentNode.removeChild(root);
|
|
387
|
-
if (priorFocus?.isConnected) priorFocus.focus({ preventScroll: true });
|
|
388
|
-
};
|
|
389
|
-
const close = () => {
|
|
390
|
-
if (closed) return;
|
|
391
|
-
closed = true;
|
|
392
|
-
root.style.opacity = "0";
|
|
393
|
-
const done = () => {
|
|
394
|
-
fadeTimer = 0;
|
|
395
|
-
if (disposed) return;
|
|
396
|
-
teardown();
|
|
397
|
-
opts.onClose?.();
|
|
398
|
-
};
|
|
399
|
-
fadeTimer = window.setTimeout(done, fadeMs);
|
|
400
|
-
};
|
|
401
|
-
const onKey = (e) => {
|
|
402
|
-
if (e.key === "Tab") {
|
|
403
|
-
e.preventDefault();
|
|
404
|
-
closeBtn.focus({ preventScroll: true });
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (e.key === "Escape") {
|
|
408
|
-
e.stopPropagation();
|
|
409
|
-
close();
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
|
413
|
-
posX += e.key === "ArrowLeft" ? -32 : 32;
|
|
414
|
-
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
|
415
|
-
pitchPx += (e.key === "ArrowUp" ? 4 : -4) * (bgH / 180);
|
|
416
|
-
} else if (e.key === "+" || e.key === "=") {
|
|
417
|
-
setViewFov(viewFov - 5);
|
|
418
|
-
e.preventDefault();
|
|
419
|
-
return;
|
|
420
|
-
} else if (e.key === "-") {
|
|
421
|
-
setViewFov(viewFov + 5);
|
|
422
|
-
e.preventDefault();
|
|
423
|
-
return;
|
|
424
|
-
} else {
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
e.preventDefault();
|
|
428
|
-
applyPos();
|
|
429
|
-
};
|
|
430
|
-
window.addEventListener("keydown", onKey);
|
|
431
|
-
closeBtn.addEventListener("click", close);
|
|
432
|
-
requestAnimationFrame(() => {
|
|
433
|
-
root.style.opacity = "1";
|
|
434
|
-
});
|
|
435
|
-
return {
|
|
436
|
-
close,
|
|
437
|
-
dispose() {
|
|
438
|
-
closed = true;
|
|
439
|
-
disposed = true;
|
|
440
|
-
teardown();
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
445
|
-
0 && (module.exports = {
|
|
446
|
-
MAX_PITCH_DEG,
|
|
447
|
-
MAX_VFOV_DEG,
|
|
448
|
-
MIN_VFOV_DEG,
|
|
449
|
-
VFOV_DEG,
|
|
450
|
-
bearingToOffsetPx,
|
|
451
|
-
clampPanoramaFov,
|
|
452
|
-
clampPitchPx,
|
|
453
|
-
horizonOffsetPy,
|
|
454
|
-
isAuthoredSeatView,
|
|
455
|
-
mountPanorama,
|
|
456
|
-
seatViewDisclosure,
|
|
457
|
-
windowedBgHeight
|
|
458
|
-
});
|
|
459
|
-
//# sourceMappingURL=panorama.cjs.map
|
|
1
|
+
"use strict";var V=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var le=Object.prototype.hasOwnProperty;var de=(e,n)=>{for(var r in n)V(e,r,{get:n[r],enumerable:!0})},ce=(e,n,r,a)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of se(n))!le.call(e,l)&&l!==r&&V(e,l,{get:()=>n[l],enumerable:!(a=ae(n,l))||a.enumerable});return e};var ue=e=>ce(V({},"__esModule",{value:!0}),e);var he={};de(he,{MAX_PITCH_DEG:()=>Z,VFOV_DEG:()=>z,bearingToOffsetPx:()=>ee,clampPanoramaFov:()=>Q,clampPitchPx:()=>E,horizonOffsetPy:()=>O,isAuthoredSeatView:()=>be,mountPanorama:()=>xe,seatViewDisclosure:()=>ve,windowedBgHeight:()=>te});module.exports=ue(he);function me(e,n){return!Number.isFinite(e)||!Number.isFinite(n)||e<=0||n<=0?0:Math.ceil(e*n*4*(4/3))}function pe(e){return(e!==void 0&&e<=2?32:e!==void 0&&e>=8?192:96)*1024*1024}function j(e){let n=typeof navigator>"u"?void 0:navigator;return{saveData:n?.connection?.saveData===!0,maxTextureSize:e,maxDecodedBytes:pe(n?.deviceMemory)}}function q(e,n={}){let r=e.previewUrl?.trim();if(!r||r===e.url)return{initialUrl:e.url,initialWidth:e.sourceWidth,initialHeight:e.sourceHeight,reason:"full-only"};let a={initialUrl:r,initialWidth:e.previewWidth,initialHeight:e.previewHeight};return n.saveData?{...a,reason:"save-data"}:n.maxTextureSize!==void 0&&e.sourceWidth!==void 0&&e.sourceHeight!==void 0&&Math.max(e.sourceWidth,e.sourceHeight)>n.maxTextureSize?{...a,reason:"texture-limit"}:n.maxDecodedBytes!==void 0&&e.sourceWidth!==void 0&&e.sourceHeight!==void 0&&me(e.sourceWidth,e.sourceHeight)>n.maxDecodedBytes?{...a,reason:"memory-limit"}:{...a,upgradeUrl:e.url,reason:"progressive"}}function N(){return new DOMException("Panorama image load aborted","AbortError")}function G(e,n){return new Promise((r,a)=>{if(n?.aborted){a(N());return}let l=new Image;l.crossOrigin="anonymous";let m=!1,v=()=>{l.onload=null,l.onerror=null,n?.removeEventListener("abort",i)},b=x=>{m||(m=!0,v(),x?a(x):r(l))},i=()=>{l.src="",b(N())};l.onload=()=>{(typeof l.decode=="function"?l.decode():Promise.resolve()).then(()=>b(),()=>b())},l.onerror=()=>b(new Error("panorama_image_failed")),n?.addEventListener("abort",i,{once:!0}),l.src=e})}function J(e){let n=globalThis;if(n.requestIdleCallback){let a=n.requestIdleCallback(e,{timeout:1200});return()=>n.cancelIdleCallback?.(a)}let r=globalThis.setTimeout(e,32);return()=>globalThis.clearTimeout(r)}function ve(e){let n=e.mediaKind==="demo-render",r=e.generated||e.mediaKind==="model"?"Live 3D \xB7 chart-derived seat-eye":n?e.coverage==="exact-seat"?"AI-generated exact-seat demo":e.coverage==="row-representative"?"AI-generated representative row demo":e.coverage==="section-representative"?"AI-generated representative section demo":"AI-generated illustrative venue demo":e.coverage==="exact-seat"?"Exact seat photo":e.coverage==="row-representative"?"Representative row view":e.coverage==="section-representative"?"Representative section view":e.coverage==="venue-representative"?"Representative venue view":"Venue photo",a=e.capturedAt&&/^\d{4}/.test(e.capturedAt)?e.capturedAt.slice(0,4):"";return[r,a?`${n?"created":"captured"} ${a}`:"",e.sourceLabel??""].filter(Boolean).join(" \xB7 ")}function be(e){if(e.generated||e.mediaKind==="model"||e.mediaKind==="demo-render")return!1;let n=e.sourceLabel?.trim()??"";return!/^(?:AI[- ]generated|generated\s+(?:demo|preview))/i.test(n)}var z=70,ge=35,fe=90;function Q(e){return Math.max(ge,Math.min(fe,e))}var Z=35;function ee(e,n,r){let a=(.5+e/360)*r;return n/2-a}function te(e,n=z){return e*(180/n)}function O(e,n,r){return(e-n)/2+E(r,n)}function E(e,n){let r=Z/180*n;return Math.max(-r,Math.min(r,e))}function xe(e,n,r={}){let a=r.fadeMs??400,l=n.initialBearingDeg??0,m=q(n,j()),v=new AbortController,b=()=>v.abort();r.signal?.addEventListener("abort",b,{once:!0});let i=document.createElement("div"),x=document.activeElement instanceof HTMLElement?document.activeElement:null;i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true"),i.setAttribute("aria-label",r.seatLabel?`View from ${r.seatLabel}`:"View from seat"),i.tabIndex=-1,Object.assign(i.style,{position:"absolute",inset:"0",zIndex:"10",opacity:"0",transition:`opacity ${a}ms ease`,background:"#05070c",overflow:"hidden",touchAction:"none"});let s=document.createElement("div");Object.assign(s.style,{position:"absolute",inset:"0",backgroundImage:`url("${m.initialUrl}")`,backgroundRepeat:"repeat-x",cursor:"grab"}),i.appendChild(s);let p=document.createElement("button");if(p.type="button",p.setAttribute("aria-label","Close"),p.textContent="\u2715",Object.assign(p.style,{position:"absolute",top:"12px",right:"12px",zIndex:"2",width:"44px",height:"44px",borderRadius:"999px",cursor:"pointer",border:"1px solid rgba(255,255,255,0.25)",background:"rgba(8,12,18,0.6)",color:"#e6edf3",fontSize:"15px",lineHeight:"1"}),i.appendChild(p),r.disclosure){let t=document.createElement("div");t.textContent=`${r.disclosurePrefix??"360\xB0 panorama"} \xB7 ${r.disclosure}`,Object.assign(t.style,{position:"absolute",top:"12px",left:"12px",zIndex:"2",maxWidth:"calc(100% - 88px)",padding:"8px 11px",borderRadius:"999px",overflow:"hidden",color:"#dce6f8",background:"rgba(8,12,18,0.68)",font:"600 11px/1.2 ui-sans-serif, system-ui, sans-serif",textOverflow:"ellipsis",whiteSpace:"nowrap",pointerEvents:"none",backdropFilter:"blur(6px)"}),i.appendChild(t)}let L=document.createElement("div");L.textContent="Drag to look \xB7 pinch or scroll to zoom \xB7 Esc to close",Object.assign(L.style,{position:"absolute",bottom:"12px",left:"0",right:"0",textAlign:"center",color:"rgba(230,237,243,0.7)",font:"12px ui-sans-serif, system-ui, sans-serif",pointerEvents:"none"}),i.appendChild(L),e.appendChild(i),p.focus({preventScroll:!0});let h=0,d=0,g=0,u=0,f=z,D=()=>{let t=i.clientHeight||1,o=i.clientWidth||1,M=y.naturalWidth||o*2,re=y.naturalHeight||t,oe=h>0?(o/2-g)/h:0,ie=d>0?u/d:0;d=te(t,f),h=d*(M/re),s.style.backgroundSize=`${h}px ${d}px`,$?g=o/2-oe*h:(g=ee(l,o,h),u=E((n.initialPitchDeg??0)/180*d,d),$=!0),u=E(ie*d,d),s.style.backgroundPosition=`${g}px ${O(t,d,u)}px`},$=!1,B=()=>{let t=i.clientHeight||1;u=E(u,d),s.style.backgroundPosition=`${g}px ${O(t,d,u)}px`},y=new Image;y.onload=D,y.src=m.initialUrl;let F=()=>{};m.upgradeUrl&&(F=J(()=>{(n.resolveUrl?n.resolveUrl(m.upgradeUrl):Promise.resolve(m.upgradeUrl)).then(o=>!o||v.signal.aborted?null:G(o,v.signal).then(M=>({full:M,url:o}))).then(o=>{o&&(T||v.signal.aborted||(y=o.full,s.style.backgroundImage=`url("${o.url}")`,D()))}).catch(()=>{})})),requestAnimationFrame(D);let C=!1,S=0,A=0,w=0,c=new Map,H=()=>{let[t,o]=[...c.values()];return t&&o?Math.hypot(t.x-o.x,t.y-o.y):0},I=t=>{let o=Q(t);o!==f&&(f=o,D())},R=t=>{c.set(t.pointerId,{x:t.clientX,y:t.clientY}),C=c.size===1,S=t.clientX,A=t.clientY,c.size===2&&(w=H()),s.style.cursor="grabbing";try{s.setPointerCapture?.(t.pointerId)}catch{}},X=t=>{if(c.has(t.pointerId)){if(c.set(t.pointerId,{x:t.clientX,y:t.clientY}),c.size>=2){let o=H();w>0&&o>0&&I(f*(w/o)),w=o,C=!1;return}C&&(g+=t.clientX-S,u+=t.clientY-A,S=t.clientX,A=t.clientY,B())}},k=t=>{c.delete(t.pointerId),w=c.size===2?H():0;let o=c.values().next().value;C=c.size===1,o?(S=o.x,A=o.y):s.style.cursor="grab";try{s.releasePointerCapture?.(t.pointerId)}catch{}},_=t=>{t.preventDefault(),I(f+t.deltaY*.04)};s.addEventListener("pointerdown",R),s.addEventListener("pointermove",X),s.addEventListener("pointerup",k),s.addEventListener("pointercancel",k),s.addEventListener("wheel",_,{passive:!1});let U=!1,T=!1,P=0,ne=()=>{s.removeEventListener("pointerdown",R),s.removeEventListener("pointermove",X),s.removeEventListener("pointerup",k),s.removeEventListener("pointercancel",k),s.removeEventListener("wheel",_),window.removeEventListener("keydown",Y)},K=()=>{F(),v.abort(),r.signal?.removeEventListener("abort",b),P&&(window.clearTimeout(P),P=0),ne(),i.parentNode&&i.parentNode.removeChild(i),x?.isConnected&&x.focus({preventScroll:!0})},W=()=>{if(U)return;U=!0,i.style.opacity="0";let t=()=>{P=0,!T&&(K(),r.onClose?.())};P=window.setTimeout(t,a)},Y=t=>{if(t.key==="Tab"){t.preventDefault(),p.focus({preventScroll:!0});return}if(t.key==="Escape"){t.stopPropagation(),W();return}if(t.key==="ArrowLeft"||t.key==="ArrowRight")g+=t.key==="ArrowLeft"?-32:32;else if(t.key==="ArrowUp"||t.key==="ArrowDown")u+=(t.key==="ArrowUp"?4:-4)*(d/180);else if(t.key==="+"||t.key==="="){I(f-5),t.preventDefault();return}else if(t.key==="-"){I(f+5),t.preventDefault();return}else return;t.preventDefault(),B()};return window.addEventListener("keydown",Y),p.addEventListener("click",W),requestAnimationFrame(()=>{i.style.opacity="1"}),{close:W,dispose(){U=!0,T=!0,K()}}}0&&(module.exports={MAX_PITCH_DEG,VFOV_DEG,bearingToOffsetPx,clampPanoramaFov,clampPitchPx,horizonOffsetPy,isAuthoredSeatView,mountPanorama,seatViewDisclosure,windowedBgHeight});
|
|
@@ -76,8 +76,6 @@ interface PanoramaHandle {
|
|
|
76
76
|
* horizon-centred, and let the user drag pitch within ±`MAX_PITCH_DEG`.
|
|
77
77
|
*/
|
|
78
78
|
declare const VFOV_DEG = 70;
|
|
79
|
-
declare const MIN_VFOV_DEG = 35;
|
|
80
|
-
declare const MAX_VFOV_DEG = 90;
|
|
81
79
|
/** Keep wheel, keyboard and pinch zoom inside a useful, non-disorienting range. */
|
|
82
80
|
declare function clampPanoramaFov(fovDeg: number): number;
|
|
83
81
|
/** Users may look this far up/down from the horizon; well inside the image so
|
|
@@ -117,4 +115,4 @@ interface PanoramaOptions {
|
|
|
117
115
|
}
|
|
118
116
|
declare function mountPanorama(container: HTMLElement, view: SeatView, opts?: PanoramaOptions): PanoramaHandle;
|
|
119
117
|
|
|
120
|
-
export { MAX_PITCH_DEG,
|
|
118
|
+
export { MAX_PITCH_DEG, type PanoramaHandle, type PanoramaOptions, type SeatView, VFOV_DEG, bearingToOffsetPx, clampPanoramaFov, clampPitchPx, horizonOffsetPy, isAuthoredSeatView, mountPanorama, seatViewDisclosure, windowedBgHeight };
|
|
@@ -76,8 +76,6 @@ interface PanoramaHandle {
|
|
|
76
76
|
* horizon-centred, and let the user drag pitch within ±`MAX_PITCH_DEG`.
|
|
77
77
|
*/
|
|
78
78
|
declare const VFOV_DEG = 70;
|
|
79
|
-
declare const MIN_VFOV_DEG = 35;
|
|
80
|
-
declare const MAX_VFOV_DEG = 90;
|
|
81
79
|
/** Keep wheel, keyboard and pinch zoom inside a useful, non-disorienting range. */
|
|
82
80
|
declare function clampPanoramaFov(fovDeg: number): number;
|
|
83
81
|
/** Users may look this far up/down from the horizon; well inside the image so
|
|
@@ -117,4 +115,4 @@ interface PanoramaOptions {
|
|
|
117
115
|
}
|
|
118
116
|
declare function mountPanorama(container: HTMLElement, view: SeatView, opts?: PanoramaOptions): PanoramaHandle;
|
|
119
117
|
|
|
120
|
-
export { MAX_PITCH_DEG,
|
|
118
|
+
export { MAX_PITCH_DEG, type PanoramaHandle, type PanoramaOptions, type SeatView, VFOV_DEG, bearingToOffsetPx, clampPanoramaFov, clampPitchPx, horizonOffsetPy, isAuthoredSeatView, mountPanorama, seatViewDisclosure, windowedBgHeight };
|
|
@@ -1,30 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
MAX_PITCH_DEG,
|
|
3
|
-
MAX_VFOV_DEG,
|
|
4
|
-
MIN_VFOV_DEG,
|
|
5
|
-
VFOV_DEG,
|
|
6
|
-
bearingToOffsetPx,
|
|
7
|
-
clampPanoramaFov,
|
|
8
|
-
clampPitchPx,
|
|
9
|
-
horizonOffsetPy,
|
|
10
|
-
isAuthoredSeatView,
|
|
11
|
-
mountPanorama,
|
|
12
|
-
seatViewDisclosure,
|
|
13
|
-
windowedBgHeight
|
|
14
|
-
} from "../../chunk-BE3F7CT3.js";
|
|
15
|
-
import "../../chunk-AZODENEL.js";
|
|
16
|
-
export {
|
|
17
|
-
MAX_PITCH_DEG,
|
|
18
|
-
MAX_VFOV_DEG,
|
|
19
|
-
MIN_VFOV_DEG,
|
|
20
|
-
VFOV_DEG,
|
|
21
|
-
bearingToOffsetPx,
|
|
22
|
-
clampPanoramaFov,
|
|
23
|
-
clampPitchPx,
|
|
24
|
-
horizonOffsetPy,
|
|
25
|
-
isAuthoredSeatView,
|
|
26
|
-
mountPanorama,
|
|
27
|
-
seatViewDisclosure,
|
|
28
|
-
windowedBgHeight
|
|
29
|
-
};
|
|
30
|
-
//# sourceMappingURL=panorama.js.map
|
|
1
|
+
import{a,b,c,d,e,f,g,h,i,j}from"../../chunk-XPPT3OGQ.js";import"../../chunk-IJI4WU57.js";export{e as MAX_PITCH_DEG,c as VFOV_DEG,f as bearingToOffsetPx,d as clampPanoramaFov,i as clampPitchPx,h as horizonOffsetPy,b as isAuthoredSeatView,j as mountPanorama,a as seatViewDisclosure,g as windowedBgHeight};
|