@frybynite/image-cloud 0.1.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 +21 -0
- package/README.md +345 -0
- package/dist/image-cloud-auto-init.js +3106 -0
- package/dist/image-cloud-auto-init.js.map +1 -0
- package/dist/image-cloud.js +3078 -0
- package/dist/image-cloud.js.map +1 -0
- package/dist/image-cloud.umd.js +5 -0
- package/dist/image-cloud.umd.js.map +1 -0
- package/dist/index.d.ts +1514 -0
- package/dist/style.css +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,3078 @@
|
|
|
1
|
+
const St = Object.freeze({
|
|
2
|
+
none: "none",
|
|
3
|
+
sm: "0 2px 4px rgba(0,0,0,0.1)",
|
|
4
|
+
md: "0 4px 16px rgba(0,0,0,0.4)",
|
|
5
|
+
lg: "0 8px 32px rgba(0,0,0,0.5)",
|
|
6
|
+
glow: "0 0 30px rgba(255,255,255,0.6)"
|
|
7
|
+
}), zt = Object.freeze({
|
|
8
|
+
energetic: Object.freeze({ overshoot: 0.25, bounces: 2, decayRatio: 0.5 }),
|
|
9
|
+
playful: Object.freeze({ overshoot: 0.15, bounces: 1, decayRatio: 0.5 }),
|
|
10
|
+
subtle: Object.freeze({ overshoot: 0.08, bounces: 1, decayRatio: 0.5 })
|
|
11
|
+
}), Tt = Object.freeze({
|
|
12
|
+
gentle: Object.freeze({ stiffness: 150, damping: 30, mass: 1, oscillations: 2 }),
|
|
13
|
+
bouncy: Object.freeze({ stiffness: 300, damping: 15, mass: 1, oscillations: 4 }),
|
|
14
|
+
wobbly: Object.freeze({ stiffness: 180, damping: 12, mass: 1.5, oscillations: 5 }),
|
|
15
|
+
snappy: Object.freeze({ stiffness: 400, damping: 25, mass: 0.8, oscillations: 2 })
|
|
16
|
+
}), Mt = Object.freeze({
|
|
17
|
+
gentle: Object.freeze({ amplitude: 30, frequency: 1.5, decay: !0, decayRate: 0.9, phase: 0 }),
|
|
18
|
+
playful: Object.freeze({ amplitude: 50, frequency: 2.5, decay: !0, decayRate: 0.7, phase: 0 }),
|
|
19
|
+
serpentine: Object.freeze({ amplitude: 60, frequency: 3, decay: !1, decayRate: 1, phase: 0 }),
|
|
20
|
+
flutter: Object.freeze({ amplitude: 20, frequency: 4, decay: !0, decayRate: 0.5, phase: 0 })
|
|
21
|
+
}), It = Object.freeze({
|
|
22
|
+
type: "linear"
|
|
23
|
+
}), Rt = Object.freeze({
|
|
24
|
+
mode: "none"
|
|
25
|
+
}), Et = Object.freeze({
|
|
26
|
+
mode: "none"
|
|
27
|
+
}), Lt = Object.freeze({
|
|
28
|
+
default: Object.freeze({
|
|
29
|
+
border: Object.freeze({
|
|
30
|
+
width: 0,
|
|
31
|
+
color: "#000000",
|
|
32
|
+
radius: 0,
|
|
33
|
+
style: "solid"
|
|
34
|
+
}),
|
|
35
|
+
shadow: "none",
|
|
36
|
+
filter: Object.freeze({}),
|
|
37
|
+
opacity: 1,
|
|
38
|
+
cursor: "pointer",
|
|
39
|
+
outline: Object.freeze({
|
|
40
|
+
width: 0,
|
|
41
|
+
color: "#000000",
|
|
42
|
+
style: "solid",
|
|
43
|
+
offset: 0
|
|
44
|
+
})
|
|
45
|
+
}),
|
|
46
|
+
hover: Object.freeze({
|
|
47
|
+
shadow: "none"
|
|
48
|
+
}),
|
|
49
|
+
focused: Object.freeze({
|
|
50
|
+
shadow: "none"
|
|
51
|
+
})
|
|
52
|
+
}), Ht = Object.freeze({
|
|
53
|
+
rows: 1,
|
|
54
|
+
amplitude: 100,
|
|
55
|
+
frequency: 2,
|
|
56
|
+
phaseShift: 0,
|
|
57
|
+
synchronization: "offset"
|
|
58
|
+
// Note: Image rotation along wave is now controlled via image.rotation.mode = 'tangent'
|
|
59
|
+
}), _t = Object.freeze({
|
|
60
|
+
// baseHeight not set - layouts will auto-calculate based on targetCoverage
|
|
61
|
+
variance: Object.freeze({
|
|
62
|
+
min: 1,
|
|
63
|
+
// No variance by default
|
|
64
|
+
max: 1
|
|
65
|
+
}),
|
|
66
|
+
scaleDecay: 0
|
|
67
|
+
// No decay by default
|
|
68
|
+
}), jt = Object.freeze({
|
|
69
|
+
mode: "none",
|
|
70
|
+
range: Object.freeze({
|
|
71
|
+
min: -15,
|
|
72
|
+
max: 15
|
|
73
|
+
})
|
|
74
|
+
}), $t = Object.freeze({
|
|
75
|
+
sizing: _t,
|
|
76
|
+
rotation: jt
|
|
77
|
+
}), b = Object.freeze({
|
|
78
|
+
// Unified loader configuration
|
|
79
|
+
loader: Object.freeze({
|
|
80
|
+
type: "googleDrive",
|
|
81
|
+
googleDrive: Object.freeze({
|
|
82
|
+
apiKey: "",
|
|
83
|
+
// Must be provided by user
|
|
84
|
+
sources: [],
|
|
85
|
+
// Must be provided by user
|
|
86
|
+
apiEndpoint: "https://www.googleapis.com/drive/v3/files",
|
|
87
|
+
allowedExtensions: ["jpg", "jpeg", "png", "gif", "webp", "bmp"],
|
|
88
|
+
debugLogging: !1
|
|
89
|
+
}),
|
|
90
|
+
static: Object.freeze({
|
|
91
|
+
sources: [],
|
|
92
|
+
// Must be provided by user
|
|
93
|
+
validateUrls: !0,
|
|
94
|
+
validationTimeout: 5e3,
|
|
95
|
+
validationMethod: "head",
|
|
96
|
+
failOnAllMissing: !0,
|
|
97
|
+
allowedExtensions: ["jpg", "jpeg", "png", "gif", "webp", "bmp"],
|
|
98
|
+
debugLogging: !1
|
|
99
|
+
})
|
|
100
|
+
}),
|
|
101
|
+
// Image sizing and rotation configuration
|
|
102
|
+
image: $t,
|
|
103
|
+
// Pattern-based layout configuration
|
|
104
|
+
layout: Object.freeze({
|
|
105
|
+
algorithm: "radial",
|
|
106
|
+
sizing: Object.freeze({
|
|
107
|
+
base: 200,
|
|
108
|
+
// pixels - fallback when image.sizing.baseHeight not set
|
|
109
|
+
responsive: [
|
|
110
|
+
{ minWidth: 1200, height: 225 },
|
|
111
|
+
// Large screens
|
|
112
|
+
{ minWidth: 768, height: 180 },
|
|
113
|
+
// Tablet / Small desktop
|
|
114
|
+
{ minWidth: 0, height: 100 }
|
|
115
|
+
// Mobile / Default
|
|
116
|
+
],
|
|
117
|
+
adaptive: Object.freeze({
|
|
118
|
+
enabled: !0,
|
|
119
|
+
// Enable adaptive sizing by default
|
|
120
|
+
minSize: 50,
|
|
121
|
+
// Minimum 50px image height
|
|
122
|
+
maxSize: 400
|
|
123
|
+
// Maximum 400px image height
|
|
124
|
+
})
|
|
125
|
+
}),
|
|
126
|
+
targetCoverage: 0.6,
|
|
127
|
+
// Target 60% of container area
|
|
128
|
+
densityFactor: 1,
|
|
129
|
+
// Default density
|
|
130
|
+
spacing: Object.freeze({
|
|
131
|
+
padding: 50,
|
|
132
|
+
// padding from viewport edges
|
|
133
|
+
minGap: 20
|
|
134
|
+
// minimum spacing between images
|
|
135
|
+
}),
|
|
136
|
+
debugRadials: !1,
|
|
137
|
+
debugCenters: !1
|
|
138
|
+
}),
|
|
139
|
+
// Pattern-based animation configuration
|
|
140
|
+
animation: Object.freeze({
|
|
141
|
+
duration: 600,
|
|
142
|
+
// milliseconds
|
|
143
|
+
easing: Object.freeze({
|
|
144
|
+
default: "cubic-bezier(0.4, 0.0, 0.2, 1)",
|
|
145
|
+
// smooth easing
|
|
146
|
+
bounce: "cubic-bezier(0.68, -0.55, 0.265, 1.55)",
|
|
147
|
+
// bounce effect
|
|
148
|
+
focus: "cubic-bezier(0.4, 0.0, 0.2, 1)"
|
|
149
|
+
// focus/zoom easing
|
|
150
|
+
}),
|
|
151
|
+
queue: Object.freeze({
|
|
152
|
+
enabled: !0,
|
|
153
|
+
// When false, all images display simultaneously
|
|
154
|
+
interval: 150,
|
|
155
|
+
// ms between processing queue items (when enabled)
|
|
156
|
+
maxConcurrent: void 0
|
|
157
|
+
// STUB: Not implemented yet
|
|
158
|
+
}),
|
|
159
|
+
performance: Object.freeze({
|
|
160
|
+
useGPU: void 0,
|
|
161
|
+
// STUB: Not implemented yet
|
|
162
|
+
reduceMotion: void 0
|
|
163
|
+
// STUB: Not implemented yet
|
|
164
|
+
}),
|
|
165
|
+
entry: Object.freeze({
|
|
166
|
+
start: Object.freeze({
|
|
167
|
+
position: "nearest-edge",
|
|
168
|
+
// Default to nearest edge (current behavior)
|
|
169
|
+
offset: 100,
|
|
170
|
+
// pixels beyond edge
|
|
171
|
+
circular: Object.freeze({
|
|
172
|
+
radius: "120%",
|
|
173
|
+
// 120% of container diagonal
|
|
174
|
+
distribution: "even"
|
|
175
|
+
})
|
|
176
|
+
}),
|
|
177
|
+
timing: Object.freeze({
|
|
178
|
+
duration: 600,
|
|
179
|
+
// ms
|
|
180
|
+
stagger: 150
|
|
181
|
+
// ms between images
|
|
182
|
+
}),
|
|
183
|
+
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
|
|
184
|
+
// smooth deceleration
|
|
185
|
+
path: It,
|
|
186
|
+
rotation: Rt,
|
|
187
|
+
scale: Et
|
|
188
|
+
})
|
|
189
|
+
}),
|
|
190
|
+
// Pattern-based interaction configuration
|
|
191
|
+
interaction: Object.freeze({
|
|
192
|
+
focus: Object.freeze({
|
|
193
|
+
scalePercent: 0.8,
|
|
194
|
+
// 80% of container height
|
|
195
|
+
zIndex: 1e3,
|
|
196
|
+
animationDuration: void 0
|
|
197
|
+
// Use default animation duration
|
|
198
|
+
}),
|
|
199
|
+
navigation: Object.freeze({
|
|
200
|
+
keyboard: void 0,
|
|
201
|
+
// STUB: Not implemented yet
|
|
202
|
+
swipe: void 0,
|
|
203
|
+
// STUB: Not implemented yet
|
|
204
|
+
mouseWheel: void 0
|
|
205
|
+
// STUB: Not implemented yet
|
|
206
|
+
}),
|
|
207
|
+
gestures: Object.freeze({
|
|
208
|
+
pinchToZoom: void 0,
|
|
209
|
+
// STUB: Not implemented yet
|
|
210
|
+
doubleTapToFocus: void 0
|
|
211
|
+
// STUB: Not implemented yet
|
|
212
|
+
})
|
|
213
|
+
}),
|
|
214
|
+
// Pattern-based rendering configuration
|
|
215
|
+
rendering: Object.freeze({
|
|
216
|
+
responsive: Object.freeze({
|
|
217
|
+
breakpoints: Object.freeze({
|
|
218
|
+
mobile: 768,
|
|
219
|
+
tablet: void 0,
|
|
220
|
+
// STUB: Not implemented yet
|
|
221
|
+
desktop: void 0
|
|
222
|
+
// STUB: Not implemented yet
|
|
223
|
+
}),
|
|
224
|
+
mobileDetection: () => typeof window > "u" ? !1 : window.innerWidth <= 768
|
|
225
|
+
}),
|
|
226
|
+
ui: Object.freeze({
|
|
227
|
+
showLoadingSpinner: !1,
|
|
228
|
+
showImageCounter: void 0,
|
|
229
|
+
// STUB: Not implemented yet
|
|
230
|
+
showThumbnails: void 0,
|
|
231
|
+
// STUB: Not implemented yet
|
|
232
|
+
theme: void 0
|
|
233
|
+
// STUB: Not implemented yet
|
|
234
|
+
}),
|
|
235
|
+
performance: Object.freeze({
|
|
236
|
+
lazyLoad: void 0,
|
|
237
|
+
// STUB: Not implemented yet
|
|
238
|
+
preloadCount: void 0,
|
|
239
|
+
// STUB: Not implemented yet
|
|
240
|
+
imageQuality: void 0
|
|
241
|
+
// STUB: Not implemented yet
|
|
242
|
+
})
|
|
243
|
+
}),
|
|
244
|
+
// Image styling
|
|
245
|
+
styling: Lt,
|
|
246
|
+
// Debug mode
|
|
247
|
+
debug: !1
|
|
248
|
+
});
|
|
249
|
+
function rt(n, t) {
|
|
250
|
+
if (!n) return t || {};
|
|
251
|
+
if (!t) return { ...n };
|
|
252
|
+
const e = { ...n };
|
|
253
|
+
return t.border !== void 0 && (e.border = { ...n.border, ...t.border }), t.borderTop !== void 0 && (e.borderTop = { ...n.borderTop, ...t.borderTop }), t.borderRight !== void 0 && (e.borderRight = { ...n.borderRight, ...t.borderRight }), t.borderBottom !== void 0 && (e.borderBottom = { ...n.borderBottom, ...t.borderBottom }), t.borderLeft !== void 0 && (e.borderLeft = { ...n.borderLeft, ...t.borderLeft }), t.filter !== void 0 && (e.filter = { ...n.filter, ...t.filter }), t.outline !== void 0 && (e.outline = { ...n.outline, ...t.outline }), t.shadow !== void 0 && (e.shadow = t.shadow), t.opacity !== void 0 && (e.opacity = t.opacity), t.cursor !== void 0 && (e.cursor = t.cursor), t.className !== void 0 && (e.className = t.className), t.objectFit !== void 0 && (e.objectFit = t.objectFit), t.aspectRatio !== void 0 && (e.aspectRatio = t.aspectRatio), t.borderRadiusTopLeft !== void 0 && (e.borderRadiusTopLeft = t.borderRadiusTopLeft), t.borderRadiusTopRight !== void 0 && (e.borderRadiusTopRight = t.borderRadiusTopRight), t.borderRadiusBottomRight !== void 0 && (e.borderRadiusBottomRight = t.borderRadiusBottomRight), t.borderRadiusBottomLeft !== void 0 && (e.borderRadiusBottomLeft = t.borderRadiusBottomLeft), e;
|
|
254
|
+
}
|
|
255
|
+
function Gt(n, t) {
|
|
256
|
+
if (!t) return { ...n };
|
|
257
|
+
const e = rt(n.default, t.default), i = rt(
|
|
258
|
+
rt(e, n.hover),
|
|
259
|
+
t.hover
|
|
260
|
+
), o = rt(
|
|
261
|
+
rt(e, n.focused),
|
|
262
|
+
t.focused
|
|
263
|
+
);
|
|
264
|
+
return {
|
|
265
|
+
default: e,
|
|
266
|
+
hover: i,
|
|
267
|
+
focused: o
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function kt(n, t) {
|
|
271
|
+
var i, o, s, c, a, h, l, g;
|
|
272
|
+
if (!t) return { ...n };
|
|
273
|
+
const e = { ...n };
|
|
274
|
+
if (t.sizing !== void 0 && (e.sizing = {
|
|
275
|
+
...n.sizing,
|
|
276
|
+
...t.sizing
|
|
277
|
+
}, t.sizing.variance)) {
|
|
278
|
+
const r = t.sizing.variance, d = r.min !== void 0 && r.min > 0.1 && r.min < 1 ? r.min : ((o = (i = n.sizing) == null ? void 0 : i.variance) == null ? void 0 : o.min) ?? 1, f = r.max !== void 0 && r.max > 1 && r.max < 2 ? r.max : ((c = (s = n.sizing) == null ? void 0 : s.variance) == null ? void 0 : c.max) ?? 1;
|
|
279
|
+
e.sizing.variance = { min: d, max: f };
|
|
280
|
+
}
|
|
281
|
+
if (t.rotation !== void 0 && (e.rotation = {
|
|
282
|
+
...n.rotation,
|
|
283
|
+
...t.rotation
|
|
284
|
+
}, t.rotation.range)) {
|
|
285
|
+
const r = t.rotation.range, d = r.min !== void 0 && r.min >= -180 && r.min <= 0 ? r.min : ((h = (a = n.rotation) == null ? void 0 : a.range) == null ? void 0 : h.min) ?? -15, f = r.max !== void 0 && r.max >= 0 && r.max <= 180 ? r.max : ((g = (l = n.rotation) == null ? void 0 : l.range) == null ? void 0 : g.max) ?? 15;
|
|
286
|
+
e.rotation.range = { min: d, max: f };
|
|
287
|
+
}
|
|
288
|
+
return e;
|
|
289
|
+
}
|
|
290
|
+
function Wt(n) {
|
|
291
|
+
var e;
|
|
292
|
+
const t = (e = n.layout) == null ? void 0 : e.rotation;
|
|
293
|
+
if (t && "enabled" in t)
|
|
294
|
+
return {
|
|
295
|
+
rotation: {
|
|
296
|
+
mode: t.enabled ? "random" : "none",
|
|
297
|
+
range: t.range
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function Nt(n) {
|
|
302
|
+
var e, i;
|
|
303
|
+
const t = (i = (e = n.layout) == null ? void 0 : e.sizing) == null ? void 0 : i.variance;
|
|
304
|
+
if (t)
|
|
305
|
+
return {
|
|
306
|
+
sizing: {
|
|
307
|
+
variance: t
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function qt(n = {}) {
|
|
312
|
+
var s;
|
|
313
|
+
const t = Wt(n), e = Nt(n);
|
|
314
|
+
let i = n.image;
|
|
315
|
+
(t || e) && (i = {
|
|
316
|
+
...e || {},
|
|
317
|
+
...t || {},
|
|
318
|
+
...i
|
|
319
|
+
}, i.rotation && (t != null && t.rotation) && ((s = n.image) != null && s.rotation) && (i.rotation = {
|
|
320
|
+
...t.rotation,
|
|
321
|
+
...n.image.rotation
|
|
322
|
+
}));
|
|
323
|
+
const o = {
|
|
324
|
+
loader: { ...b.loader },
|
|
325
|
+
image: kt($t, i),
|
|
326
|
+
layout: { ...b.layout },
|
|
327
|
+
animation: { ...b.animation },
|
|
328
|
+
interaction: { ...b.interaction },
|
|
329
|
+
rendering: { ...b.rendering },
|
|
330
|
+
styling: Gt(Lt, n.styling),
|
|
331
|
+
debug: b.debug
|
|
332
|
+
};
|
|
333
|
+
return n.loader && (o.loader = {
|
|
334
|
+
...b.loader,
|
|
335
|
+
...n.loader
|
|
336
|
+
}, n.loader.googleDrive && (o.loader.googleDrive = {
|
|
337
|
+
...b.loader.googleDrive,
|
|
338
|
+
...n.loader.googleDrive,
|
|
339
|
+
sources: n.loader.googleDrive.sources || b.loader.googleDrive.sources,
|
|
340
|
+
allowedExtensions: n.loader.googleDrive.allowedExtensions || b.loader.googleDrive.allowedExtensions
|
|
341
|
+
}), n.loader.static && (o.loader.static = {
|
|
342
|
+
...b.loader.static,
|
|
343
|
+
...n.loader.static,
|
|
344
|
+
sources: n.loader.static.sources || b.loader.static.sources,
|
|
345
|
+
allowedExtensions: n.loader.static.allowedExtensions || b.loader.static.allowedExtensions
|
|
346
|
+
})), n.layout && (o.layout = {
|
|
347
|
+
...b.layout,
|
|
348
|
+
...n.layout
|
|
349
|
+
}, n.layout.sizing && (o.layout.sizing = {
|
|
350
|
+
...b.layout.sizing,
|
|
351
|
+
...n.layout.sizing,
|
|
352
|
+
responsive: n.layout.sizing.responsive || b.layout.sizing.responsive,
|
|
353
|
+
adaptive: n.layout.sizing.adaptive ? { ...b.layout.sizing.adaptive, ...n.layout.sizing.adaptive } : b.layout.sizing.adaptive
|
|
354
|
+
}), n.layout.spacing && (o.layout.spacing = {
|
|
355
|
+
...b.layout.spacing,
|
|
356
|
+
...n.layout.spacing
|
|
357
|
+
})), n.animation && (o.animation = {
|
|
358
|
+
...b.animation,
|
|
359
|
+
...n.animation
|
|
360
|
+
}, n.animation.easing && (o.animation.easing = {
|
|
361
|
+
...b.animation.easing,
|
|
362
|
+
...n.animation.easing
|
|
363
|
+
}), n.animation.queue && (o.animation.queue = {
|
|
364
|
+
...b.animation.queue,
|
|
365
|
+
...n.animation.queue
|
|
366
|
+
}), n.animation.performance && (o.animation.performance = {
|
|
367
|
+
...b.animation.performance,
|
|
368
|
+
...n.animation.performance
|
|
369
|
+
}), n.animation.entry && (o.animation.entry = {
|
|
370
|
+
...b.animation.entry,
|
|
371
|
+
...n.animation.entry,
|
|
372
|
+
start: n.animation.entry.start ? {
|
|
373
|
+
...b.animation.entry.start,
|
|
374
|
+
...n.animation.entry.start,
|
|
375
|
+
circular: n.animation.entry.start.circular ? { ...b.animation.entry.start.circular, ...n.animation.entry.start.circular } : b.animation.entry.start.circular
|
|
376
|
+
} : b.animation.entry.start,
|
|
377
|
+
timing: n.animation.entry.timing ? { ...b.animation.entry.timing, ...n.animation.entry.timing } : b.animation.entry.timing,
|
|
378
|
+
path: n.animation.entry.path ? { ...It, ...n.animation.entry.path } : b.animation.entry.path,
|
|
379
|
+
rotation: n.animation.entry.rotation ? { ...Rt, ...n.animation.entry.rotation } : b.animation.entry.rotation,
|
|
380
|
+
scale: n.animation.entry.scale ? { ...Et, ...n.animation.entry.scale } : b.animation.entry.scale
|
|
381
|
+
})), n.interaction && (o.interaction = {
|
|
382
|
+
...b.interaction,
|
|
383
|
+
...n.interaction
|
|
384
|
+
}, n.interaction.focus && (o.interaction.focus = {
|
|
385
|
+
...b.interaction.focus,
|
|
386
|
+
...n.interaction.focus
|
|
387
|
+
}), n.interaction.navigation && (o.interaction.navigation = {
|
|
388
|
+
...b.interaction.navigation,
|
|
389
|
+
...n.interaction.navigation
|
|
390
|
+
}), n.interaction.gestures && (o.interaction.gestures = {
|
|
391
|
+
...b.interaction.gestures,
|
|
392
|
+
...n.interaction.gestures
|
|
393
|
+
})), n.rendering && (o.rendering = {
|
|
394
|
+
...b.rendering,
|
|
395
|
+
...n.rendering
|
|
396
|
+
}, n.rendering.responsive && (o.rendering.responsive = {
|
|
397
|
+
...b.rendering.responsive,
|
|
398
|
+
...n.rendering.responsive,
|
|
399
|
+
breakpoints: n.rendering.responsive.breakpoints ? { ...b.rendering.responsive.breakpoints, ...n.rendering.responsive.breakpoints } : b.rendering.responsive.breakpoints,
|
|
400
|
+
mobileDetection: n.rendering.responsive.mobileDetection ? n.rendering.responsive.mobileDetection : b.rendering.responsive.mobileDetection
|
|
401
|
+
}), n.rendering.ui && (o.rendering.ui = {
|
|
402
|
+
...b.rendering.ui,
|
|
403
|
+
...n.rendering.ui
|
|
404
|
+
}), n.rendering.performance && (o.rendering.performance = {
|
|
405
|
+
...b.rendering.performance,
|
|
406
|
+
...n.rendering.performance
|
|
407
|
+
})), n.debug !== void 0 && (o.debug = n.debug), o;
|
|
408
|
+
}
|
|
409
|
+
function Yt(n, t) {
|
|
410
|
+
return { ...n ? zt[n] : zt.playful, ...t };
|
|
411
|
+
}
|
|
412
|
+
function Xt(n, t) {
|
|
413
|
+
return { ...n ? Tt[n] : Tt.gentle, ...t };
|
|
414
|
+
}
|
|
415
|
+
function Bt(n, t) {
|
|
416
|
+
return { ...n ? Mt[n] : Mt.gentle, ...t };
|
|
417
|
+
}
|
|
418
|
+
class Vt {
|
|
419
|
+
constructor(t) {
|
|
420
|
+
this.activeAnimations = /* @__PURE__ */ new Map(), this.animationIdCounter = 0, this.config = t;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Build transform string from transform params
|
|
424
|
+
* Always starts with centering transform to match image positioning system
|
|
425
|
+
*/
|
|
426
|
+
buildTransformString(t) {
|
|
427
|
+
const e = ["translate(-50%, -50%)"];
|
|
428
|
+
if (t.x !== void 0 || t.y !== void 0) {
|
|
429
|
+
const i = t.x ?? 0, o = t.y ?? 0;
|
|
430
|
+
e.push(`translate(${i}px, ${o}px)`);
|
|
431
|
+
}
|
|
432
|
+
return t.rotation !== void 0 && e.push(`rotate(${t.rotation}deg)`), t.scale !== void 0 && e.push(`scale(${t.scale})`), e.join(" ");
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Start a cancellable transform animation using Web Animations API
|
|
436
|
+
* @param element - The element to animate
|
|
437
|
+
* @param from - Starting transform state
|
|
438
|
+
* @param to - Ending transform state
|
|
439
|
+
* @param duration - Animation duration in ms (optional)
|
|
440
|
+
* @param easing - CSS easing function (optional)
|
|
441
|
+
* @returns AnimationHandle that can be used to cancel or query the animation
|
|
442
|
+
*/
|
|
443
|
+
animateTransformCancellable(t, e, i, o = null, s = null) {
|
|
444
|
+
this.cancelAllAnimations(t);
|
|
445
|
+
const c = o ?? this.config.duration, a = s ?? this.config.easing.default, h = this.buildTransformString(e), l = this.buildTransformString(i);
|
|
446
|
+
t.style.transition = "none";
|
|
447
|
+
const g = t.animate(
|
|
448
|
+
[
|
|
449
|
+
{ transform: h },
|
|
450
|
+
{ transform: l }
|
|
451
|
+
],
|
|
452
|
+
{
|
|
453
|
+
duration: c,
|
|
454
|
+
easing: a,
|
|
455
|
+
fill: "forwards"
|
|
456
|
+
// Keep final state after animation
|
|
457
|
+
}
|
|
458
|
+
), r = {
|
|
459
|
+
id: `anim-${++this.animationIdCounter}`,
|
|
460
|
+
element: t,
|
|
461
|
+
animation: g,
|
|
462
|
+
fromState: e,
|
|
463
|
+
toState: i,
|
|
464
|
+
startTime: performance.now(),
|
|
465
|
+
duration: c
|
|
466
|
+
};
|
|
467
|
+
return this.activeAnimations.set(t, r), g.finished.then(() => {
|
|
468
|
+
t.style.transform = l, this.activeAnimations.delete(t);
|
|
469
|
+
}).catch(() => {
|
|
470
|
+
this.activeAnimations.delete(t);
|
|
471
|
+
}), r;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Cancel an active animation
|
|
475
|
+
* @param handle - The animation handle to cancel
|
|
476
|
+
* @param commitStyle - If true, keeps current position; if false, no style change
|
|
477
|
+
* @returns Snapshot of where the animation was when cancelled
|
|
478
|
+
*/
|
|
479
|
+
cancelAnimation(t, e = !0) {
|
|
480
|
+
const i = this.getCurrentTransform(t.element);
|
|
481
|
+
if (t.animation.cancel(), e) {
|
|
482
|
+
const o = this.buildTransformString({
|
|
483
|
+
x: i.x,
|
|
484
|
+
y: i.y,
|
|
485
|
+
rotation: i.rotation,
|
|
486
|
+
scale: i.scale
|
|
487
|
+
});
|
|
488
|
+
t.element.style.transform = o;
|
|
489
|
+
}
|
|
490
|
+
return this.activeAnimations.delete(t.element), i;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Cancel all animations on an element
|
|
494
|
+
* Uses Web Animations API to find and cancel ALL animations, not just tracked ones
|
|
495
|
+
* @param element - The element to cancel animations for
|
|
496
|
+
*/
|
|
497
|
+
cancelAllAnimations(t) {
|
|
498
|
+
const e = this.activeAnimations.get(t);
|
|
499
|
+
e && this.cancelAnimation(e, !1);
|
|
500
|
+
const i = t.getAnimations();
|
|
501
|
+
for (const o of i)
|
|
502
|
+
o.cancel();
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Get current transform state of an element (works mid-animation)
|
|
506
|
+
* Uses DOMMatrix to parse the computed transform
|
|
507
|
+
* @param element - The element to query
|
|
508
|
+
* @returns Current transform snapshot
|
|
509
|
+
*/
|
|
510
|
+
getCurrentTransform(t) {
|
|
511
|
+
const i = getComputedStyle(t).transform;
|
|
512
|
+
if (i === "none" || !i)
|
|
513
|
+
return { x: 0, y: 0, rotation: 0, scale: 1 };
|
|
514
|
+
const o = new DOMMatrix(i), s = Math.sqrt(o.a * o.a + o.b * o.b), c = Math.atan2(o.b, o.a) * (180 / Math.PI), a = o.e, h = o.f;
|
|
515
|
+
return { x: a, y: h, rotation: c, scale: s };
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Check if an element has an active animation
|
|
519
|
+
* @param element - The element to check
|
|
520
|
+
* @returns True if animation is in progress
|
|
521
|
+
*/
|
|
522
|
+
hasActiveAnimation(t) {
|
|
523
|
+
return this.activeAnimations.has(t);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Get animation handle for an element if it exists
|
|
527
|
+
* @param element - The element to query
|
|
528
|
+
* @returns AnimationHandle or undefined
|
|
529
|
+
*/
|
|
530
|
+
getAnimationHandle(t) {
|
|
531
|
+
return this.activeAnimations.get(t);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Animate element transform with smooth easing (CSS transitions - legacy method)
|
|
535
|
+
* @param element - The element to animate
|
|
536
|
+
* @param properties - Transform properties {x, y, rotation, scale}
|
|
537
|
+
* @param duration - Animation duration in ms (optional)
|
|
538
|
+
* @param easing - CSS easing function (optional)
|
|
539
|
+
* @returns Promise that resolves when animation completes
|
|
540
|
+
*/
|
|
541
|
+
animateTransform(t, e, i = null, o = null) {
|
|
542
|
+
return new Promise((s) => {
|
|
543
|
+
const c = i ?? this.config.duration, a = o ?? this.config.easing.default;
|
|
544
|
+
t.style.transition = `transform ${c}ms ${a}, box-shadow ${c}ms ${a}`, t.style.transform = this.buildTransformString(e), setTimeout(() => {
|
|
545
|
+
s();
|
|
546
|
+
}, c);
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Reset element to its original transform
|
|
551
|
+
* @param element - The element to reset
|
|
552
|
+
* @param originalState - Original transform state {x, y, rotation, scale}
|
|
553
|
+
* @returns Promise that resolves when animation completes
|
|
554
|
+
*/
|
|
555
|
+
resetTransform(t, e) {
|
|
556
|
+
return this.animateTransform(t, e);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Remove transition styles from element
|
|
560
|
+
* @param element - The element to clear
|
|
561
|
+
*/
|
|
562
|
+
clearTransition(t) {
|
|
563
|
+
t.style.transition = "";
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Utility: Wait for a specified duration
|
|
567
|
+
* @param ms - Milliseconds to wait
|
|
568
|
+
* @returns Promise that resolves after the specified duration
|
|
569
|
+
*/
|
|
570
|
+
wait(t) {
|
|
571
|
+
return new Promise((e) => setTimeout(e, t));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function st(n, t, e) {
|
|
575
|
+
return n + (t - n) * e;
|
|
576
|
+
}
|
|
577
|
+
function Jt(n, t, e, i) {
|
|
578
|
+
const { overshoot: o, bounces: s, decayRatio: c } = i, a = e.x - t.x, h = e.y - t.y, l = Zt(s, c);
|
|
579
|
+
let g = 0, r = 0, d = 1, f = o, y = !1;
|
|
580
|
+
for (let u = 0; u < l.length; u++)
|
|
581
|
+
if (n <= l[u].time) {
|
|
582
|
+
r = u === 0 ? 0 : l[u - 1].time, d = l[u].time, f = l[u].overshoot, y = l[u].isOvershoot;
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
const p = (n - r) / (d - r);
|
|
586
|
+
if (y)
|
|
587
|
+
g = 1 + f * ut(p);
|
|
588
|
+
else if (r === 0)
|
|
589
|
+
g = ut(p);
|
|
590
|
+
else {
|
|
591
|
+
const u = l.find(
|
|
592
|
+
(w, v) => w.time > r && v > 0 && l[v - 1].isOvershoot
|
|
593
|
+
), m = 1 + ((u == null ? void 0 : u.overshoot) || f);
|
|
594
|
+
g = st(m, 1, ut(p));
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
x: t.x + a * g,
|
|
598
|
+
y: t.y + h * g
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function Zt(n, t) {
|
|
602
|
+
const e = [];
|
|
603
|
+
let i = 0.6;
|
|
604
|
+
e.push({ time: i, overshoot: 0, isOvershoot: !1 });
|
|
605
|
+
let o = 0.15;
|
|
606
|
+
const c = 0.4 / (n * 2);
|
|
607
|
+
for (let a = 0; a < n; a++)
|
|
608
|
+
i += c, e.push({ time: i, overshoot: o, isOvershoot: !0 }), i += c, e.push({ time: i, overshoot: o * t, isOvershoot: !1 }), o *= t;
|
|
609
|
+
return e.push({ time: 1, overshoot: 0, isOvershoot: !1 }), e;
|
|
610
|
+
}
|
|
611
|
+
function Qt(n, t, e, i) {
|
|
612
|
+
const { stiffness: o, damping: s, mass: c, oscillations: a } = i, h = e.x - t.x, l = e.y - t.y, g = Math.sqrt(o / c), r = s / (2 * Math.sqrt(o * c));
|
|
613
|
+
let d;
|
|
614
|
+
if (r < 1) {
|
|
615
|
+
const f = g * Math.sqrt(1 - r * r), y = Math.exp(-r * g * n * 3), p = Math.cos(f * n * a * Math.PI);
|
|
616
|
+
d = 1 - y * p;
|
|
617
|
+
} else
|
|
618
|
+
d = 1 - Math.exp(-g * n * 3);
|
|
619
|
+
return d = Math.max(0, Math.min(d, 1.3)), {
|
|
620
|
+
x: t.x + h * d,
|
|
621
|
+
y: t.y + l * d
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function Kt(n, t, e, i) {
|
|
625
|
+
const { amplitude: o, frequency: s, decay: c, decayRate: a, phase: h } = i, l = e.x - t.x, g = e.y - t.y, r = Math.sqrt(l * l + g * g), d = r > 0 ? -g / r : 0, f = r > 0 ? l / r : 1, y = s * Math.PI * 2 * n + h, p = c ? Math.pow(1 - n, a) : 1, u = o * Math.sin(y) * p, m = te(n);
|
|
626
|
+
return {
|
|
627
|
+
x: st(t.x, e.x, m) + u * d,
|
|
628
|
+
y: st(t.y, e.y, m) + u * f
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function ut(n) {
|
|
632
|
+
return 1 - (1 - n) * (1 - n);
|
|
633
|
+
}
|
|
634
|
+
function te(n) {
|
|
635
|
+
return 1 - Math.pow(1 - n, 3);
|
|
636
|
+
}
|
|
637
|
+
function ee(n, t, e) {
|
|
638
|
+
const { amplitude: i, frequency: o, decay: s } = e, c = Math.sin(n * o * Math.PI * 2), a = s ? Math.pow(1 - n, 2) : 1, h = i * c * a;
|
|
639
|
+
return t + h;
|
|
640
|
+
}
|
|
641
|
+
function ie(n, t, e) {
|
|
642
|
+
const { overshoot: i, bounces: o } = e, s = [];
|
|
643
|
+
s.push({ time: 0.5, scale: i });
|
|
644
|
+
let c = i;
|
|
645
|
+
const a = 0.5, l = 0.5 / (o * 2);
|
|
646
|
+
let g = 0.5;
|
|
647
|
+
for (let d = 0; d < o; d++) {
|
|
648
|
+
const f = 1 - (c - 1) * a;
|
|
649
|
+
g += l, s.push({ time: g, scale: f }), c = 1 + (c - 1) * a * a, g += l, d < o - 1 && s.push({ time: g, scale: c });
|
|
650
|
+
}
|
|
651
|
+
s.push({ time: 1, scale: 1 });
|
|
652
|
+
let r = 1;
|
|
653
|
+
for (let d = 0; d < s.length; d++)
|
|
654
|
+
if (n <= s[d].time) {
|
|
655
|
+
const f = d === 0 ? 0 : s[d - 1].time, y = d === 0 ? 1 : s[d - 1].scale, p = (n - f) / (s[d].time - f), u = ut(p);
|
|
656
|
+
r = y + (s[d].scale - y) * u;
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
return r * t;
|
|
660
|
+
}
|
|
661
|
+
function ne(n) {
|
|
662
|
+
const {
|
|
663
|
+
element: t,
|
|
664
|
+
startPosition: e,
|
|
665
|
+
endPosition: i,
|
|
666
|
+
pathConfig: o,
|
|
667
|
+
duration: s,
|
|
668
|
+
imageWidth: c,
|
|
669
|
+
imageHeight: a,
|
|
670
|
+
rotation: h,
|
|
671
|
+
scale: l,
|
|
672
|
+
onComplete: g,
|
|
673
|
+
rotationConfig: r,
|
|
674
|
+
startRotation: d,
|
|
675
|
+
scaleConfig: f,
|
|
676
|
+
startScale: y
|
|
677
|
+
} = n, p = o.type, u = d !== void 0 && d !== h, m = (r == null ? void 0 : r.mode) === "wobble", w = (r == null ? void 0 : r.wobble) || { amplitude: 15, frequency: 3, decay: !0 }, v = u || m, E = y !== void 0 && y !== l, M = (f == null ? void 0 : f.mode) === "pop", x = (f == null ? void 0 : f.pop) || { overshoot: 1.2, bounces: 1 };
|
|
678
|
+
if ((p === "linear" || p === "arc") && !v && !(E || M)) {
|
|
679
|
+
g && g();
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
const G = performance.now(), j = -c / 2, P = -a / 2;
|
|
683
|
+
function U(k) {
|
|
684
|
+
const F = k - G, I = Math.min(F / s, 1);
|
|
685
|
+
let A;
|
|
686
|
+
switch (p) {
|
|
687
|
+
case "bounce": {
|
|
688
|
+
const D = Yt(
|
|
689
|
+
o.bouncePreset,
|
|
690
|
+
o.bounce
|
|
691
|
+
);
|
|
692
|
+
A = Jt(I, e, i, D);
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case "elastic": {
|
|
696
|
+
const D = Xt(
|
|
697
|
+
o.elasticPreset,
|
|
698
|
+
o.elastic
|
|
699
|
+
);
|
|
700
|
+
A = Qt(I, e, i, D);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
case "wave": {
|
|
704
|
+
const D = Bt(
|
|
705
|
+
o.wavePreset,
|
|
706
|
+
o.wave
|
|
707
|
+
);
|
|
708
|
+
A = Kt(I, e, i, D);
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
default:
|
|
712
|
+
A = {
|
|
713
|
+
x: st(e.x, i.x, I),
|
|
714
|
+
y: st(e.y, i.y, I)
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
const O = A.x - i.x, S = A.y - i.y;
|
|
718
|
+
let z;
|
|
719
|
+
m ? z = ee(I, h, w) : u ? z = st(d, h, I) : z = h;
|
|
720
|
+
let T;
|
|
721
|
+
M ? T = ie(I, l, x) : E ? T = st(y, l, I) : T = l, t.style.transform = `translate(${j}px, ${P}px) translate(${O}px, ${S}px) rotate(${z}deg) scale(${T})`, I < 1 ? requestAnimationFrame(U) : (t.style.transform = `translate(${j}px, ${P}px) rotate(${h}deg) scale(${l})`, g && g());
|
|
722
|
+
}
|
|
723
|
+
requestAnimationFrame(U);
|
|
724
|
+
}
|
|
725
|
+
function oe(n) {
|
|
726
|
+
return n === "bounce" || n === "elastic" || n === "wave";
|
|
727
|
+
}
|
|
728
|
+
const se = {
|
|
729
|
+
radial: "center",
|
|
730
|
+
spiral: "center",
|
|
731
|
+
grid: "top",
|
|
732
|
+
cluster: "nearest-edge",
|
|
733
|
+
random: "nearest-edge",
|
|
734
|
+
wave: "left"
|
|
735
|
+
};
|
|
736
|
+
class ae {
|
|
737
|
+
constructor(t, e) {
|
|
738
|
+
this.config = t, this.layoutAlgorithm = e, this.resolvedStartPosition = this.resolveStartPosition(), this.pathConfig = t.path || It, this.rotationConfig = t.rotation || Rt, this.scaleConfig = t.scale || Et;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Get the effective start position, considering layout-aware defaults
|
|
742
|
+
*/
|
|
743
|
+
resolveStartPosition() {
|
|
744
|
+
return this.config.start.position ? this.config.start.position : se[this.layoutAlgorithm] || "nearest-edge";
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Calculate the starting position for an image's entry animation
|
|
748
|
+
*/
|
|
749
|
+
calculateStartPosition(t, e, i, o, s) {
|
|
750
|
+
const c = this.resolvedStartPosition, a = this.config.start.offset ?? 100;
|
|
751
|
+
switch (c) {
|
|
752
|
+
case "nearest-edge":
|
|
753
|
+
return this.calculateNearestEdge(t, e, i, a);
|
|
754
|
+
case "top":
|
|
755
|
+
return this.calculateEdgePosition("top", t, e, i, a);
|
|
756
|
+
case "bottom":
|
|
757
|
+
return this.calculateEdgePosition("bottom", t, e, i, a);
|
|
758
|
+
case "left":
|
|
759
|
+
return this.calculateEdgePosition("left", t, e, i, a);
|
|
760
|
+
case "right":
|
|
761
|
+
return this.calculateEdgePosition("right", t, e, i, a);
|
|
762
|
+
case "center":
|
|
763
|
+
return this.calculateCenterPosition(i, t, e);
|
|
764
|
+
case "random-edge":
|
|
765
|
+
return this.calculateRandomEdge(t, e, i, a);
|
|
766
|
+
case "circular":
|
|
767
|
+
return this.calculateCircularPosition(
|
|
768
|
+
t,
|
|
769
|
+
e,
|
|
770
|
+
i,
|
|
771
|
+
o,
|
|
772
|
+
s
|
|
773
|
+
);
|
|
774
|
+
default:
|
|
775
|
+
return this.calculateNearestEdge(t, e, i, a);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Calculate start position from the nearest edge (current default behavior)
|
|
780
|
+
*/
|
|
781
|
+
calculateNearestEdge(t, e, i, o) {
|
|
782
|
+
const s = t.x, c = t.y, a = s, h = i.width - s, l = c, g = i.height - c, r = Math.min(a, h, l, g);
|
|
783
|
+
let d = t.x, f = t.y;
|
|
784
|
+
return r === a ? d = -(e.width + o) : r === h ? d = i.width + o : r === l ? f = -(e.height + o) : f = i.height + o, { x: d, y: f };
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Calculate start position from a specific edge
|
|
788
|
+
*/
|
|
789
|
+
calculateEdgePosition(t, e, i, o, s) {
|
|
790
|
+
let c = e.x, a = e.y;
|
|
791
|
+
switch (t) {
|
|
792
|
+
case "top":
|
|
793
|
+
a = -(i.height + s);
|
|
794
|
+
break;
|
|
795
|
+
case "bottom":
|
|
796
|
+
a = o.height + s;
|
|
797
|
+
break;
|
|
798
|
+
case "left":
|
|
799
|
+
c = -(i.width + s);
|
|
800
|
+
break;
|
|
801
|
+
case "right":
|
|
802
|
+
c = o.width + s;
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
return { x: c, y: a };
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Calculate start position from center with scale animation
|
|
809
|
+
*/
|
|
810
|
+
calculateCenterPosition(t, e, i) {
|
|
811
|
+
const o = t.width / 2, s = t.height / 2;
|
|
812
|
+
return {
|
|
813
|
+
x: o,
|
|
814
|
+
y: s,
|
|
815
|
+
useScale: !0
|
|
816
|
+
// Signal to use scale animation from 0
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Calculate start position from a random edge
|
|
821
|
+
*/
|
|
822
|
+
calculateRandomEdge(t, e, i, o) {
|
|
823
|
+
const s = ["top", "bottom", "left", "right"], c = s[Math.floor(Math.random() * s.length)];
|
|
824
|
+
return this.calculateEdgePosition(c, t, e, i, o);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Calculate start position on a circle around the container
|
|
828
|
+
*/
|
|
829
|
+
calculateCircularPosition(t, e, i, o, s) {
|
|
830
|
+
const c = this.config.start.circular || {}, a = c.distribution || "even";
|
|
831
|
+
let h;
|
|
832
|
+
const l = c.radius || "120%";
|
|
833
|
+
if (typeof l == "string" && l.endsWith("%")) {
|
|
834
|
+
const p = parseFloat(l) / 100;
|
|
835
|
+
h = Math.sqrt(
|
|
836
|
+
i.width ** 2 + i.height ** 2
|
|
837
|
+
) * p / 2;
|
|
838
|
+
} else
|
|
839
|
+
h = typeof l == "number" ? l : 500;
|
|
840
|
+
let g;
|
|
841
|
+
a === "even" ? g = o / s * 2 * Math.PI : g = Math.random() * 2 * Math.PI;
|
|
842
|
+
const r = i.width / 2, d = i.height / 2, f = r + Math.cos(g) * h, y = d + Math.sin(g) * h;
|
|
843
|
+
return { x: f, y };
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get animation parameters for an image
|
|
847
|
+
*/
|
|
848
|
+
getAnimationParams(t) {
|
|
849
|
+
const e = this.config.timing.duration, i = this.config.timing.stagger, o = this.config.easing;
|
|
850
|
+
return {
|
|
851
|
+
startTransform: "",
|
|
852
|
+
// Will be computed by caller based on start position
|
|
853
|
+
duration: e,
|
|
854
|
+
delay: t * i,
|
|
855
|
+
easing: o
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Build a CSS transform string for the start position
|
|
860
|
+
* Uses pixel-based centering offset for reliable cross-browser behavior
|
|
861
|
+
*/
|
|
862
|
+
buildStartTransform(t, e, i, o, s, c, a, h) {
|
|
863
|
+
const l = t.x - e.x, g = t.y - e.y, r = a !== void 0 ? a : i, d = h !== void 0 ? h : o, f = s !== void 0 ? -s / 2 : 0, y = c !== void 0 ? -c / 2 : 0, p = s !== void 0 ? `translate(${f}px, ${y}px)` : "translate(-50%, -50%)";
|
|
864
|
+
return t.useScale ? `${p} translate(${l}px, ${g}px) rotate(${r}deg) scale(0)` : `${p} translate(${l}px, ${g}px) rotate(${r}deg) scale(${d})`;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Build the final CSS transform string
|
|
868
|
+
* Uses pixel-based centering offset for reliable cross-browser behavior
|
|
869
|
+
*/
|
|
870
|
+
buildFinalTransform(t, e, i, o) {
|
|
871
|
+
if (i !== void 0 && o !== void 0) {
|
|
872
|
+
const s = -i / 2, c = -o / 2;
|
|
873
|
+
return `translate(${s}px, ${c}px) rotate(${t}deg) scale(${e})`;
|
|
874
|
+
}
|
|
875
|
+
return `translate(-50%, -50%) rotate(${t}deg) scale(${e})`;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Get the transition CSS for entry animation
|
|
879
|
+
* For JS-animated paths, only animate opacity (transform handled by JS)
|
|
880
|
+
*/
|
|
881
|
+
getTransitionCSS() {
|
|
882
|
+
const t = this.config.timing.duration, e = this.config.easing;
|
|
883
|
+
return this.requiresJSAnimation() ? `opacity ${t}ms ease-out` : `opacity ${t}ms ease-out, transform ${t}ms ${e}`;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Check if the current path type requires JavaScript animation
|
|
887
|
+
*/
|
|
888
|
+
requiresJSAnimation() {
|
|
889
|
+
return oe(this.pathConfig.type);
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Get the path configuration
|
|
893
|
+
*/
|
|
894
|
+
getPathConfig() {
|
|
895
|
+
return this.pathConfig;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Get the path type
|
|
899
|
+
*/
|
|
900
|
+
getPathType() {
|
|
901
|
+
return this.pathConfig.type;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Get animation timing configuration
|
|
905
|
+
*/
|
|
906
|
+
getTiming() {
|
|
907
|
+
return {
|
|
908
|
+
duration: this.config.timing.duration,
|
|
909
|
+
stagger: this.config.timing.stagger
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Get the rotation configuration
|
|
914
|
+
*/
|
|
915
|
+
getRotationConfig() {
|
|
916
|
+
return this.rotationConfig;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Get the rotation mode
|
|
920
|
+
*/
|
|
921
|
+
getRotationMode() {
|
|
922
|
+
return this.rotationConfig.mode;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Calculate the starting rotation for an entry animation
|
|
926
|
+
* @param finalRotation - The final rotation from the layout
|
|
927
|
+
* @returns The starting rotation in degrees
|
|
928
|
+
*/
|
|
929
|
+
calculateStartRotation(t) {
|
|
930
|
+
switch (this.rotationConfig.mode) {
|
|
931
|
+
case "none":
|
|
932
|
+
return t;
|
|
933
|
+
case "settle": {
|
|
934
|
+
const i = this.rotationConfig.startRotation;
|
|
935
|
+
if (i === void 0)
|
|
936
|
+
return t + (Math.random() - 0.5) * 60;
|
|
937
|
+
if (typeof i == "number")
|
|
938
|
+
return i;
|
|
939
|
+
const o = i.max - i.min;
|
|
940
|
+
return i.min + Math.random() * o;
|
|
941
|
+
}
|
|
942
|
+
case "spin": {
|
|
943
|
+
const i = this.rotationConfig.spinCount ?? 1, o = this.resolveSpinDirection(t);
|
|
944
|
+
return t + i * 360 * o;
|
|
945
|
+
}
|
|
946
|
+
case "random":
|
|
947
|
+
return t + (Math.random() - 0.5) * 60;
|
|
948
|
+
case "wobble":
|
|
949
|
+
return t;
|
|
950
|
+
default:
|
|
951
|
+
return t;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Resolve spin direction based on config
|
|
956
|
+
* @returns 1 for clockwise, -1 for counterclockwise
|
|
957
|
+
*/
|
|
958
|
+
resolveSpinDirection(t) {
|
|
959
|
+
switch (this.rotationConfig.direction ?? "auto") {
|
|
960
|
+
case "clockwise":
|
|
961
|
+
return -1;
|
|
962
|
+
case "counterclockwise":
|
|
963
|
+
return 1;
|
|
964
|
+
case "random":
|
|
965
|
+
return Math.random() < 0.5 ? 1 : -1;
|
|
966
|
+
case "auto":
|
|
967
|
+
default:
|
|
968
|
+
return t >= 0 ? 1 : -1;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Check if the current rotation mode requires JavaScript animation
|
|
973
|
+
* (as opposed to CSS transitions)
|
|
974
|
+
*/
|
|
975
|
+
requiresJSRotation() {
|
|
976
|
+
return this.rotationConfig.mode === "wobble";
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Calculate wobble rotation for a given animation progress
|
|
980
|
+
* @param progress - Animation progress from 0 to 1
|
|
981
|
+
* @param finalRotation - The final rotation in degrees
|
|
982
|
+
* @returns The current rotation in degrees
|
|
983
|
+
*/
|
|
984
|
+
calculateWobbleRotation(t, e) {
|
|
985
|
+
if (this.rotationConfig.mode !== "wobble")
|
|
986
|
+
return e;
|
|
987
|
+
const i = this.rotationConfig.wobble || {
|
|
988
|
+
amplitude: 15,
|
|
989
|
+
frequency: 3,
|
|
990
|
+
decay: !0
|
|
991
|
+
}, { amplitude: o, frequency: s, decay: c } = i, a = Math.sin(t * s * Math.PI * 2), h = c ? Math.pow(1 - t, 2) : 1, l = o * a * h;
|
|
992
|
+
return e + l;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Get the scale configuration
|
|
996
|
+
*/
|
|
997
|
+
getScaleConfig() {
|
|
998
|
+
return this.scaleConfig;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Get the scale mode
|
|
1002
|
+
*/
|
|
1003
|
+
getScaleMode() {
|
|
1004
|
+
return this.scaleConfig.mode;
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Calculate the starting scale for an entry animation
|
|
1008
|
+
* @param finalScale - The final scale from the layout
|
|
1009
|
+
* @returns The starting scale
|
|
1010
|
+
*/
|
|
1011
|
+
calculateStartScale(t) {
|
|
1012
|
+
switch (this.scaleConfig.mode) {
|
|
1013
|
+
case "none":
|
|
1014
|
+
return t;
|
|
1015
|
+
case "grow":
|
|
1016
|
+
return (this.scaleConfig.startScale ?? 0.3) * t;
|
|
1017
|
+
case "shrink":
|
|
1018
|
+
return (this.scaleConfig.startScale ?? 1.5) * t;
|
|
1019
|
+
case "pop":
|
|
1020
|
+
return t;
|
|
1021
|
+
case "random": {
|
|
1022
|
+
const i = this.scaleConfig.range ?? { min: 0.5, max: 1 };
|
|
1023
|
+
return (i.min + Math.random() * (i.max - i.min)) * t;
|
|
1024
|
+
}
|
|
1025
|
+
default:
|
|
1026
|
+
return t;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Check if the current scale mode requires JavaScript animation
|
|
1031
|
+
* (as opposed to CSS transitions)
|
|
1032
|
+
*/
|
|
1033
|
+
requiresJSScale() {
|
|
1034
|
+
return this.scaleConfig.mode === "pop";
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Calculate pop scale for a given animation progress
|
|
1038
|
+
* @param progress - Animation progress from 0 to 1
|
|
1039
|
+
* @param finalScale - The final scale value
|
|
1040
|
+
* @returns The current scale value with bounce effect
|
|
1041
|
+
*/
|
|
1042
|
+
calculatePopScale(t, e) {
|
|
1043
|
+
if (this.scaleConfig.mode !== "pop")
|
|
1044
|
+
return e;
|
|
1045
|
+
const i = this.scaleConfig.pop || {
|
|
1046
|
+
overshoot: 1.2,
|
|
1047
|
+
bounces: 1
|
|
1048
|
+
}, { overshoot: o, bounces: s } = i, c = this.generateScaleBounceKeyframes(s, o);
|
|
1049
|
+
let a = e;
|
|
1050
|
+
for (let h = 0; h < c.length; h++)
|
|
1051
|
+
if (t <= c[h].time) {
|
|
1052
|
+
const l = h === 0 ? 0 : c[h - 1].time, g = h === 0 ? e : c[h - 1].scale, r = (t - l) / (c[h].time - l), d = this.easeOutQuad(r);
|
|
1053
|
+
a = g + (c[h].scale - g) * d;
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
return a * e;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Generate keyframes for scale bounce animation
|
|
1060
|
+
*/
|
|
1061
|
+
generateScaleBounceKeyframes(t, e) {
|
|
1062
|
+
const i = [];
|
|
1063
|
+
i.push({ time: 0.5, scale: e });
|
|
1064
|
+
let o = e;
|
|
1065
|
+
const s = 0.5, a = 0.5 / (t * 2);
|
|
1066
|
+
let h = 0.5;
|
|
1067
|
+
for (let l = 0; l < t; l++) {
|
|
1068
|
+
const g = 1 - (o - 1) * s;
|
|
1069
|
+
h += a, i.push({ time: h, scale: g }), o = 1 + (o - 1) * s * s, h += a, l < t - 1 && i.push({ time: h, scale: o });
|
|
1070
|
+
}
|
|
1071
|
+
return i.push({ time: 1, scale: 1 }), i;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Easing function for smooth transitions
|
|
1075
|
+
*/
|
|
1076
|
+
easeOutQuad(t) {
|
|
1077
|
+
return 1 - (1 - t) * (1 - t);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
class re {
|
|
1081
|
+
constructor(t, e = {}) {
|
|
1082
|
+
this.config = t, this.imageConfig = e;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Generate random layout positions for images
|
|
1086
|
+
* @param imageCount - Number of images to layout
|
|
1087
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1088
|
+
* @param options - Optional overrides (includes fixedHeight)
|
|
1089
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1090
|
+
*/
|
|
1091
|
+
generate(t, e, i = {}) {
|
|
1092
|
+
var x, L, G, j, P, U, k, F, I;
|
|
1093
|
+
const o = [], { width: s, height: c } = e, a = this.config.spacing.padding, h = i.fixedHeight ?? this.config.sizing.base, l = ((x = this.imageConfig.rotation) == null ? void 0 : x.mode) ?? "none", g = ((G = (L = this.imageConfig.rotation) == null ? void 0 : L.range) == null ? void 0 : G.min) ?? -15, r = ((P = (j = this.imageConfig.rotation) == null ? void 0 : j.range) == null ? void 0 : P.max) ?? 15, d = ((k = (U = this.imageConfig.sizing) == null ? void 0 : U.variance) == null ? void 0 : k.min) ?? 1, f = ((I = (F = this.imageConfig.sizing) == null ? void 0 : F.variance) == null ? void 0 : I.max) ?? 1, y = d !== 1 || f !== 1, u = h * 1.5 / 2, m = h / 2, w = s - a - u, v = c - a - m, E = a + u, M = a + m;
|
|
1094
|
+
for (let A = 0; A < t; A++) {
|
|
1095
|
+
const O = this.random(E, w), S = this.random(M, v), z = l === "random" ? this.random(g, r) : 0, T = y ? this.random(d, f) : 1, D = h * T, Z = {
|
|
1096
|
+
id: A,
|
|
1097
|
+
x: O,
|
|
1098
|
+
y: S,
|
|
1099
|
+
rotation: z,
|
|
1100
|
+
scale: T,
|
|
1101
|
+
baseSize: D
|
|
1102
|
+
};
|
|
1103
|
+
o.push(Z);
|
|
1104
|
+
}
|
|
1105
|
+
return o;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Utility: Generate random number between min and max
|
|
1109
|
+
* @param min - Minimum value
|
|
1110
|
+
* @param max - Maximum value
|
|
1111
|
+
* @returns Random number in range
|
|
1112
|
+
*/
|
|
1113
|
+
random(t, e) {
|
|
1114
|
+
return Math.random() * (e - t) + t;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
class ce {
|
|
1118
|
+
constructor(t, e = {}) {
|
|
1119
|
+
this.config = t, this.imageConfig = e;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Generate radial layout positions for images
|
|
1123
|
+
* @param imageCount - Number of images to layout
|
|
1124
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1125
|
+
* @param options - Optional overrides
|
|
1126
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1127
|
+
*/
|
|
1128
|
+
generate(t, e, i = {}) {
|
|
1129
|
+
var L, G, j, P, U, k, F, I, A, O;
|
|
1130
|
+
const o = [], { width: s, height: c } = e, { debugRadials: a } = this.config, h = this.config.sizing.base, l = ((L = this.imageConfig.rotation) == null ? void 0 : L.mode) ?? "none", g = ((j = (G = this.imageConfig.rotation) == null ? void 0 : G.range) == null ? void 0 : j.min) ?? -15, r = ((U = (P = this.imageConfig.rotation) == null ? void 0 : P.range) == null ? void 0 : U.max) ?? 15, d = ((F = (k = this.imageConfig.sizing) == null ? void 0 : k.variance) == null ? void 0 : F.min) ?? 1, f = ((A = (I = this.imageConfig.sizing) == null ? void 0 : I.variance) == null ? void 0 : A.max) ?? 1, y = d !== 1 || f !== 1, p = ((O = this.imageConfig.sizing) == null ? void 0 : O.scaleDecay) ?? 0, u = ["green", "blue", "red", "yellow", "orange", "purple"], m = i.fixedHeight ?? h, w = s / 2, v = c / 2, E = Math.ceil(Math.sqrt(t));
|
|
1131
|
+
if (t > 0) {
|
|
1132
|
+
const S = y ? this.random(d, f) : 1, z = m * S;
|
|
1133
|
+
o.push({
|
|
1134
|
+
id: 0,
|
|
1135
|
+
x: w,
|
|
1136
|
+
y: v,
|
|
1137
|
+
rotation: l === "random" ? this.random(g * 0.33, r * 0.33) : 0,
|
|
1138
|
+
// Less rotation for center
|
|
1139
|
+
scale: S,
|
|
1140
|
+
baseSize: z,
|
|
1141
|
+
zIndex: 100,
|
|
1142
|
+
// Center image is highest
|
|
1143
|
+
borderColor: a ? "cyan" : void 0
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
let M = 1, x = 1;
|
|
1147
|
+
for (; M < t; ) {
|
|
1148
|
+
const S = x / E, z = p > 0 ? 1 - S * p * 0.5 : 1, T = x * (m * 0.8), D = T * 1.5, Z = Math.PI * (3 * (D + T) - Math.sqrt((3 * D + T) * (D + 3 * T))), et = this.estimateWidth(m), W = Math.floor(Z / (et * 0.7));
|
|
1149
|
+
if (W === 0) {
|
|
1150
|
+
x++;
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
const N = 2 * Math.PI / W, it = x * (20 * Math.PI / 180);
|
|
1154
|
+
for (let Q = 0; Q < W && M < t; Q++) {
|
|
1155
|
+
const B = Q * N + it, q = y ? this.random(d, f) : 1, nt = z * q, V = m * nt;
|
|
1156
|
+
let C = w + Math.cos(B) * D, H = v + Math.sin(B) * T;
|
|
1157
|
+
const $ = this.config.spacing.padding ?? 50, K = V * 1.5 / 2, _ = V / 2;
|
|
1158
|
+
C - K < $ ? C = $ + K : C + K > s - $ && (C = s - $ - K), H - _ < $ ? H = $ + _ : H + _ > c - $ && (H = c - $ - _);
|
|
1159
|
+
const tt = l === "random" ? this.random(g, r) : 0;
|
|
1160
|
+
o.push({
|
|
1161
|
+
id: M,
|
|
1162
|
+
x: C,
|
|
1163
|
+
y: H,
|
|
1164
|
+
rotation: tt,
|
|
1165
|
+
scale: nt,
|
|
1166
|
+
baseSize: V,
|
|
1167
|
+
zIndex: Math.max(1, 100 - x),
|
|
1168
|
+
// Outer rings have lower z-index
|
|
1169
|
+
borderColor: a ? u[(x - 1) % u.length] : void 0
|
|
1170
|
+
}), M++;
|
|
1171
|
+
}
|
|
1172
|
+
x++;
|
|
1173
|
+
}
|
|
1174
|
+
return o;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Estimate image width based on height
|
|
1178
|
+
* Assumes landscape aspect ratio (approximately 1.4:1)
|
|
1179
|
+
* @param height - Image height
|
|
1180
|
+
* @returns Estimated width
|
|
1181
|
+
*/
|
|
1182
|
+
estimateWidth(t) {
|
|
1183
|
+
return t * 1.4;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Utility: Generate random number between min and max
|
|
1187
|
+
* @param min - Minimum value
|
|
1188
|
+
* @param max - Maximum value
|
|
1189
|
+
* @returns Random number in range
|
|
1190
|
+
*/
|
|
1191
|
+
random(t, e) {
|
|
1192
|
+
return Math.random() * (e - t) + t;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
console.log("[GridPlacementGenerator] Module loaded");
|
|
1196
|
+
const le = {
|
|
1197
|
+
columns: "auto",
|
|
1198
|
+
rows: "auto",
|
|
1199
|
+
stagger: "none",
|
|
1200
|
+
jitter: 0,
|
|
1201
|
+
overlap: 0,
|
|
1202
|
+
fillDirection: "row",
|
|
1203
|
+
alignment: "center",
|
|
1204
|
+
gap: 10,
|
|
1205
|
+
overflowOffset: 0.25
|
|
1206
|
+
}, Ft = [
|
|
1207
|
+
{ x: 1, y: 1 },
|
|
1208
|
+
// bottom-right
|
|
1209
|
+
{ x: -1, y: -1 },
|
|
1210
|
+
// upper-left
|
|
1211
|
+
{ x: 1, y: -1 },
|
|
1212
|
+
// upper-right
|
|
1213
|
+
{ x: -1, y: 1 },
|
|
1214
|
+
// bottom-left
|
|
1215
|
+
{ x: -1, y: 0 },
|
|
1216
|
+
// left
|
|
1217
|
+
{ x: 1, y: 0 },
|
|
1218
|
+
// right
|
|
1219
|
+
{ x: 0, y: -1 },
|
|
1220
|
+
// up
|
|
1221
|
+
{ x: 0, y: 1 }
|
|
1222
|
+
// down
|
|
1223
|
+
];
|
|
1224
|
+
class he {
|
|
1225
|
+
constructor(t, e = {}) {
|
|
1226
|
+
this.config = t, this.imageConfig = e;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Generate grid layout positions for images
|
|
1230
|
+
* @param imageCount - Number of images to layout
|
|
1231
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1232
|
+
* @param options - Optional overrides (includes fixedHeight)
|
|
1233
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1234
|
+
*/
|
|
1235
|
+
generate(t, e, i = {}) {
|
|
1236
|
+
var et, W, N, it, Q, B, q, nt, V;
|
|
1237
|
+
const o = [], { width: s, height: c } = e, a = { ...le, ...this.config.grid }, h = this.config.spacing.padding, l = i.fixedHeight ?? this.config.sizing.base, g = ((et = this.imageConfig.rotation) == null ? void 0 : et.mode) ?? "none", r = ((N = (W = this.imageConfig.sizing) == null ? void 0 : W.variance) == null ? void 0 : N.min) ?? 1, d = ((Q = (it = this.imageConfig.sizing) == null ? void 0 : it.variance) == null ? void 0 : Q.max) ?? 1, f = r !== 1 || d !== 1, y = s - 2 * h, p = c - 2 * h, { columns: u, rows: m } = this.calculateGridDimensions(
|
|
1238
|
+
t,
|
|
1239
|
+
y,
|
|
1240
|
+
p,
|
|
1241
|
+
l,
|
|
1242
|
+
a
|
|
1243
|
+
), w = a.stagger === "row", v = a.stagger === "column", E = w ? u + 0.5 : u, M = v ? m + 0.5 : m, x = (y - a.gap * (u - 1)) / E, L = (p - a.gap * (m - 1)) / M, G = w ? x / 2 : 0, j = v ? L / 2 : 0, P = 1 + a.overlap, U = Math.min(x, L) * P, k = i.fixedHeight ? Math.min(i.fixedHeight, U) : U, F = u * x + (u - 1) * a.gap + G, I = m * L + (m - 1) * a.gap + j, A = h + (y - F) / 2, O = h + (p - I) / 2, S = u * m, z = a.columns !== "auto" && a.rows !== "auto", T = z && t > S;
|
|
1244
|
+
console.log("[GridGen] generate called, imageCount:", t, "gridConfig:", JSON.stringify({ columns: a.columns, rows: a.rows })), console.log("[GridGen] cellCount:", S, "hasFixedGrid:", z, "isOverflowMode:", T), typeof window < "u" && (window.__gridOverflowDebug = {
|
|
1245
|
+
gridConfigColumns: a.columns,
|
|
1246
|
+
gridConfigRows: a.rows,
|
|
1247
|
+
columns: u,
|
|
1248
|
+
rows: m,
|
|
1249
|
+
cellCount: S,
|
|
1250
|
+
hasFixedGrid: z,
|
|
1251
|
+
imageCount: t,
|
|
1252
|
+
isOverflowMode: T
|
|
1253
|
+
});
|
|
1254
|
+
const D = T ? new Array(S).fill(0) : [], Z = Math.min(x, L) * a.overflowOffset;
|
|
1255
|
+
for (let C = 0; C < t; C++) {
|
|
1256
|
+
let H, $, Y = 0;
|
|
1257
|
+
if (T && C >= S) {
|
|
1258
|
+
const J = C - S, X = J % S;
|
|
1259
|
+
Y = Math.floor(J / S) + 1, D[X]++, a.fillDirection === "row" ? (H = X % u, $ = Math.floor(X / u)) : ($ = X % m, H = Math.floor(X / m));
|
|
1260
|
+
} else
|
|
1261
|
+
a.fillDirection === "row" ? (H = C % u, $ = Math.floor(C / u)) : ($ = C % m, H = Math.floor(C / m));
|
|
1262
|
+
let K = A + H * (x + a.gap) + x / 2, _ = O + $ * (L + a.gap) + L / 2;
|
|
1263
|
+
if (a.stagger === "row" && $ % 2 === 1 ? K += x / 2 : a.stagger === "column" && H % 2 === 1 && (_ += L / 2), Y > 0) {
|
|
1264
|
+
const J = (Y - 1) % Ft.length, X = Ft[J];
|
|
1265
|
+
K += X.x * Z, _ += X.y * Z;
|
|
1266
|
+
}
|
|
1267
|
+
if (a.jitter > 0) {
|
|
1268
|
+
const J = x / 2 * a.jitter, X = L / 2 * a.jitter;
|
|
1269
|
+
K += this.random(-J, J), _ += this.random(-X, X);
|
|
1270
|
+
}
|
|
1271
|
+
let tt = K, ot = _;
|
|
1272
|
+
if (!T && a.fillDirection === "row") {
|
|
1273
|
+
const J = t % u || u;
|
|
1274
|
+
if ($ === Math.floor((t - 1) / u) && J < u) {
|
|
1275
|
+
const At = J * x + (J - 1) * a.gap;
|
|
1276
|
+
let xt = 0;
|
|
1277
|
+
a.alignment === "center" ? xt = (F - At) / 2 : a.alignment === "end" && (xt = F - At), tt += xt;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
const ft = f ? this.random(r, d) : 1, at = k * ft, dt = at * 1.5 / 2, gt = at / 2, yt = h + dt, bt = s - h - dt, Pt = h + gt, Ut = c - h - gt;
|
|
1281
|
+
tt = Math.max(yt, Math.min(tt, bt)), ot = Math.max(Pt, Math.min(ot, Ut));
|
|
1282
|
+
let vt = 0;
|
|
1283
|
+
if (g === "random") {
|
|
1284
|
+
const J = ((q = (B = this.imageConfig.rotation) == null ? void 0 : B.range) == null ? void 0 : q.min) ?? -15, X = ((V = (nt = this.imageConfig.rotation) == null ? void 0 : nt.range) == null ? void 0 : V.max) ?? 15;
|
|
1285
|
+
a.jitter > 0 ? vt = this.random(J * a.jitter, X * a.jitter) : vt = this.random(J, X);
|
|
1286
|
+
}
|
|
1287
|
+
let wt;
|
|
1288
|
+
T && Y > 0 ? wt = 50 - Y : wt = T ? 100 + C : C + 1, o.push({
|
|
1289
|
+
id: C,
|
|
1290
|
+
x: tt,
|
|
1291
|
+
y: ot,
|
|
1292
|
+
rotation: vt,
|
|
1293
|
+
scale: ft,
|
|
1294
|
+
baseSize: at,
|
|
1295
|
+
zIndex: wt
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
return o;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Calculate optimal grid dimensions based on image count and container
|
|
1302
|
+
*/
|
|
1303
|
+
calculateGridDimensions(t, e, i, o, s) {
|
|
1304
|
+
let c, a;
|
|
1305
|
+
if (s.columns !== "auto" && s.rows !== "auto")
|
|
1306
|
+
c = s.columns, a = s.rows;
|
|
1307
|
+
else if (s.columns !== "auto")
|
|
1308
|
+
c = s.columns, a = Math.ceil(t / c);
|
|
1309
|
+
else if (s.rows !== "auto")
|
|
1310
|
+
a = s.rows, c = Math.ceil(t / a);
|
|
1311
|
+
else {
|
|
1312
|
+
const h = e / i;
|
|
1313
|
+
for (c = Math.max(1, Math.round(Math.sqrt(t * h / 1.4))), a = Math.ceil(t / c); c > 1 && (c - 1) * a >= t; )
|
|
1314
|
+
c--;
|
|
1315
|
+
}
|
|
1316
|
+
return { columns: Math.max(1, c), rows: Math.max(1, a) };
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Utility: Generate random number between min and max
|
|
1320
|
+
*/
|
|
1321
|
+
random(t, e) {
|
|
1322
|
+
return Math.random() * (e - t) + t;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
const de = Math.PI * (3 - Math.sqrt(5)), ge = {
|
|
1326
|
+
spiralType: "golden",
|
|
1327
|
+
direction: "counterclockwise",
|
|
1328
|
+
tightness: 1,
|
|
1329
|
+
scaleDecay: 0,
|
|
1330
|
+
startAngle: 0
|
|
1331
|
+
};
|
|
1332
|
+
class ue {
|
|
1333
|
+
constructor(t, e = {}) {
|
|
1334
|
+
this.config = t, this.imageConfig = e;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Generate spiral layout positions for images
|
|
1338
|
+
* @param imageCount - Number of images to layout
|
|
1339
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1340
|
+
* @param options - Optional overrides (includes fixedHeight)
|
|
1341
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1342
|
+
*/
|
|
1343
|
+
generate(t, e, i = {}) {
|
|
1344
|
+
var M, x, L, G, j, P, U, k, F, I;
|
|
1345
|
+
const o = [], { width: s, height: c } = e, a = { ...ge, ...this.config.spiral }, h = this.config.spacing.padding, l = i.fixedHeight ?? this.config.sizing.base, g = ((M = this.imageConfig.rotation) == null ? void 0 : M.mode) ?? "none", r = ((L = (x = this.imageConfig.rotation) == null ? void 0 : x.range) == null ? void 0 : L.min) ?? -15, d = ((j = (G = this.imageConfig.rotation) == null ? void 0 : G.range) == null ? void 0 : j.max) ?? 15, f = ((U = (P = this.imageConfig.sizing) == null ? void 0 : P.variance) == null ? void 0 : U.min) ?? 1, y = ((F = (k = this.imageConfig.sizing) == null ? void 0 : k.variance) == null ? void 0 : F.max) ?? 1, p = f !== 1 || y !== 1, u = ((I = this.imageConfig.sizing) == null ? void 0 : I.scaleDecay) ?? a.scaleDecay, m = s / 2, w = c / 2, v = Math.min(
|
|
1346
|
+
m - h - l / 2,
|
|
1347
|
+
w - h - l / 2
|
|
1348
|
+
), E = a.direction === "clockwise" ? -1 : 1;
|
|
1349
|
+
for (let A = 0; A < t; A++) {
|
|
1350
|
+
let O, S;
|
|
1351
|
+
if (a.spiralType === "golden")
|
|
1352
|
+
O = A * de * E + a.startAngle, S = this.calculateGoldenRadius(A, t, v, a.tightness);
|
|
1353
|
+
else if (a.spiralType === "archimedean") {
|
|
1354
|
+
const _ = A * 0.5 * a.tightness;
|
|
1355
|
+
O = _ * E + a.startAngle, S = this.calculateArchimedeanRadius(_, t, v, a.tightness);
|
|
1356
|
+
} else {
|
|
1357
|
+
const _ = A * 0.3 * a.tightness;
|
|
1358
|
+
O = _ * E + a.startAngle, S = this.calculateLogarithmicRadius(_, t, v, a.tightness);
|
|
1359
|
+
}
|
|
1360
|
+
const z = m + Math.cos(O) * S, T = w + Math.sin(O) * S, D = S / v, Z = u > 0 ? 1 - D * u * 0.5 : 1, et = p ? this.random(f, y) : 1, W = Z * et, N = l * W, Q = N * 1.5 / 2, B = N / 2, q = h + Q, nt = s - h - Q, V = h + B, C = c - h - B, H = Math.max(q, Math.min(z, nt)), $ = Math.max(V, Math.min(T, C));
|
|
1361
|
+
let Y = 0;
|
|
1362
|
+
if (g === "random") {
|
|
1363
|
+
const _ = O * 180 / Math.PI % 360, tt = this.random(r, d);
|
|
1364
|
+
Y = a.spiralType === "golden" ? tt : _ * 0.1 + tt * 0.9;
|
|
1365
|
+
} else g === "tangent" && (Y = this.calculateSpiralTangent(O, S, a));
|
|
1366
|
+
const K = t - A;
|
|
1367
|
+
o.push({
|
|
1368
|
+
id: A,
|
|
1369
|
+
x: H,
|
|
1370
|
+
y: $,
|
|
1371
|
+
rotation: Y,
|
|
1372
|
+
scale: W,
|
|
1373
|
+
baseSize: N,
|
|
1374
|
+
zIndex: K
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
return o;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Calculate tangent angle for spiral curve at given position
|
|
1381
|
+
* This aligns the image along the spiral's direction of travel
|
|
1382
|
+
*/
|
|
1383
|
+
calculateSpiralTangent(t, e, i) {
|
|
1384
|
+
let o;
|
|
1385
|
+
if (i.spiralType === "golden")
|
|
1386
|
+
o = t + Math.PI / 2;
|
|
1387
|
+
else if (i.spiralType === "archimedean") {
|
|
1388
|
+
const c = 1 / i.tightness, a = Math.atan(e / c);
|
|
1389
|
+
o = t + a;
|
|
1390
|
+
} else {
|
|
1391
|
+
const c = 0.15 / i.tightness, a = Math.atan(1 / c);
|
|
1392
|
+
o = t + a;
|
|
1393
|
+
}
|
|
1394
|
+
return o * 180 / Math.PI % 360 - 90;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Calculate radius for golden spiral (Vogel's model)
|
|
1398
|
+
* Creates even distribution like sunflower seeds
|
|
1399
|
+
*/
|
|
1400
|
+
calculateGoldenRadius(t, e, i, o) {
|
|
1401
|
+
const c = i / Math.sqrt(e) * Math.sqrt(t) / o;
|
|
1402
|
+
return Math.min(c, i);
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Calculate radius for Archimedean spiral
|
|
1406
|
+
* r = a + b*θ (constant spacing between arms)
|
|
1407
|
+
*/
|
|
1408
|
+
calculateArchimedeanRadius(t, e, i, o) {
|
|
1409
|
+
const s = e * 0.5 * o;
|
|
1410
|
+
return t / s * i;
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Calculate radius for logarithmic (equiangular) spiral
|
|
1414
|
+
* r = a * e^(b*θ)
|
|
1415
|
+
*/
|
|
1416
|
+
calculateLogarithmicRadius(t, e, i, o) {
|
|
1417
|
+
const s = i * 0.05, c = 0.15 / o, a = s * Math.exp(c * t), h = e * 0.3 * o, l = s * Math.exp(c * h);
|
|
1418
|
+
return a / l * i;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Utility: Generate random number between min and max
|
|
1422
|
+
*/
|
|
1423
|
+
random(t, e) {
|
|
1424
|
+
return Math.random() * (e - t) + t;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
const me = {
|
|
1428
|
+
clusterCount: "auto",
|
|
1429
|
+
clusterSpread: 150,
|
|
1430
|
+
clusterSpacing: 200,
|
|
1431
|
+
density: "uniform",
|
|
1432
|
+
overlap: 0.3,
|
|
1433
|
+
distribution: "gaussian"
|
|
1434
|
+
};
|
|
1435
|
+
class fe {
|
|
1436
|
+
constructor(t, e = {}) {
|
|
1437
|
+
this.config = t, this.imageConfig = e;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Generate cluster layout positions for images
|
|
1441
|
+
* @param imageCount - Number of images to layout
|
|
1442
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1443
|
+
* @param options - Optional overrides (includes fixedHeight)
|
|
1444
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1445
|
+
*/
|
|
1446
|
+
generate(t, e, i = {}) {
|
|
1447
|
+
var E, M, x, L, G, j, P, U, k;
|
|
1448
|
+
const o = [], { width: s, height: c } = e, a = { ...me, ...this.config.cluster }, h = this.config.spacing.padding, l = i.fixedHeight ?? this.config.sizing.base, g = ((E = this.imageConfig.rotation) == null ? void 0 : E.mode) ?? "none", r = ((x = (M = this.imageConfig.rotation) == null ? void 0 : M.range) == null ? void 0 : x.min) ?? -15, d = ((G = (L = this.imageConfig.rotation) == null ? void 0 : L.range) == null ? void 0 : G.max) ?? 15, f = ((P = (j = this.imageConfig.sizing) == null ? void 0 : j.variance) == null ? void 0 : P.min) ?? 1, y = ((k = (U = this.imageConfig.sizing) == null ? void 0 : U.variance) == null ? void 0 : k.max) ?? 1, p = f !== 1 || y !== 1, u = this.calculateClusterCount(
|
|
1449
|
+
t,
|
|
1450
|
+
a.clusterCount,
|
|
1451
|
+
s,
|
|
1452
|
+
c,
|
|
1453
|
+
a.clusterSpacing
|
|
1454
|
+
), m = this.generateClusterCenters(
|
|
1455
|
+
u,
|
|
1456
|
+
s,
|
|
1457
|
+
c,
|
|
1458
|
+
h,
|
|
1459
|
+
a
|
|
1460
|
+
), w = new Array(u).fill(0);
|
|
1461
|
+
for (let F = 0; F < t; F++)
|
|
1462
|
+
w[F % u]++;
|
|
1463
|
+
let v = 0;
|
|
1464
|
+
for (let F = 0; F < u; F++) {
|
|
1465
|
+
const I = m[F], A = w[F];
|
|
1466
|
+
for (let O = 0; O < A; O++) {
|
|
1467
|
+
let S, z;
|
|
1468
|
+
if (a.distribution === "gaussian")
|
|
1469
|
+
S = this.gaussianRandom() * I.spread, z = this.gaussianRandom() * I.spread;
|
|
1470
|
+
else {
|
|
1471
|
+
const $ = this.random(0, Math.PI * 2), Y = this.random(0, I.spread);
|
|
1472
|
+
S = Math.cos($) * Y, z = Math.sin($) * Y;
|
|
1473
|
+
}
|
|
1474
|
+
const T = 1 + a.overlap * 0.5, D = 1 + a.overlap * 0.3;
|
|
1475
|
+
S /= T, z /= T;
|
|
1476
|
+
const Z = p ? this.random(f, y) : 1, et = D * Z, W = l * et;
|
|
1477
|
+
let N = I.x + S, it = I.y + z;
|
|
1478
|
+
const B = W * 1.5 / 2, q = W / 2;
|
|
1479
|
+
N = Math.max(h + B, Math.min(N, s - h - B)), it = Math.max(h + q, Math.min(it, c - h - q));
|
|
1480
|
+
const nt = g === "random" ? this.random(r, d) : 0, C = Math.sqrt(S * S + z * z) / I.spread, H = Math.round((1 - C) * 50) + 1;
|
|
1481
|
+
o.push({
|
|
1482
|
+
id: v,
|
|
1483
|
+
x: N,
|
|
1484
|
+
y: it,
|
|
1485
|
+
rotation: nt,
|
|
1486
|
+
scale: et,
|
|
1487
|
+
baseSize: W,
|
|
1488
|
+
zIndex: H
|
|
1489
|
+
}), v++;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return o;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Calculate optimal number of clusters based on image count and container
|
|
1496
|
+
*/
|
|
1497
|
+
calculateClusterCount(t, e, i, o, s) {
|
|
1498
|
+
if (e !== "auto")
|
|
1499
|
+
return Math.max(1, Math.min(e, t));
|
|
1500
|
+
const a = Math.max(1, Math.ceil(t / 8)), h = Math.floor(
|
|
1501
|
+
i / s * (o / s) * 0.6
|
|
1502
|
+
);
|
|
1503
|
+
return Math.max(1, Math.min(a, h, 10));
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Generate cluster center positions with spacing constraints
|
|
1507
|
+
*/
|
|
1508
|
+
generateClusterCenters(t, e, i, o, s) {
|
|
1509
|
+
const c = [], h = o + s.clusterSpread, l = e - o - s.clusterSpread, g = o + s.clusterSpread, r = i - o - s.clusterSpread;
|
|
1510
|
+
for (let d = 0; d < t; d++) {
|
|
1511
|
+
let f = null, y = -1;
|
|
1512
|
+
for (let p = 0; p < 100; p++) {
|
|
1513
|
+
const u = {
|
|
1514
|
+
x: this.random(h, l),
|
|
1515
|
+
y: this.random(g, r),
|
|
1516
|
+
spread: this.calculateClusterSpread(s)
|
|
1517
|
+
};
|
|
1518
|
+
let m = 1 / 0;
|
|
1519
|
+
for (const w of c) {
|
|
1520
|
+
const v = u.x - w.x, E = u.y - w.y, M = Math.sqrt(v * v + E * E);
|
|
1521
|
+
m = Math.min(m, M);
|
|
1522
|
+
}
|
|
1523
|
+
if ((c.length === 0 || m > y) && (f = u, y = m), m >= s.clusterSpacing)
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
f && c.push(f);
|
|
1527
|
+
}
|
|
1528
|
+
return c;
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Calculate spread for a cluster (may vary if density='varied')
|
|
1532
|
+
*/
|
|
1533
|
+
calculateClusterSpread(t) {
|
|
1534
|
+
return t.density === "uniform" ? t.clusterSpread : t.clusterSpread * this.random(0.5, 1.5);
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Generate a random number with approximately Gaussian distribution
|
|
1538
|
+
* Using Box-Muller transform
|
|
1539
|
+
*/
|
|
1540
|
+
gaussianRandom() {
|
|
1541
|
+
let t = 0, e = 0;
|
|
1542
|
+
for (; t === 0; ) t = Math.random();
|
|
1543
|
+
for (; e === 0; ) e = Math.random();
|
|
1544
|
+
const i = Math.sqrt(-2 * Math.log(t)) * Math.cos(2 * Math.PI * e);
|
|
1545
|
+
return Math.max(-3, Math.min(3, i)) / 3;
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Utility: Generate random number between min and max
|
|
1549
|
+
*/
|
|
1550
|
+
random(t, e) {
|
|
1551
|
+
return Math.random() * (e - t) + t;
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
class pe {
|
|
1555
|
+
constructor(t, e = {}) {
|
|
1556
|
+
this.config = t, this.imageConfig = e;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Generate wave layout positions for images
|
|
1560
|
+
* @param imageCount - Number of images to layout
|
|
1561
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1562
|
+
* @param options - Optional overrides
|
|
1563
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1564
|
+
*/
|
|
1565
|
+
generate(t, e, i = {}) {
|
|
1566
|
+
var T, D, Z, et, W, N, it, Q, B;
|
|
1567
|
+
const o = [], { width: s, height: c } = e, a = this.config.sizing.base, h = this.config.spacing.padding ?? 50, l = ((T = this.imageConfig.rotation) == null ? void 0 : T.mode) ?? "none", g = ((Z = (D = this.imageConfig.rotation) == null ? void 0 : D.range) == null ? void 0 : Z.min) ?? -15, r = ((W = (et = this.imageConfig.rotation) == null ? void 0 : et.range) == null ? void 0 : W.max) ?? 15, d = ((it = (N = this.imageConfig.sizing) == null ? void 0 : N.variance) == null ? void 0 : it.min) ?? 1, f = ((B = (Q = this.imageConfig.sizing) == null ? void 0 : Q.variance) == null ? void 0 : B.max) ?? 1, y = d !== 1 || f !== 1, p = i.fixedHeight ?? a, u = {
|
|
1568
|
+
...Ht,
|
|
1569
|
+
...this.config.wave
|
|
1570
|
+
}, { rows: m, amplitude: w, frequency: v, phaseShift: E, synchronization: M } = u, x = Math.ceil(t / m), j = p * 1.5 / 2, P = h + j, U = s - h - j, k = U - P, F = x > 1 ? k / (x - 1) : 0, I = h + w + p / 2, A = c - h - w - p / 2, O = A - I, S = m > 1 ? O / (m - 1) : 0;
|
|
1571
|
+
let z = 0;
|
|
1572
|
+
for (let q = 0; q < m && z < t; q++) {
|
|
1573
|
+
const nt = m === 1 ? (I + A) / 2 : I + q * S;
|
|
1574
|
+
let V = 0;
|
|
1575
|
+
M === "offset" ? V = q * E : M === "alternating" && (V = q * Math.PI);
|
|
1576
|
+
for (let C = 0; C < x && z < t; C++) {
|
|
1577
|
+
const H = x === 1 ? (P + U) / 2 : P + C * F, $ = this.calculateWaveY(H, s, w, v, V), Y = H, K = nt + $, _ = y ? this.random(d, f) : 1, tt = p * _;
|
|
1578
|
+
let ot = 0;
|
|
1579
|
+
l === "tangent" ? ot = this.calculateRotation(H, s, w, v, V) : l === "random" && (ot = this.random(g, r));
|
|
1580
|
+
const at = tt * 1.5 / 2, pt = tt / 2, dt = h + at, gt = s - h - at, yt = h + pt, bt = c - h - pt;
|
|
1581
|
+
o.push({
|
|
1582
|
+
id: z,
|
|
1583
|
+
x: Math.max(dt, Math.min(Y, gt)),
|
|
1584
|
+
y: Math.max(yt, Math.min(K, bt)),
|
|
1585
|
+
rotation: ot,
|
|
1586
|
+
scale: _,
|
|
1587
|
+
baseSize: tt,
|
|
1588
|
+
zIndex: z + 1
|
|
1589
|
+
}), z++;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
return o;
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Calculate Y position displacement on wave curve
|
|
1596
|
+
* @param x - Horizontal position
|
|
1597
|
+
* @param containerWidth - Container width
|
|
1598
|
+
* @param amplitude - Wave amplitude
|
|
1599
|
+
* @param frequency - Wave frequency
|
|
1600
|
+
* @param phase - Phase offset
|
|
1601
|
+
* @returns Y displacement from baseline
|
|
1602
|
+
*/
|
|
1603
|
+
calculateWaveY(t, e, i, o, s) {
|
|
1604
|
+
const c = t / e;
|
|
1605
|
+
return i * Math.sin(o * c * 2 * Math.PI + s);
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Calculate rotation based on wave tangent
|
|
1609
|
+
* @param x - Horizontal position
|
|
1610
|
+
* @param containerWidth - Container width
|
|
1611
|
+
* @param amplitude - Wave amplitude
|
|
1612
|
+
* @param frequency - Wave frequency
|
|
1613
|
+
* @param phase - Phase offset
|
|
1614
|
+
* @returns Rotation angle in degrees
|
|
1615
|
+
*/
|
|
1616
|
+
calculateRotation(t, e, i, o, s) {
|
|
1617
|
+
const c = t / e, a = i * o * 2 * Math.PI * Math.cos(o * c * 2 * Math.PI + s) / e;
|
|
1618
|
+
return Math.atan(a) * (180 / Math.PI);
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Estimate image width based on height
|
|
1622
|
+
/**
|
|
1623
|
+
* Utility: Generate random number between min and max
|
|
1624
|
+
* @param min - Minimum value
|
|
1625
|
+
* @param max - Maximum value
|
|
1626
|
+
* @returns Random number in range
|
|
1627
|
+
*/
|
|
1628
|
+
random(t, e) {
|
|
1629
|
+
return Math.random() * (e - t) + t;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
class ye {
|
|
1633
|
+
constructor(t) {
|
|
1634
|
+
this.config = t.layout, this.imageConfig = t.image, this.breakpoints = t.breakpoints ?? { mobile: 768 }, this.layouts = /* @__PURE__ */ new Map(), this.generator = this.initGenerator();
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Initialize the appropriate generator based on config type
|
|
1638
|
+
* @returns Initialized placement generator
|
|
1639
|
+
*/
|
|
1640
|
+
initGenerator() {
|
|
1641
|
+
switch (this.config.algorithm) {
|
|
1642
|
+
case "radial":
|
|
1643
|
+
return new ce(this.config, this.imageConfig);
|
|
1644
|
+
case "grid":
|
|
1645
|
+
return new he(this.config, this.imageConfig);
|
|
1646
|
+
case "spiral":
|
|
1647
|
+
return new ue(this.config, this.imageConfig);
|
|
1648
|
+
case "cluster":
|
|
1649
|
+
return new fe(this.config, this.imageConfig);
|
|
1650
|
+
case "wave":
|
|
1651
|
+
return new pe(this.config, this.imageConfig);
|
|
1652
|
+
case "random":
|
|
1653
|
+
default:
|
|
1654
|
+
return new re(this.config, this.imageConfig);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Generate layout positions for images
|
|
1659
|
+
* @param imageCount - Number of images to layout
|
|
1660
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1661
|
+
* @param options - Optional overrides for configuration (e.g. fixedHeight)
|
|
1662
|
+
* @returns Array of layout objects with position, rotation, scale
|
|
1663
|
+
*/
|
|
1664
|
+
generateLayout(t, e, i = {}) {
|
|
1665
|
+
const o = this.generator.generate(t, e, i);
|
|
1666
|
+
return o.forEach((s) => {
|
|
1667
|
+
this.layouts.set(s.id, s);
|
|
1668
|
+
}), o;
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Get the original layout state for an image
|
|
1672
|
+
* @param imageId - The image ID (number or string)
|
|
1673
|
+
* @returns Original layout state or undefined if not found
|
|
1674
|
+
*/
|
|
1675
|
+
getOriginalState(t) {
|
|
1676
|
+
return this.layouts.get(Number(t));
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Reset all stored layouts
|
|
1680
|
+
*/
|
|
1681
|
+
reset() {
|
|
1682
|
+
this.layouts.clear();
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Update config dynamically (useful for responsive changes)
|
|
1686
|
+
* @param newConfig - Updated configuration
|
|
1687
|
+
*/
|
|
1688
|
+
updateConfig(t) {
|
|
1689
|
+
t.layout && (Object.assign(this.config, t.layout), t.layout.algorithm && t.layout.algorithm !== this.config.algorithm && (this.generator = this.initGenerator())), t.image && Object.assign(this.imageConfig, t.image), t.breakpoints && (this.breakpoints = t.breakpoints);
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Resolve the effective base height based on image config and current viewport
|
|
1693
|
+
* @param viewportWidth - Current viewport width
|
|
1694
|
+
* @returns Resolved base height or undefined if should auto-calculate
|
|
1695
|
+
*/
|
|
1696
|
+
resolveBaseHeight(t) {
|
|
1697
|
+
var o;
|
|
1698
|
+
const e = (o = this.imageConfig.sizing) == null ? void 0 : o.baseHeight;
|
|
1699
|
+
if (e === void 0)
|
|
1700
|
+
return;
|
|
1701
|
+
if (typeof e == "number")
|
|
1702
|
+
return e;
|
|
1703
|
+
const i = e;
|
|
1704
|
+
return t <= this.breakpoints.mobile ? i.mobile ?? i.tablet ?? i.default : this.breakpoints.tablet && t <= this.breakpoints.tablet ? i.tablet ?? i.default : i.default;
|
|
1705
|
+
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Calculate adaptive image size based on container dimensions and image count
|
|
1708
|
+
* @param containerBounds - Container dimensions {width, height}
|
|
1709
|
+
* @param imageCount - Number of images to display
|
|
1710
|
+
* @param responsiveHeight - Current responsive breakpoint height (upper bound)
|
|
1711
|
+
* @param viewportWidth - Current viewport width for baseHeight resolution
|
|
1712
|
+
* @returns Calculated sizing result with height
|
|
1713
|
+
*/
|
|
1714
|
+
calculateAdaptiveSize(t, e, i, o) {
|
|
1715
|
+
const s = this.config.sizing.adaptive, c = this.resolveBaseHeight(o);
|
|
1716
|
+
if (c !== void 0) {
|
|
1717
|
+
let v = Math.min(c, i);
|
|
1718
|
+
return s && (v = this.clamp(v, s.minSize, s.maxSize)), { height: v };
|
|
1719
|
+
}
|
|
1720
|
+
if (!s || !s.enabled)
|
|
1721
|
+
return { height: i };
|
|
1722
|
+
const { width: a, height: h } = t, { minSize: l, maxSize: g } = s, r = this.config.targetCoverage ?? 0.6, d = this.config.densityFactor ?? 1, p = a * h * r / e;
|
|
1723
|
+
let m = Math.sqrt(p / 1.4);
|
|
1724
|
+
m *= d, m = Math.min(m, i);
|
|
1725
|
+
let w = this.clamp(m, l, g);
|
|
1726
|
+
if (w === l && m < l) {
|
|
1727
|
+
const v = Math.max(l * 0.05, 20);
|
|
1728
|
+
w = Math.max(v, m);
|
|
1729
|
+
}
|
|
1730
|
+
return { height: w };
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Utility: Clamp a value between min and max
|
|
1734
|
+
*/
|
|
1735
|
+
clamp(t, e, i) {
|
|
1736
|
+
return Math.max(e, Math.min(i, t));
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
var R = /* @__PURE__ */ ((n) => (n.IDLE = "idle", n.FOCUSING = "focusing", n.FOCUSED = "focused", n.UNFOCUSING = "unfocusing", n.CROSS_ANIMATING = "cross_animating", n))(R || {});
|
|
1740
|
+
function be(n) {
|
|
1741
|
+
return n in St;
|
|
1742
|
+
}
|
|
1743
|
+
function ve(n) {
|
|
1744
|
+
return n ? be(n) ? St[n] : n : St.md;
|
|
1745
|
+
}
|
|
1746
|
+
function we(n) {
|
|
1747
|
+
if (!n) return "";
|
|
1748
|
+
const t = [];
|
|
1749
|
+
if (n.grayscale !== void 0 && t.push(`grayscale(${n.grayscale})`), n.blur !== void 0 && t.push(`blur(${n.blur}px)`), n.brightness !== void 0 && t.push(`brightness(${n.brightness})`), n.contrast !== void 0 && t.push(`contrast(${n.contrast})`), n.saturate !== void 0 && t.push(`saturate(${n.saturate})`), n.opacity !== void 0 && t.push(`opacity(${n.opacity})`), n.sepia !== void 0 && t.push(`sepia(${n.sepia})`), n.hueRotate !== void 0 && t.push(`hue-rotate(${n.hueRotate}deg)`), n.invert !== void 0 && t.push(`invert(${n.invert})`), n.dropShadow !== void 0)
|
|
1750
|
+
if (typeof n.dropShadow == "string")
|
|
1751
|
+
t.push(`drop-shadow(${n.dropShadow})`);
|
|
1752
|
+
else {
|
|
1753
|
+
const e = n.dropShadow;
|
|
1754
|
+
t.push(`drop-shadow(${e.x}px ${e.y}px ${e.blur}px ${e.color})`);
|
|
1755
|
+
}
|
|
1756
|
+
return t.join(" ");
|
|
1757
|
+
}
|
|
1758
|
+
function ct(n) {
|
|
1759
|
+
if (!n || n.style === "none" || n.width === 0)
|
|
1760
|
+
return "none";
|
|
1761
|
+
const t = n.width ?? 0, e = n.style ?? "solid", i = n.color ?? "#000000";
|
|
1762
|
+
return `${t}px ${e} ${i}`;
|
|
1763
|
+
}
|
|
1764
|
+
function mt(n) {
|
|
1765
|
+
var s, c;
|
|
1766
|
+
if (!n) return {};
|
|
1767
|
+
const t = {};
|
|
1768
|
+
if (n.borderRadiusTopLeft !== void 0 || n.borderRadiusTopRight !== void 0 || n.borderRadiusBottomRight !== void 0 || n.borderRadiusBottomLeft !== void 0) {
|
|
1769
|
+
const a = ((s = n.border) == null ? void 0 : s.radius) ?? 0;
|
|
1770
|
+
n.borderRadiusTopLeft !== void 0 ? t.borderTopLeftRadius = `${n.borderRadiusTopLeft}px` : a && (t.borderTopLeftRadius = `${a}px`), n.borderRadiusTopRight !== void 0 ? t.borderTopRightRadius = `${n.borderRadiusTopRight}px` : a && (t.borderTopRightRadius = `${a}px`), n.borderRadiusBottomRight !== void 0 ? t.borderBottomRightRadius = `${n.borderRadiusBottomRight}px` : a && (t.borderBottomRightRadius = `${a}px`), n.borderRadiusBottomLeft !== void 0 ? t.borderBottomLeftRadius = `${n.borderRadiusBottomLeft}px` : a && (t.borderBottomLeftRadius = `${a}px`);
|
|
1771
|
+
} else ((c = n.border) == null ? void 0 : c.radius) !== void 0 && (t.borderRadius = `${n.border.radius}px`);
|
|
1772
|
+
if (n.borderTop || n.borderRight || n.borderBottom || n.borderLeft) {
|
|
1773
|
+
const a = n.border || {}, h = { ...a, ...n.borderTop }, l = { ...a, ...n.borderRight }, g = { ...a, ...n.borderBottom }, r = { ...a, ...n.borderLeft };
|
|
1774
|
+
t.borderTop = ct(h), t.borderRight = ct(l), t.borderBottom = ct(g), t.borderLeft = ct(r);
|
|
1775
|
+
} else n.border && (t.border = ct(n.border));
|
|
1776
|
+
n.shadow !== void 0 && (t.boxShadow = ve(n.shadow));
|
|
1777
|
+
const o = we(n.filter);
|
|
1778
|
+
if (o && (t.filter = o), n.opacity !== void 0 && (t.opacity = String(n.opacity)), n.cursor !== void 0 && (t.cursor = n.cursor), n.outline && n.outline.style !== "none" && (n.outline.width ?? 0) > 0) {
|
|
1779
|
+
const a = n.outline.width ?? 0, h = n.outline.style ?? "solid", l = n.outline.color ?? "#000000";
|
|
1780
|
+
t.outline = `${a}px ${h} ${l}`, n.outline.offset !== void 0 && (t.outlineOffset = `${n.outline.offset}px`);
|
|
1781
|
+
}
|
|
1782
|
+
return n.objectFit !== void 0 && (t.objectFit = n.objectFit), n.aspectRatio !== void 0 && (t.aspectRatio = n.aspectRatio), t;
|
|
1783
|
+
}
|
|
1784
|
+
function lt(n, t) {
|
|
1785
|
+
t.borderRadius !== void 0 && (n.style.borderRadius = t.borderRadius), t.borderTopLeftRadius !== void 0 && (n.style.borderTopLeftRadius = t.borderTopLeftRadius), t.borderTopRightRadius !== void 0 && (n.style.borderTopRightRadius = t.borderTopRightRadius), t.borderBottomRightRadius !== void 0 && (n.style.borderBottomRightRadius = t.borderBottomRightRadius), t.borderBottomLeftRadius !== void 0 && (n.style.borderBottomLeftRadius = t.borderBottomLeftRadius), t.border !== void 0 && (n.style.border = t.border), t.borderTop !== void 0 && (n.style.borderTop = t.borderTop), t.borderRight !== void 0 && (n.style.borderRight = t.borderRight), t.borderBottom !== void 0 && (n.style.borderBottom = t.borderBottom), t.borderLeft !== void 0 && (n.style.borderLeft = t.borderLeft), t.boxShadow !== void 0 && (n.style.boxShadow = t.boxShadow), t.filter !== void 0 && (n.style.filter = t.filter), t.opacity !== void 0 && (n.style.opacity = t.opacity), t.cursor !== void 0 && (n.style.cursor = t.cursor), t.outline !== void 0 && (n.style.outline = t.outline), t.outlineOffset !== void 0 && (n.style.outlineOffset = t.outlineOffset), t.objectFit !== void 0 && (n.style.objectFit = t.objectFit), t.aspectRatio !== void 0 && (n.style.aspectRatio = t.aspectRatio);
|
|
1786
|
+
}
|
|
1787
|
+
function Ot(n) {
|
|
1788
|
+
return n ? Array.isArray(n) ? n.join(" ") : n : "";
|
|
1789
|
+
}
|
|
1790
|
+
function ht(n, t) {
|
|
1791
|
+
const e = Ot(t);
|
|
1792
|
+
e && e.split(" ").forEach((i) => {
|
|
1793
|
+
i.trim() && n.classList.add(i.trim());
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
function Dt(n, t) {
|
|
1797
|
+
const e = Ot(t);
|
|
1798
|
+
e && e.split(" ").forEach((i) => {
|
|
1799
|
+
i.trim() && n.classList.remove(i.trim());
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
const Ct = {
|
|
1803
|
+
UNFOCUSING: 999,
|
|
1804
|
+
FOCUSING: 1e3
|
|
1805
|
+
};
|
|
1806
|
+
class xe {
|
|
1807
|
+
constructor(t, e, i) {
|
|
1808
|
+
var o, s;
|
|
1809
|
+
this.state = R.IDLE, this.currentFocus = null, this.focusData = null, this.outgoing = null, this.incoming = null, this.focusGeneration = 0, this.config = t, this.animationEngine = e, this.defaultStyles = mt(i == null ? void 0 : i.default), this.focusedStyles = mt(i == null ? void 0 : i.focused), this.defaultClassName = (o = i == null ? void 0 : i.default) == null ? void 0 : o.className, this.focusedClassName = (s = i == null ? void 0 : i.focused) == null ? void 0 : s.className;
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Get current state machine state
|
|
1813
|
+
*/
|
|
1814
|
+
getState() {
|
|
1815
|
+
return this.state;
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Check if any animation is in progress
|
|
1819
|
+
*/
|
|
1820
|
+
isAnimating() {
|
|
1821
|
+
return this.state !== R.IDLE && this.state !== R.FOCUSED;
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Normalize scalePercent value
|
|
1825
|
+
*/
|
|
1826
|
+
normalizeScalePercent(t) {
|
|
1827
|
+
return t > 1 ? t / 100 : t;
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Calculate target dimensions for focused image
|
|
1831
|
+
* Returns actual pixel dimensions instead of scale factor for sharper rendering
|
|
1832
|
+
*/
|
|
1833
|
+
calculateFocusDimensions(t, e, i) {
|
|
1834
|
+
const o = this.normalizeScalePercent(this.config.scalePercent), s = i.height * o, c = t / e;
|
|
1835
|
+
let a = s, h = a * c;
|
|
1836
|
+
const l = i.width * o;
|
|
1837
|
+
return h > l && (h = l, a = h / c), { width: h, height: a };
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Calculate the transform needed to center an image (position only, no scale)
|
|
1841
|
+
* Scale is handled by animating actual dimensions for sharper rendering
|
|
1842
|
+
*/
|
|
1843
|
+
calculateFocusTransform(t, e) {
|
|
1844
|
+
const i = t.width / 2, o = t.height / 2, s = i - e.x, c = o - e.y;
|
|
1845
|
+
return {
|
|
1846
|
+
x: s,
|
|
1847
|
+
y: c,
|
|
1848
|
+
rotation: 0,
|
|
1849
|
+
scale: 1
|
|
1850
|
+
// No scale transform - dimensions are animated instead
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Build transform string for dimension-based zoom (no scale in transform)
|
|
1855
|
+
*/
|
|
1856
|
+
buildDimensionZoomTransform(t) {
|
|
1857
|
+
const e = ["translate(-50%, -50%)"];
|
|
1858
|
+
if (t.x !== void 0 || t.y !== void 0) {
|
|
1859
|
+
const i = t.x ?? 0, o = t.y ?? 0;
|
|
1860
|
+
e.push(`translate(${i}px, ${o}px)`);
|
|
1861
|
+
}
|
|
1862
|
+
return t.rotation !== void 0 && e.push(`rotate(${t.rotation}deg)`), e.join(" ");
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Create a Web Animation that animates both transform (position) and dimensions
|
|
1866
|
+
* This provides sharper zoom by re-rendering at target size instead of scaling pixels
|
|
1867
|
+
*/
|
|
1868
|
+
animateWithDimensions(t, e, i, o, s, c, a, h) {
|
|
1869
|
+
const l = this.buildDimensionZoomTransform(e), g = this.buildDimensionZoomTransform(i);
|
|
1870
|
+
return t.style.transition = "none", t.animate(
|
|
1871
|
+
[
|
|
1872
|
+
{
|
|
1873
|
+
transform: l,
|
|
1874
|
+
width: `${o}px`,
|
|
1875
|
+
height: `${s}px`
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
transform: g,
|
|
1879
|
+
width: `${c}px`,
|
|
1880
|
+
height: `${a}px`
|
|
1881
|
+
}
|
|
1882
|
+
],
|
|
1883
|
+
{
|
|
1884
|
+
duration: h,
|
|
1885
|
+
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
1886
|
+
fill: "forwards"
|
|
1887
|
+
}
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Apply focused styling to an element
|
|
1892
|
+
*/
|
|
1893
|
+
applyFocusedStyling(t, e) {
|
|
1894
|
+
t.style.zIndex = String(e), t.classList.add("fbn-ic-focused"), lt(t, this.focusedStyles), ht(t, this.focusedClassName);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Remove focused styling from an element
|
|
1898
|
+
*/
|
|
1899
|
+
removeFocusedStyling(t, e) {
|
|
1900
|
+
t.style.zIndex = e, t.classList.remove("fbn-ic-focused"), Dt(t, this.focusedClassName), lt(t, this.defaultStyles), ht(t, this.defaultClassName);
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Start focus animation for an image using dimension-based zoom
|
|
1904
|
+
* Animates actual width/height for sharper rendering instead of transform scale
|
|
1905
|
+
* @param fromTransform - Optional starting transform (for mid-animation reversals)
|
|
1906
|
+
* @param fromDimensions - Optional starting dimensions (for mid-animation reversals)
|
|
1907
|
+
*/
|
|
1908
|
+
startFocusAnimation(t, e, i, o, s) {
|
|
1909
|
+
const c = t.style.zIndex || "", a = t.offsetWidth, h = t.offsetHeight, l = this.calculateFocusDimensions(a, h, e), g = this.calculateFocusTransform(e, i);
|
|
1910
|
+
this.applyFocusedStyling(t, Ct.FOCUSING), this.animationEngine.cancelAllAnimations(t);
|
|
1911
|
+
const r = o ?? {
|
|
1912
|
+
x: 0,
|
|
1913
|
+
y: 0,
|
|
1914
|
+
rotation: i.rotation,
|
|
1915
|
+
scale: 1
|
|
1916
|
+
// No scale - using dimensions
|
|
1917
|
+
}, d = (s == null ? void 0 : s.width) ?? a, f = (s == null ? void 0 : s.height) ?? h, y = this.config.animationDuration ?? 600, p = this.animateWithDimensions(
|
|
1918
|
+
t,
|
|
1919
|
+
r,
|
|
1920
|
+
g,
|
|
1921
|
+
d,
|
|
1922
|
+
f,
|
|
1923
|
+
l.width,
|
|
1924
|
+
l.height,
|
|
1925
|
+
y
|
|
1926
|
+
), u = {
|
|
1927
|
+
id: `focus-${Date.now()}`,
|
|
1928
|
+
element: t,
|
|
1929
|
+
animation: p,
|
|
1930
|
+
fromState: r,
|
|
1931
|
+
toState: g,
|
|
1932
|
+
startTime: performance.now(),
|
|
1933
|
+
duration: y
|
|
1934
|
+
};
|
|
1935
|
+
return this.focusData = {
|
|
1936
|
+
element: t,
|
|
1937
|
+
originalState: i,
|
|
1938
|
+
focusTransform: g,
|
|
1939
|
+
originalZIndex: c,
|
|
1940
|
+
originalWidth: a,
|
|
1941
|
+
originalHeight: h,
|
|
1942
|
+
focusWidth: l.width,
|
|
1943
|
+
focusHeight: l.height
|
|
1944
|
+
}, {
|
|
1945
|
+
element: t,
|
|
1946
|
+
originalState: i,
|
|
1947
|
+
animationHandle: u,
|
|
1948
|
+
direction: "in",
|
|
1949
|
+
originalWidth: a,
|
|
1950
|
+
originalHeight: h
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Start unfocus animation for an image using dimension-based zoom
|
|
1955
|
+
* Animates back to original dimensions for consistent behavior
|
|
1956
|
+
* @param fromDimensions - Optional starting dimensions (for mid-animation reversals)
|
|
1957
|
+
*/
|
|
1958
|
+
startUnfocusAnimation(t, e, i, o) {
|
|
1959
|
+
var y, p, u, m, w;
|
|
1960
|
+
t.style.zIndex = String(Ct.UNFOCUSING), this.animationEngine.cancelAllAnimations(t);
|
|
1961
|
+
const s = i ?? ((y = this.focusData) == null ? void 0 : y.focusTransform) ?? { x: 0, y: 0, rotation: 0, scale: 1 }, c = (o == null ? void 0 : o.width) ?? ((p = this.focusData) == null ? void 0 : p.focusWidth) ?? t.offsetWidth, a = (o == null ? void 0 : o.height) ?? ((u = this.focusData) == null ? void 0 : u.focusHeight) ?? t.offsetHeight, h = {
|
|
1962
|
+
x: 0,
|
|
1963
|
+
y: 0,
|
|
1964
|
+
rotation: e.rotation,
|
|
1965
|
+
scale: 1
|
|
1966
|
+
// No scale - using dimensions
|
|
1967
|
+
}, l = ((m = this.focusData) == null ? void 0 : m.originalWidth) ?? t.offsetWidth, g = ((w = this.focusData) == null ? void 0 : w.originalHeight) ?? t.offsetHeight, r = this.config.animationDuration ?? 600, d = this.animateWithDimensions(
|
|
1968
|
+
t,
|
|
1969
|
+
s,
|
|
1970
|
+
h,
|
|
1971
|
+
c,
|
|
1972
|
+
a,
|
|
1973
|
+
l,
|
|
1974
|
+
g,
|
|
1975
|
+
r
|
|
1976
|
+
), f = {
|
|
1977
|
+
id: `unfocus-${Date.now()}`,
|
|
1978
|
+
element: t,
|
|
1979
|
+
animation: d,
|
|
1980
|
+
fromState: s,
|
|
1981
|
+
toState: h,
|
|
1982
|
+
startTime: performance.now(),
|
|
1983
|
+
duration: r
|
|
1984
|
+
};
|
|
1985
|
+
return {
|
|
1986
|
+
element: t,
|
|
1987
|
+
originalState: e,
|
|
1988
|
+
animationHandle: f,
|
|
1989
|
+
direction: "out",
|
|
1990
|
+
originalWidth: l,
|
|
1991
|
+
originalHeight: g
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
/**
|
|
1995
|
+
* Handle animation completion
|
|
1996
|
+
*/
|
|
1997
|
+
async waitForAnimation(t) {
|
|
1998
|
+
try {
|
|
1999
|
+
await t.animation.finished;
|
|
2000
|
+
} catch {
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Reset an element instantly to its original position and dimensions (no animation)
|
|
2005
|
+
*/
|
|
2006
|
+
resetElementInstantly(t, e, i, o, s) {
|
|
2007
|
+
this.animationEngine.cancelAllAnimations(t);
|
|
2008
|
+
const c = ["translate(-50%, -50%)"];
|
|
2009
|
+
c.push("translate(0px, 0px)"), c.push(`rotate(${e.rotation}deg)`), t.style.transition = "none", t.style.transform = c.join(" "), o !== void 0 && s !== void 0 && (t.style.width = `${o}px`, t.style.height = `${s}px`), this.removeFocusedStyling(t, i);
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Focus (zoom) an image to center of container
|
|
2013
|
+
* Implements cross-animation when swapping focus
|
|
2014
|
+
*/
|
|
2015
|
+
async focusImage(t, e, i) {
|
|
2016
|
+
var s, c, a, h, l, g, r, d, f, y, p, u;
|
|
2017
|
+
if (this.currentFocus === t && this.state === R.FOCUSED)
|
|
2018
|
+
return this.unfocusImage();
|
|
2019
|
+
if (((s = this.incoming) == null ? void 0 : s.element) === t && this.state === R.FOCUSING) {
|
|
2020
|
+
const m = this.animationEngine.cancelAnimation(this.incoming.animationHandle, !0), w = {
|
|
2021
|
+
x: m.x,
|
|
2022
|
+
y: m.y,
|
|
2023
|
+
rotation: m.rotation,
|
|
2024
|
+
scale: 1
|
|
2025
|
+
// No scale transform - using dimensions
|
|
2026
|
+
}, v = {
|
|
2027
|
+
width: t.offsetWidth,
|
|
2028
|
+
height: t.offsetHeight
|
|
2029
|
+
};
|
|
2030
|
+
this.outgoing = this.startUnfocusAnimation(
|
|
2031
|
+
t,
|
|
2032
|
+
this.incoming.originalState,
|
|
2033
|
+
w,
|
|
2034
|
+
v
|
|
2035
|
+
), this.incoming = null, this.state = R.UNFOCUSING, await this.waitForAnimation(this.outgoing.animationHandle), this.removeFocusedStyling(this.outgoing.element, ((c = this.focusData) == null ? void 0 : c.originalZIndex) || ""), this.outgoing = null, this.currentFocus = null, this.focusData = null, this.state = R.IDLE;
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
const o = ++this.focusGeneration;
|
|
2039
|
+
switch (this.state) {
|
|
2040
|
+
case R.IDLE:
|
|
2041
|
+
if (this.state = R.FOCUSING, this.incoming = this.startFocusAnimation(t, e, i), await this.waitForAnimation(this.incoming.animationHandle), this.focusGeneration !== o) return;
|
|
2042
|
+
this.currentFocus = t, this.incoming = null, this.state = R.FOCUSED;
|
|
2043
|
+
break;
|
|
2044
|
+
case R.FOCUSED:
|
|
2045
|
+
if (this.state = R.CROSS_ANIMATING, this.currentFocus && this.focusData && (this.outgoing = this.startUnfocusAnimation(
|
|
2046
|
+
this.currentFocus,
|
|
2047
|
+
this.focusData.originalState
|
|
2048
|
+
)), this.incoming = this.startFocusAnimation(t, e, i), await Promise.all([
|
|
2049
|
+
this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),
|
|
2050
|
+
this.waitForAnimation(this.incoming.animationHandle)
|
|
2051
|
+
]), this.focusGeneration !== o)
|
|
2052
|
+
return;
|
|
2053
|
+
this.outgoing && (this.removeFocusedStyling(this.outgoing.element, ((a = this.outgoing.originalState.zIndex) == null ? void 0 : a.toString()) || ""), this.outgoing = null), this.currentFocus = t, this.incoming = null, this.state = R.FOCUSED;
|
|
2054
|
+
break;
|
|
2055
|
+
case R.FOCUSING:
|
|
2056
|
+
if (this.incoming && (this.animationEngine.cancelAnimation(this.incoming.animationHandle, !1), this.resetElementInstantly(
|
|
2057
|
+
this.incoming.element,
|
|
2058
|
+
this.incoming.originalState,
|
|
2059
|
+
((h = this.focusData) == null ? void 0 : h.originalZIndex) || "",
|
|
2060
|
+
(l = this.focusData) == null ? void 0 : l.originalWidth,
|
|
2061
|
+
(g = this.focusData) == null ? void 0 : g.originalHeight
|
|
2062
|
+
), this.incoming = null), this.incoming = this.startFocusAnimation(t, e, i), await this.waitForAnimation(this.incoming.animationHandle), this.focusGeneration !== o) return;
|
|
2063
|
+
this.currentFocus = t, this.incoming = null, this.state = R.FOCUSED;
|
|
2064
|
+
break;
|
|
2065
|
+
case R.UNFOCUSING:
|
|
2066
|
+
if (this.state = R.CROSS_ANIMATING, this.incoming = this.startFocusAnimation(t, e, i), await Promise.all([
|
|
2067
|
+
this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),
|
|
2068
|
+
this.waitForAnimation(this.incoming.animationHandle)
|
|
2069
|
+
]), this.focusGeneration !== o) return;
|
|
2070
|
+
this.outgoing && (this.removeFocusedStyling(this.outgoing.element, ((r = this.outgoing.originalState.zIndex) == null ? void 0 : r.toString()) || ""), this.outgoing = null), this.currentFocus = t, this.incoming = null, this.state = R.FOCUSED;
|
|
2071
|
+
break;
|
|
2072
|
+
case R.CROSS_ANIMATING:
|
|
2073
|
+
if (((d = this.incoming) == null ? void 0 : d.element) === t)
|
|
2074
|
+
return;
|
|
2075
|
+
if (((f = this.outgoing) == null ? void 0 : f.element) === t) {
|
|
2076
|
+
const m = this.animationEngine.cancelAnimation(this.outgoing.animationHandle, !0), w = {
|
|
2077
|
+
x: m.x,
|
|
2078
|
+
y: m.y,
|
|
2079
|
+
rotation: m.rotation,
|
|
2080
|
+
scale: 1
|
|
2081
|
+
// No scale - using dimensions
|
|
2082
|
+
}, v = {
|
|
2083
|
+
width: t.offsetWidth,
|
|
2084
|
+
height: t.offsetHeight
|
|
2085
|
+
};
|
|
2086
|
+
if (this.incoming) {
|
|
2087
|
+
const E = this.animationEngine.cancelAnimation(this.incoming.animationHandle, !0), M = {
|
|
2088
|
+
x: E.x,
|
|
2089
|
+
y: E.y,
|
|
2090
|
+
rotation: E.rotation,
|
|
2091
|
+
scale: 1
|
|
2092
|
+
// No scale - using dimensions
|
|
2093
|
+
}, x = {
|
|
2094
|
+
width: this.incoming.element.offsetWidth,
|
|
2095
|
+
height: this.incoming.element.offsetHeight
|
|
2096
|
+
};
|
|
2097
|
+
this.outgoing = this.startUnfocusAnimation(
|
|
2098
|
+
this.incoming.element,
|
|
2099
|
+
this.incoming.originalState,
|
|
2100
|
+
M,
|
|
2101
|
+
x
|
|
2102
|
+
);
|
|
2103
|
+
} else
|
|
2104
|
+
this.outgoing = null;
|
|
2105
|
+
if (this.incoming = this.startFocusAnimation(t, e, i, w, v), await Promise.all([
|
|
2106
|
+
this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),
|
|
2107
|
+
this.waitForAnimation(this.incoming.animationHandle)
|
|
2108
|
+
]), this.focusGeneration !== o) return;
|
|
2109
|
+
this.outgoing && (this.removeFocusedStyling(this.outgoing.element, ((y = this.outgoing.originalState.zIndex) == null ? void 0 : y.toString()) || ""), this.outgoing = null), this.currentFocus = t, this.incoming = null, this.state = R.FOCUSED;
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
if (this.outgoing && (this.animationEngine.cancelAnimation(this.outgoing.animationHandle, !1), this.resetElementInstantly(
|
|
2113
|
+
this.outgoing.element,
|
|
2114
|
+
this.outgoing.originalState,
|
|
2115
|
+
((p = this.outgoing.originalState.zIndex) == null ? void 0 : p.toString()) || "",
|
|
2116
|
+
this.outgoing.originalWidth,
|
|
2117
|
+
this.outgoing.originalHeight
|
|
2118
|
+
), this.outgoing = null), this.incoming) {
|
|
2119
|
+
const m = this.animationEngine.cancelAnimation(this.incoming.animationHandle, !0), w = {
|
|
2120
|
+
x: m.x,
|
|
2121
|
+
y: m.y,
|
|
2122
|
+
rotation: m.rotation,
|
|
2123
|
+
scale: 1
|
|
2124
|
+
// No scale - using dimensions
|
|
2125
|
+
}, v = {
|
|
2126
|
+
width: this.incoming.element.offsetWidth,
|
|
2127
|
+
height: this.incoming.element.offsetHeight
|
|
2128
|
+
};
|
|
2129
|
+
this.outgoing = this.startUnfocusAnimation(
|
|
2130
|
+
this.incoming.element,
|
|
2131
|
+
this.incoming.originalState,
|
|
2132
|
+
w,
|
|
2133
|
+
v
|
|
2134
|
+
);
|
|
2135
|
+
}
|
|
2136
|
+
if (this.incoming = this.startFocusAnimation(t, e, i), await Promise.all([
|
|
2137
|
+
this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),
|
|
2138
|
+
this.waitForAnimation(this.incoming.animationHandle)
|
|
2139
|
+
]), this.focusGeneration !== o) return;
|
|
2140
|
+
this.outgoing && (this.removeFocusedStyling(this.outgoing.element, ((u = this.outgoing.originalState.zIndex) == null ? void 0 : u.toString()) || ""), this.outgoing = null), this.currentFocus = t, this.incoming = null, this.state = R.FOCUSED;
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Unfocus current image, returning it to original position
|
|
2146
|
+
*/
|
|
2147
|
+
async unfocusImage() {
|
|
2148
|
+
var s, c, a;
|
|
2149
|
+
const t = ++this.focusGeneration;
|
|
2150
|
+
if (!this.currentFocus || !this.focusData) {
|
|
2151
|
+
if (this.incoming && this.state === R.FOCUSING) {
|
|
2152
|
+
const h = this.animationEngine.cancelAnimation(this.incoming.animationHandle, !0), l = {
|
|
2153
|
+
x: h.x,
|
|
2154
|
+
y: h.y,
|
|
2155
|
+
rotation: h.rotation,
|
|
2156
|
+
scale: 1
|
|
2157
|
+
// No scale - using dimensions
|
|
2158
|
+
}, g = {
|
|
2159
|
+
width: this.incoming.element.offsetWidth,
|
|
2160
|
+
height: this.incoming.element.offsetHeight
|
|
2161
|
+
};
|
|
2162
|
+
if (this.outgoing = this.startUnfocusAnimation(
|
|
2163
|
+
this.incoming.element,
|
|
2164
|
+
this.incoming.originalState,
|
|
2165
|
+
l,
|
|
2166
|
+
g
|
|
2167
|
+
), this.incoming = null, this.state = R.UNFOCUSING, await this.waitForAnimation(this.outgoing.animationHandle), this.focusGeneration !== t) return;
|
|
2168
|
+
this.removeFocusedStyling(this.outgoing.element, ((s = this.focusData) == null ? void 0 : s.originalZIndex) || ""), this.outgoing = null, this.focusData = null, this.state = R.IDLE;
|
|
2169
|
+
}
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
if (this.state === R.CROSS_ANIMATING && this.incoming) {
|
|
2173
|
+
const h = this.animationEngine.cancelAnimation(this.incoming.animationHandle, !0), l = {
|
|
2174
|
+
x: h.x,
|
|
2175
|
+
y: h.y,
|
|
2176
|
+
rotation: h.rotation,
|
|
2177
|
+
scale: 1
|
|
2178
|
+
// No scale - using dimensions
|
|
2179
|
+
}, g = {
|
|
2180
|
+
width: this.incoming.element.offsetWidth,
|
|
2181
|
+
height: this.incoming.element.offsetHeight
|
|
2182
|
+
}, r = this.startUnfocusAnimation(
|
|
2183
|
+
this.incoming.element,
|
|
2184
|
+
this.incoming.originalState,
|
|
2185
|
+
l,
|
|
2186
|
+
g
|
|
2187
|
+
);
|
|
2188
|
+
if (await Promise.all([
|
|
2189
|
+
this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),
|
|
2190
|
+
this.waitForAnimation(r.animationHandle)
|
|
2191
|
+
]), this.focusGeneration !== t) return;
|
|
2192
|
+
this.outgoing && this.removeFocusedStyling(this.outgoing.element, ((c = this.outgoing.originalState.zIndex) == null ? void 0 : c.toString()) || ""), this.removeFocusedStyling(r.element, ((a = this.incoming.originalState.zIndex) == null ? void 0 : a.toString()) || ""), this.outgoing = null, this.incoming = null, this.currentFocus = null, this.focusData = null, this.state = R.IDLE;
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
this.state = R.UNFOCUSING;
|
|
2196
|
+
const e = this.currentFocus, i = this.focusData.originalState, o = this.focusData.originalZIndex;
|
|
2197
|
+
this.outgoing = this.startUnfocusAnimation(e, i), await this.waitForAnimation(this.outgoing.animationHandle), this.focusGeneration === t && (this.removeFocusedStyling(e, o), this.outgoing = null, this.currentFocus = null, this.focusData = null, this.state = R.IDLE);
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Swap focus from current image to a new one (alias for focusImage with cross-animation)
|
|
2201
|
+
*/
|
|
2202
|
+
async swapFocus(t, e, i) {
|
|
2203
|
+
return this.focusImage(t, e, i);
|
|
2204
|
+
}
|
|
2205
|
+
/**
|
|
2206
|
+
* Get currently focused image element
|
|
2207
|
+
*/
|
|
2208
|
+
getCurrentFocus() {
|
|
2209
|
+
return this.currentFocus;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Check if an image is currently focused (stable state)
|
|
2213
|
+
*/
|
|
2214
|
+
isFocused(t) {
|
|
2215
|
+
return this.currentFocus === t && this.state === R.FOCUSED;
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Check if an image is the target of current focus animation
|
|
2219
|
+
*/
|
|
2220
|
+
isTargetingFocus(t) {
|
|
2221
|
+
var e;
|
|
2222
|
+
return ((e = this.incoming) == null ? void 0 : e.element) === t;
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Check if an image is involved in any focus/animation state
|
|
2226
|
+
* Returns true if the image is focused, animating in, or animating out
|
|
2227
|
+
* Useful for hover state management - don't apply hover to animating images
|
|
2228
|
+
*/
|
|
2229
|
+
isInvolved(t) {
|
|
2230
|
+
var e, i;
|
|
2231
|
+
return this.currentFocus === t || ((e = this.incoming) == null ? void 0 : e.element) === t || ((i = this.outgoing) == null ? void 0 : i.element) === t;
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Reset zoom state (cancels all animations)
|
|
2235
|
+
*/
|
|
2236
|
+
reset() {
|
|
2237
|
+
var t, e, i, o;
|
|
2238
|
+
this.outgoing && (this.animationEngine.cancelAnimation(this.outgoing.animationHandle, !1), this.resetElementInstantly(
|
|
2239
|
+
this.outgoing.element,
|
|
2240
|
+
this.outgoing.originalState,
|
|
2241
|
+
((t = this.outgoing.originalState.zIndex) == null ? void 0 : t.toString()) || "",
|
|
2242
|
+
this.outgoing.originalWidth,
|
|
2243
|
+
this.outgoing.originalHeight
|
|
2244
|
+
)), this.incoming && (this.animationEngine.cancelAnimation(this.incoming.animationHandle, !1), this.resetElementInstantly(
|
|
2245
|
+
this.incoming.element,
|
|
2246
|
+
this.incoming.originalState,
|
|
2247
|
+
((e = this.focusData) == null ? void 0 : e.originalZIndex) || "",
|
|
2248
|
+
(i = this.focusData) == null ? void 0 : i.originalWidth,
|
|
2249
|
+
(o = this.focusData) == null ? void 0 : o.originalHeight
|
|
2250
|
+
)), this.currentFocus && this.focusData && this.resetElementInstantly(
|
|
2251
|
+
this.currentFocus,
|
|
2252
|
+
this.focusData.originalState,
|
|
2253
|
+
this.focusData.originalZIndex,
|
|
2254
|
+
this.focusData.originalWidth,
|
|
2255
|
+
this.focusData.originalHeight
|
|
2256
|
+
), this.state = R.IDLE, this.currentFocus = null, this.focusData = null, this.outgoing = null, this.incoming = null;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
class Se {
|
|
2260
|
+
constructor(t = {}) {
|
|
2261
|
+
if (this._prepared = !1, this._discoveredUrls = [], this.apiKey = t.apiKey ?? "", this.apiEndpoint = t.apiEndpoint ?? "https://www.googleapis.com/drive/v3/files", this.debugLogging = t.debugLogging ?? !1, this.sources = t.sources ?? [], !this.sources || this.sources.length === 0)
|
|
2262
|
+
throw new Error("GoogleDriveLoader requires at least one source to be configured");
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* Prepare the loader by discovering all images from configured sources
|
|
2266
|
+
* @param filter - Filter to apply to discovered images
|
|
2267
|
+
*/
|
|
2268
|
+
async prepare(t) {
|
|
2269
|
+
this._discoveredUrls = [];
|
|
2270
|
+
for (const e of this.sources)
|
|
2271
|
+
if (e.type === "folder")
|
|
2272
|
+
for (const i of e.folders) {
|
|
2273
|
+
const o = e.recursive !== void 0 ? e.recursive : !0, s = await this.loadFromFolder(i, t, o);
|
|
2274
|
+
this._discoveredUrls.push(...s);
|
|
2275
|
+
}
|
|
2276
|
+
else if (e.type === "files") {
|
|
2277
|
+
const i = await this.loadFiles(e.files, t);
|
|
2278
|
+
this._discoveredUrls.push(...i);
|
|
2279
|
+
}
|
|
2280
|
+
this._prepared = !0;
|
|
2281
|
+
}
|
|
2282
|
+
/**
|
|
2283
|
+
* Get the number of discovered images
|
|
2284
|
+
* @throws Error if called before prepare()
|
|
2285
|
+
*/
|
|
2286
|
+
imagesLength() {
|
|
2287
|
+
if (!this._prepared)
|
|
2288
|
+
throw new Error("GoogleDriveLoader.imagesLength() called before prepare()");
|
|
2289
|
+
return this._discoveredUrls.length;
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Get the ordered list of image URLs
|
|
2293
|
+
* @throws Error if called before prepare()
|
|
2294
|
+
*/
|
|
2295
|
+
imageURLs() {
|
|
2296
|
+
if (!this._prepared)
|
|
2297
|
+
throw new Error("GoogleDriveLoader.imageURLs() called before prepare()");
|
|
2298
|
+
return [...this._discoveredUrls];
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* Check if the loader has been prepared
|
|
2302
|
+
*/
|
|
2303
|
+
isPrepared() {
|
|
2304
|
+
return this._prepared;
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* Extract folder ID from various Google Drive URL formats
|
|
2308
|
+
* @param folderUrl - Google Drive folder URL
|
|
2309
|
+
* @returns Folder ID or null if invalid
|
|
2310
|
+
*/
|
|
2311
|
+
extractFolderId(t) {
|
|
2312
|
+
const e = [
|
|
2313
|
+
/\/folders\/([a-zA-Z0-9_-]+)/,
|
|
2314
|
+
// Standard format
|
|
2315
|
+
/id=([a-zA-Z0-9_-]+)/
|
|
2316
|
+
// Alternative format
|
|
2317
|
+
];
|
|
2318
|
+
for (const i of e) {
|
|
2319
|
+
const o = t.match(i);
|
|
2320
|
+
if (o && o[1])
|
|
2321
|
+
return o[1];
|
|
2322
|
+
}
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Load images from a Google Drive folder
|
|
2327
|
+
* @param folderUrl - Google Drive folder URL
|
|
2328
|
+
* @param filter - Filter to apply to discovered images
|
|
2329
|
+
* @param recursive - Whether to include images from subfolders
|
|
2330
|
+
* @returns Promise resolving to array of image URLs
|
|
2331
|
+
*/
|
|
2332
|
+
async loadFromFolder(t, e, i = !0) {
|
|
2333
|
+
const o = this.extractFolderId(t);
|
|
2334
|
+
if (!o)
|
|
2335
|
+
throw new Error("Invalid Google Drive folder URL. Please check the URL format.");
|
|
2336
|
+
if (!this.apiKey || this.apiKey === "YOUR_API_KEY_HERE")
|
|
2337
|
+
return this.loadImagesDirectly(o, e);
|
|
2338
|
+
try {
|
|
2339
|
+
return i ? await this.loadImagesRecursively(o, e) : await this.loadImagesFromSingleFolder(o, e);
|
|
2340
|
+
} catch (s) {
|
|
2341
|
+
return console.error("Error loading from Google Drive API:", s), this.loadImagesDirectly(o, e);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Load images from a single folder (non-recursive)
|
|
2346
|
+
* @param folderId - Google Drive folder ID
|
|
2347
|
+
* @param filter - Filter to apply to discovered images
|
|
2348
|
+
* @returns Promise resolving to array of image URLs
|
|
2349
|
+
*/
|
|
2350
|
+
async loadImagesFromSingleFolder(t, e) {
|
|
2351
|
+
const i = [], o = `'${t}' in parents and trashed=false`, c = `${this.apiEndpoint}?q=${encodeURIComponent(o)}&fields=files(id,name,mimeType,thumbnailLink)&key=${this.apiKey}`, a = await fetch(c);
|
|
2352
|
+
if (!a.ok)
|
|
2353
|
+
throw new Error(`API request failed: ${a.status} ${a.statusText}`);
|
|
2354
|
+
const l = (await a.json()).files.filter(
|
|
2355
|
+
(g) => g.mimeType.startsWith("image/") && e.isAllowed(g.name)
|
|
2356
|
+
);
|
|
2357
|
+
return this.log(`Found ${l.length} images in folder ${t} (non-recursive)`), l.forEach((g) => {
|
|
2358
|
+
i.push(`https://lh3.googleusercontent.com/d/${g.id}=s1600`), this.log(`Added file: ${g.name}`);
|
|
2359
|
+
}), i;
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Load specific files by their URLs or IDs
|
|
2363
|
+
* @param fileUrls - Array of Google Drive file URLs or IDs
|
|
2364
|
+
* @param filter - Filter to apply to discovered images
|
|
2365
|
+
* @returns Promise resolving to array of image URLs
|
|
2366
|
+
*/
|
|
2367
|
+
async loadFiles(t, e) {
|
|
2368
|
+
const i = [];
|
|
2369
|
+
for (const o of t) {
|
|
2370
|
+
const s = this.extractFileId(o);
|
|
2371
|
+
if (!s) {
|
|
2372
|
+
this.log(`Skipping invalid file URL: ${o}`);
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2375
|
+
if (this.apiKey && this.apiKey !== "YOUR_API_KEY_HERE")
|
|
2376
|
+
try {
|
|
2377
|
+
const c = `${this.apiEndpoint}/${s}?fields=name,mimeType&key=${this.apiKey}`, a = await fetch(c);
|
|
2378
|
+
if (a.ok) {
|
|
2379
|
+
const h = await a.json();
|
|
2380
|
+
h.mimeType.startsWith("image/") && e.isAllowed(h.name) ? (i.push(`https://lh3.googleusercontent.com/d/${s}=s1600`), this.log(`Added file: ${h.name}`)) : this.log(`Skipping non-image file: ${h.name} (${h.mimeType})`);
|
|
2381
|
+
} else
|
|
2382
|
+
this.log(`Failed to fetch metadata for file ${s}: ${a.status}`);
|
|
2383
|
+
} catch (c) {
|
|
2384
|
+
this.log(`Error fetching metadata for file ${s}:`, c);
|
|
2385
|
+
}
|
|
2386
|
+
else
|
|
2387
|
+
i.push(`https://lh3.googleusercontent.com/d/${s}=s1600`);
|
|
2388
|
+
}
|
|
2389
|
+
return i;
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2392
|
+
* Extract file ID from Google Drive file URL
|
|
2393
|
+
* @param fileUrl - Google Drive file URL or file ID
|
|
2394
|
+
* @returns File ID or null if invalid
|
|
2395
|
+
*/
|
|
2396
|
+
extractFileId(t) {
|
|
2397
|
+
if (!/[/:.]/.test(t))
|
|
2398
|
+
return t;
|
|
2399
|
+
const e = [
|
|
2400
|
+
/\/file\/d\/([a-zA-Z0-9_-]+)/,
|
|
2401
|
+
// Standard file format
|
|
2402
|
+
/\/open\?id=([a-zA-Z0-9_-]+)/,
|
|
2403
|
+
// Alternative format
|
|
2404
|
+
/id=([a-zA-Z0-9_-]+)/
|
|
2405
|
+
// Generic id parameter
|
|
2406
|
+
];
|
|
2407
|
+
for (const i of e) {
|
|
2408
|
+
const o = t.match(i);
|
|
2409
|
+
if (o && o[1])
|
|
2410
|
+
return o[1];
|
|
2411
|
+
}
|
|
2412
|
+
return null;
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* Recursively load images from a folder and all its subfolders
|
|
2416
|
+
* @param folderId - Google Drive folder ID
|
|
2417
|
+
* @param filter - Filter to apply to discovered images
|
|
2418
|
+
* @returns Promise resolving to array of image URLs
|
|
2419
|
+
*/
|
|
2420
|
+
async loadImagesRecursively(t, e) {
|
|
2421
|
+
const i = [], o = `'${t}' in parents and trashed=false`, c = `${this.apiEndpoint}?q=${encodeURIComponent(o)}&fields=files(id,name,mimeType,thumbnailLink)&key=${this.apiKey}`, a = await fetch(c);
|
|
2422
|
+
if (!a.ok)
|
|
2423
|
+
throw new Error(`API request failed: ${a.status} ${a.statusText}`);
|
|
2424
|
+
const h = await a.json(), l = h.files.filter(
|
|
2425
|
+
(r) => r.mimeType.startsWith("image/") && e.isAllowed(r.name)
|
|
2426
|
+
), g = h.files.filter(
|
|
2427
|
+
(r) => r.mimeType === "application/vnd.google-apps.folder"
|
|
2428
|
+
);
|
|
2429
|
+
this.log(`Found ${h.files.length} total items in folder ${t}`), h.files.forEach((r) => this.log(` - File: ${r.name} (${r.mimeType})`)), this.log(`- ${l.length} valid files (images only)`), this.log(`- ${g.length} subfolders`), l.forEach((r) => {
|
|
2430
|
+
i.push(`https://lh3.googleusercontent.com/d/${r.id}=s1600`), this.log(`Added file: ${r.name}`);
|
|
2431
|
+
});
|
|
2432
|
+
for (const r of g) {
|
|
2433
|
+
this.log(`Loading images from subfolder: ${r.name}`);
|
|
2434
|
+
const d = await this.loadImagesRecursively(r.id, e);
|
|
2435
|
+
i.push(...d);
|
|
2436
|
+
}
|
|
2437
|
+
return i;
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Direct loading method (no API key required, but less reliable)
|
|
2441
|
+
* Uses embedded folder view to scrape image IDs
|
|
2442
|
+
* @param folderId - Google Drive folder ID
|
|
2443
|
+
* @param filter - Filter to apply (not used in fallback mode)
|
|
2444
|
+
* @returns Promise resolving to array of image URLs
|
|
2445
|
+
*/
|
|
2446
|
+
async loadImagesDirectly(t, e) {
|
|
2447
|
+
try {
|
|
2448
|
+
const i = `https://drive.google.com/embeddedfolderview?id=${t}`, o = await fetch(i, { mode: "cors" });
|
|
2449
|
+
if (!o.ok)
|
|
2450
|
+
throw new Error("Cannot access folder directly (CORS or permissions issue)");
|
|
2451
|
+
const s = await o.text(), c = /\/file\/d\/([a-zA-Z0-9_-]+)/g, a = [...s.matchAll(c)];
|
|
2452
|
+
return [...new Set(a.map((g) => g[1]))].map(
|
|
2453
|
+
(g) => `https://drive.google.com/uc?export=view&id=${g}`
|
|
2454
|
+
);
|
|
2455
|
+
} catch (i) {
|
|
2456
|
+
throw console.error("Direct loading failed:", i), new Error(
|
|
2457
|
+
`Unable to load images. Please ensure:
|
|
2458
|
+
1. The folder is shared publicly (Anyone with the link can view)
|
|
2459
|
+
2. The folder contains image files
|
|
2460
|
+
3. Consider adding a Google Drive API key in config.js for better reliability`
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* Manually add image URLs (for testing or when auto-loading fails)
|
|
2466
|
+
* @param imageIds - Array of Google Drive file IDs
|
|
2467
|
+
* @returns Array of direct image URLs
|
|
2468
|
+
*/
|
|
2469
|
+
manualImageUrls(t) {
|
|
2470
|
+
return t.map((e) => `https://drive.google.com/uc?export=view&id=${e}`);
|
|
2471
|
+
}
|
|
2472
|
+
/**
|
|
2473
|
+
* Debug logging helper
|
|
2474
|
+
* @param args - Arguments to log
|
|
2475
|
+
*/
|
|
2476
|
+
log(...t) {
|
|
2477
|
+
this.debugLogging && typeof console < "u" && console.log(...t);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
class Ie {
|
|
2481
|
+
constructor(t = {}) {
|
|
2482
|
+
if (this._prepared = !1, this._discoveredUrls = [], this.validateUrls = t.validateUrls !== !1, this.validationTimeout = t.validationTimeout ?? 5e3, this.validationMethod = t.validationMethod ?? "head", this.sources = t.sources ?? [], this.debugLogging = t.debugLogging ?? !1, !this.sources || this.sources.length === 0)
|
|
2483
|
+
throw new Error("StaticImageLoader requires at least one source to be configured");
|
|
2484
|
+
this.log("StaticImageLoader initialized with config:", t);
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Prepare the loader by discovering all images from configured sources
|
|
2488
|
+
* @param filter - Filter to apply to discovered images
|
|
2489
|
+
*/
|
|
2490
|
+
async prepare(t) {
|
|
2491
|
+
this._discoveredUrls = [], this.log(`Processing ${this.sources.length} source(s)`);
|
|
2492
|
+
for (const e of this.sources)
|
|
2493
|
+
try {
|
|
2494
|
+
const i = await this.processSource(e, t);
|
|
2495
|
+
this._discoveredUrls.push(...i);
|
|
2496
|
+
} catch (i) {
|
|
2497
|
+
console.warn("Failed to process source:", e, i);
|
|
2498
|
+
}
|
|
2499
|
+
this._prepared = !0, this.log(`Successfully loaded ${this._discoveredUrls.length} image(s)`);
|
|
2500
|
+
}
|
|
2501
|
+
/**
|
|
2502
|
+
* Get the number of discovered images
|
|
2503
|
+
* @throws Error if called before prepare()
|
|
2504
|
+
*/
|
|
2505
|
+
imagesLength() {
|
|
2506
|
+
if (!this._prepared)
|
|
2507
|
+
throw new Error("StaticImageLoader.imagesLength() called before prepare()");
|
|
2508
|
+
return this._discoveredUrls.length;
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Get the ordered list of image URLs
|
|
2512
|
+
* @throws Error if called before prepare()
|
|
2513
|
+
*/
|
|
2514
|
+
imageURLs() {
|
|
2515
|
+
if (!this._prepared)
|
|
2516
|
+
throw new Error("StaticImageLoader.imageURLs() called before prepare()");
|
|
2517
|
+
return [...this._discoveredUrls];
|
|
2518
|
+
}
|
|
2519
|
+
/**
|
|
2520
|
+
* Check if the loader has been prepared
|
|
2521
|
+
*/
|
|
2522
|
+
isPrepared() {
|
|
2523
|
+
return this._prepared;
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* Process a single source object
|
|
2527
|
+
* @param source - Source configuration with type, urls, basePath, files
|
|
2528
|
+
* @param filter - Filter to apply to discovered images
|
|
2529
|
+
* @returns Promise resolving to array of valid URLs from this source
|
|
2530
|
+
*/
|
|
2531
|
+
async processSource(t, e) {
|
|
2532
|
+
return !t || !t.type ? (console.warn("Invalid source object (missing type):", t), []) : t.type === "urls" ? await this.processUrls(t.urls || [], e) : t.type === "path" ? await this.processPath(t.basePath, t.files || [], e) : (console.warn(`Unknown source type: ${t.type}`), []);
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* Process a list of direct URLs
|
|
2536
|
+
* @param urls - Array of image URLs
|
|
2537
|
+
* @param filter - Filter to apply to discovered images
|
|
2538
|
+
* @returns Promise resolving to array of validated URLs
|
|
2539
|
+
*/
|
|
2540
|
+
async processUrls(t, e) {
|
|
2541
|
+
if (!Array.isArray(t))
|
|
2542
|
+
return console.warn("URLs must be an array:", t), [];
|
|
2543
|
+
const i = [];
|
|
2544
|
+
for (const o of t) {
|
|
2545
|
+
const s = o.split("/").pop() || o;
|
|
2546
|
+
if (!e.isAllowed(s)) {
|
|
2547
|
+
this.log(`Skipping filtered URL: ${o}`);
|
|
2548
|
+
continue;
|
|
2549
|
+
}
|
|
2550
|
+
this.validateUrls ? await this.validateUrl(o) ? i.push(o) : console.warn(`Skipping invalid/missing URL: ${o}`) : i.push(o);
|
|
2551
|
+
}
|
|
2552
|
+
return i;
|
|
2553
|
+
}
|
|
2554
|
+
/**
|
|
2555
|
+
* Process a path-based source
|
|
2556
|
+
* @param basePath - Base path (relative or absolute)
|
|
2557
|
+
* @param files - Array of filenames
|
|
2558
|
+
* @param filter - Filter to apply to discovered images
|
|
2559
|
+
* @returns Promise resolving to array of validated URLs
|
|
2560
|
+
*/
|
|
2561
|
+
async processPath(t, e, i) {
|
|
2562
|
+
if (!t)
|
|
2563
|
+
return console.warn("basePath is required for path-type sources"), [];
|
|
2564
|
+
if (!Array.isArray(e))
|
|
2565
|
+
return console.warn("files must be an array:", e), [];
|
|
2566
|
+
const o = [];
|
|
2567
|
+
for (const s of e) {
|
|
2568
|
+
if (!i.isAllowed(s)) {
|
|
2569
|
+
this.log(`Skipping filtered file: ${s}`);
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
const c = this.constructUrl(t, s);
|
|
2573
|
+
this.validateUrls ? await this.validateUrl(c) ? o.push(c) : console.warn(`Skipping invalid/missing file: ${c}`) : o.push(c);
|
|
2574
|
+
}
|
|
2575
|
+
return o;
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* Validate a single URL using HEAD request
|
|
2579
|
+
* @param url - URL to validate
|
|
2580
|
+
* @returns Promise resolving to true if valid and accessible
|
|
2581
|
+
*/
|
|
2582
|
+
async validateUrl(t) {
|
|
2583
|
+
if (this.validationMethod === "none")
|
|
2584
|
+
return !0;
|
|
2585
|
+
if (this.validationMethod === "simple")
|
|
2586
|
+
try {
|
|
2587
|
+
return typeof window < "u" ? new URL(t, window.location.origin) : new URL(t), !0;
|
|
2588
|
+
} catch {
|
|
2589
|
+
return !1;
|
|
2590
|
+
}
|
|
2591
|
+
if (typeof window > "u")
|
|
2592
|
+
return !0;
|
|
2593
|
+
if (!(t.startsWith(window.location.origin) || t.startsWith("/")))
|
|
2594
|
+
return this.log(`Skipping validation for cross-origin URL: ${t}`), !0;
|
|
2595
|
+
try {
|
|
2596
|
+
const i = new AbortController(), o = setTimeout(() => i.abort(), this.validationTimeout), s = await fetch(t, {
|
|
2597
|
+
method: "HEAD",
|
|
2598
|
+
signal: i.signal
|
|
2599
|
+
});
|
|
2600
|
+
return clearTimeout(o), s.ok ? !0 : (this.log(`Validation failed for ${t}: HTTP ${s.status}`), !1);
|
|
2601
|
+
} catch (i) {
|
|
2602
|
+
return i instanceof Error && (i.name === "AbortError" ? this.log(`Validation timeout for ${t}`) : this.log(`Validation failed for ${t}:`, i.message)), !1;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* Construct full URL from basePath and filename
|
|
2607
|
+
* @param basePath - Base path (relative or absolute)
|
|
2608
|
+
* @param filename - Filename to append
|
|
2609
|
+
* @returns Complete URL
|
|
2610
|
+
*/
|
|
2611
|
+
constructUrl(t, e) {
|
|
2612
|
+
const i = t.replace(/\/$/, "");
|
|
2613
|
+
if (this.isAbsoluteUrl(t))
|
|
2614
|
+
return `${i}/${e}`;
|
|
2615
|
+
if (typeof window > "u")
|
|
2616
|
+
return `${i}/${e}`;
|
|
2617
|
+
const o = window.location.origin, c = (t.startsWith("/") ? t : "/" + t).replace(/\/$/, "");
|
|
2618
|
+
return `${o}${c}/${e}`;
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* Check if URL is absolute (contains protocol)
|
|
2622
|
+
* @param url - URL to check
|
|
2623
|
+
* @returns True if absolute URL
|
|
2624
|
+
*/
|
|
2625
|
+
isAbsoluteUrl(t) {
|
|
2626
|
+
try {
|
|
2627
|
+
return new URL(t), !0;
|
|
2628
|
+
} catch {
|
|
2629
|
+
return !1;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
/**
|
|
2633
|
+
* Debug logging helper
|
|
2634
|
+
* @param args - Arguments to log
|
|
2635
|
+
*/
|
|
2636
|
+
log(...t) {
|
|
2637
|
+
this.debugLogging && typeof console < "u" && console.log(...t);
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
class Re {
|
|
2641
|
+
constructor(t) {
|
|
2642
|
+
if (this._prepared = !1, this._discoveredUrls = [], this.loaders = t.loaders, this.debugLogging = t.debugLogging ?? !1, !this.loaders || this.loaders.length === 0)
|
|
2643
|
+
throw new Error("CompositeLoader requires at least one loader to be configured");
|
|
2644
|
+
this.log(`CompositeLoader initialized with ${this.loaders.length} loader(s)`);
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Prepare all loaders in parallel and combine their results
|
|
2648
|
+
* @param filter - Filter to apply to discovered images
|
|
2649
|
+
*/
|
|
2650
|
+
async prepare(t) {
|
|
2651
|
+
this._discoveredUrls = [], this.log(`Preparing ${this.loaders.length} loader(s) in parallel`);
|
|
2652
|
+
const e = this.loaders.map((i, o) => i.prepare(t).then(() => {
|
|
2653
|
+
this.log(`Loader ${o} prepared with ${i.imagesLength()} images`);
|
|
2654
|
+
}).catch((s) => {
|
|
2655
|
+
console.warn(`Loader ${o} failed to prepare:`, s);
|
|
2656
|
+
}));
|
|
2657
|
+
await Promise.all(e);
|
|
2658
|
+
for (const i of this.loaders)
|
|
2659
|
+
if (i.isPrepared()) {
|
|
2660
|
+
const o = i.imageURLs();
|
|
2661
|
+
this._discoveredUrls.push(...o);
|
|
2662
|
+
}
|
|
2663
|
+
this._prepared = !0, this.log(`CompositeLoader prepared with ${this._discoveredUrls.length} total images`);
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Get the combined number of discovered images
|
|
2667
|
+
* @throws Error if called before prepare()
|
|
2668
|
+
*/
|
|
2669
|
+
imagesLength() {
|
|
2670
|
+
if (!this._prepared)
|
|
2671
|
+
throw new Error("CompositeLoader.imagesLength() called before prepare()");
|
|
2672
|
+
return this._discoveredUrls.length;
|
|
2673
|
+
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Get the combined ordered list of image URLs
|
|
2676
|
+
* @throws Error if called before prepare()
|
|
2677
|
+
*/
|
|
2678
|
+
imageURLs() {
|
|
2679
|
+
if (!this._prepared)
|
|
2680
|
+
throw new Error("CompositeLoader.imageURLs() called before prepare()");
|
|
2681
|
+
return [...this._discoveredUrls];
|
|
2682
|
+
}
|
|
2683
|
+
/**
|
|
2684
|
+
* Check if the loader has been prepared
|
|
2685
|
+
*/
|
|
2686
|
+
isPrepared() {
|
|
2687
|
+
return this._prepared;
|
|
2688
|
+
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Debug logging helper
|
|
2691
|
+
* @param args - Arguments to log
|
|
2692
|
+
*/
|
|
2693
|
+
log(...t) {
|
|
2694
|
+
this.debugLogging && typeof console < "u" && console.log("[CompositeLoader]", ...t);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
class Ee {
|
|
2698
|
+
/**
|
|
2699
|
+
* Create a new ImageFilter
|
|
2700
|
+
* @param extensions - Array of allowed file extensions (without dots)
|
|
2701
|
+
* Defaults to common image formats if not provided
|
|
2702
|
+
*/
|
|
2703
|
+
constructor(t) {
|
|
2704
|
+
this.allowedExtensions = t || [
|
|
2705
|
+
"jpg",
|
|
2706
|
+
"jpeg",
|
|
2707
|
+
"png",
|
|
2708
|
+
"gif",
|
|
2709
|
+
"webp",
|
|
2710
|
+
"bmp"
|
|
2711
|
+
];
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Check if a filename has an allowed extension
|
|
2715
|
+
* @param filename - The filename to check (can include path or query string)
|
|
2716
|
+
* @returns True if the file extension is allowed
|
|
2717
|
+
*/
|
|
2718
|
+
isAllowed(t) {
|
|
2719
|
+
var o;
|
|
2720
|
+
const i = (o = t.split("?")[0].split(".").pop()) == null ? void 0 : o.toLowerCase();
|
|
2721
|
+
return i ? this.allowedExtensions.includes(i) : !1;
|
|
2722
|
+
}
|
|
2723
|
+
/**
|
|
2724
|
+
* Get the list of allowed extensions
|
|
2725
|
+
* @returns Array of allowed extensions
|
|
2726
|
+
*/
|
|
2727
|
+
getAllowedExtensions() {
|
|
2728
|
+
return [...this.allowedExtensions];
|
|
2729
|
+
}
|
|
2730
|
+
// Future expansion methods:
|
|
2731
|
+
// isAllowedSize(sizeBytes: number): boolean
|
|
2732
|
+
// isAllowedDate(date: Date): boolean
|
|
2733
|
+
// isAllowedDimensions(width: number, height: number): boolean
|
|
2734
|
+
}
|
|
2735
|
+
class Ae {
|
|
2736
|
+
constructor(t = {}) {
|
|
2737
|
+
var i, o, s, c, a, h;
|
|
2738
|
+
this.fullConfig = qt(t), t.container instanceof HTMLElement ? (this.containerRef = t.container, this.containerId = null) : (this.containerRef = null, this.containerId = t.container || "imageCloud"), this.imagesLoaded = !1, this.imageElements = [], this.imageLayouts = [], this.currentImageHeight = 225, this.currentFocusIndex = null, this.hoveredImage = null, this.resizeTimeout = null, this.displayQueue = [], this.queueInterval = null, this.loadGeneration = 0, this.animationEngine = new Vt(this.fullConfig.animation), this.layoutEngine = new ye({
|
|
2739
|
+
layout: this.fullConfig.layout,
|
|
2740
|
+
image: this.fullConfig.image,
|
|
2741
|
+
breakpoints: this.fullConfig.rendering.responsive.breakpoints
|
|
2742
|
+
}), this.zoomEngine = new xe(this.fullConfig.interaction.focus, this.animationEngine, this.fullConfig.styling), this.defaultStyles = mt((i = this.fullConfig.styling) == null ? void 0 : i.default), this.hoverStyles = mt((o = this.fullConfig.styling) == null ? void 0 : o.hover), this.defaultClassName = (c = (s = this.fullConfig.styling) == null ? void 0 : s.default) == null ? void 0 : c.className, this.hoverClassName = (h = (a = this.fullConfig.styling) == null ? void 0 : a.hover) == null ? void 0 : h.className;
|
|
2743
|
+
const e = this.fullConfig.animation.entry || b.animation.entry;
|
|
2744
|
+
this.entryAnimationEngine = new ae(
|
|
2745
|
+
e,
|
|
2746
|
+
this.fullConfig.layout.algorithm
|
|
2747
|
+
), this.imageFilter = this.createImageFilter(), this.imageLoader = this.createLoader(), this.containerEl = null, this.loadingEl = null, this.errorEl = null;
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Create image filter based on config
|
|
2751
|
+
*/
|
|
2752
|
+
createImageFilter() {
|
|
2753
|
+
var i, o;
|
|
2754
|
+
const t = this.fullConfig.loader.type;
|
|
2755
|
+
let e;
|
|
2756
|
+
return t === "googleDrive" ? e = (i = this.fullConfig.loader.googleDrive) == null ? void 0 : i.allowedExtensions : e = (o = this.fullConfig.loader.static) == null ? void 0 : o.allowedExtensions, new Ee(e);
|
|
2757
|
+
}
|
|
2758
|
+
/**
|
|
2759
|
+
* Create appropriate image loader based on config
|
|
2760
|
+
*/
|
|
2761
|
+
createLoader() {
|
|
2762
|
+
return this.createLoaderFromConfig(this.fullConfig.loader);
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Create a loader from a LoaderConfig object (supports recursive composite loaders)
|
|
2766
|
+
*/
|
|
2767
|
+
createLoaderFromConfig(t) {
|
|
2768
|
+
const e = t.type;
|
|
2769
|
+
if (e === "static") {
|
|
2770
|
+
const i = t.static;
|
|
2771
|
+
return new Ie(i);
|
|
2772
|
+
} else if (e === "composite") {
|
|
2773
|
+
const i = t.composite, o = i.loaders.map(
|
|
2774
|
+
(s) => this.createLoaderFromConfig(s)
|
|
2775
|
+
);
|
|
2776
|
+
return new Re({
|
|
2777
|
+
loaders: o,
|
|
2778
|
+
debugLogging: i.debugLogging
|
|
2779
|
+
});
|
|
2780
|
+
} else {
|
|
2781
|
+
const i = t.googleDrive;
|
|
2782
|
+
return new Se(i);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Initialize the gallery
|
|
2787
|
+
*/
|
|
2788
|
+
async init() {
|
|
2789
|
+
try {
|
|
2790
|
+
if (this.containerRef)
|
|
2791
|
+
this.containerEl = this.containerRef;
|
|
2792
|
+
else if (this.containerEl = document.getElementById(this.containerId), !this.containerEl)
|
|
2793
|
+
throw new Error(`Container #${this.containerId} not found`);
|
|
2794
|
+
this.containerEl.classList.add("fbn-ic-gallery"), this.setupUI(), this.setupEventListeners(), this.logDebug("ImageCloud initialized"), await this.loadImages();
|
|
2795
|
+
} catch (t) {
|
|
2796
|
+
console.error("Gallery initialization failed:", t), this.errorEl && t instanceof Error && this.showError("Gallery failed to initialize: " + t.message);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
setupUI() {
|
|
2800
|
+
this.loadingEl = document.getElementById("loading"), this.errorEl = document.getElementById("error");
|
|
2801
|
+
}
|
|
2802
|
+
setupEventListeners() {
|
|
2803
|
+
document.addEventListener("keydown", (t) => {
|
|
2804
|
+
t.key === "Escape" ? (this.zoomEngine.unfocusImage(), this.currentFocusIndex = null) : t.key === "ArrowRight" ? this.navigateToNextImage() : t.key === "ArrowLeft" ? this.navigateToPreviousImage() : (t.key === "Enter" || t.key === " ") && this.hoveredImage && (this.handleImageClick(this.hoveredImage.element, this.hoveredImage.layout), t.preventDefault());
|
|
2805
|
+
}), document.addEventListener("click", (t) => {
|
|
2806
|
+
t.target.closest(".fbn-ic-image") || (this.zoomEngine.unfocusImage(), this.currentFocusIndex = null);
|
|
2807
|
+
}), window.addEventListener("resize", () => this.handleResize());
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Navigate to the next image (Right arrow)
|
|
2811
|
+
*/
|
|
2812
|
+
navigateToNextImage() {
|
|
2813
|
+
if (this.currentFocusIndex === null || this.imageElements.length === 0) return;
|
|
2814
|
+
const t = (this.currentFocusIndex + 1) % this.imageElements.length;
|
|
2815
|
+
this.navigateToImage(t);
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Navigate to the previous image (Left arrow)
|
|
2819
|
+
*/
|
|
2820
|
+
navigateToPreviousImage() {
|
|
2821
|
+
if (this.currentFocusIndex === null || this.imageElements.length === 0) return;
|
|
2822
|
+
const t = (this.currentFocusIndex - 1 + this.imageElements.length) % this.imageElements.length;
|
|
2823
|
+
this.navigateToImage(t);
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* Navigate to a specific image by index
|
|
2827
|
+
*/
|
|
2828
|
+
async navigateToImage(t) {
|
|
2829
|
+
if (t < 0 || t >= this.imageElements.length) return;
|
|
2830
|
+
const e = this.imageElements[t], i = this.imageLayouts[t];
|
|
2831
|
+
!e || !i || await this.handleImageClick(e, i);
|
|
2832
|
+
}
|
|
2833
|
+
handleResize() {
|
|
2834
|
+
this.imagesLoaded && (this.resizeTimeout !== null && clearTimeout(this.resizeTimeout), this.resizeTimeout = window.setTimeout(() => {
|
|
2835
|
+
const t = this.getImageHeight();
|
|
2836
|
+
t !== this.currentImageHeight ? (this.logDebug(`Window resized to new breakpoint (height: ${t}px). Reloading images...`), this.loadImages()) : this.logDebug("Window resized (no breakpoint change)");
|
|
2837
|
+
}, 500));
|
|
2838
|
+
}
|
|
2839
|
+
getImageHeight() {
|
|
2840
|
+
const t = window.innerWidth, e = this.fullConfig.layout.sizing.responsive || [];
|
|
2841
|
+
for (const i of e)
|
|
2842
|
+
if (t >= i.minWidth)
|
|
2843
|
+
return i.height;
|
|
2844
|
+
return 120;
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Get container bounds for layout calculations
|
|
2848
|
+
*/
|
|
2849
|
+
getContainerBounds() {
|
|
2850
|
+
return this.containerEl ? {
|
|
2851
|
+
width: this.containerEl.offsetWidth,
|
|
2852
|
+
height: this.containerEl.offsetHeight || window.innerHeight * 0.7
|
|
2853
|
+
} : { width: window.innerWidth, height: window.innerHeight * 0.7 };
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Load images using the unified loader interface
|
|
2857
|
+
*/
|
|
2858
|
+
async loadImages() {
|
|
2859
|
+
try {
|
|
2860
|
+
this.showLoading(!0), this.hideError(), this.clearImageCloud(), await this.imageLoader.prepare(this.imageFilter);
|
|
2861
|
+
const t = this.imageLoader.imagesLength();
|
|
2862
|
+
let e = this.imageLoader.imageURLs();
|
|
2863
|
+
if (t === 0) {
|
|
2864
|
+
this.showError("No images found."), this.showLoading(!1);
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
const i = this.getContainerBounds(), o = this.getImageHeight(), s = window.innerWidth;
|
|
2868
|
+
this.logDebug(`Adaptive sizing input: container=${i.width}x${i.height}px, images=${t}, responsiveMax=${o}px`);
|
|
2869
|
+
const c = this.layoutEngine.calculateAdaptiveSize(
|
|
2870
|
+
i,
|
|
2871
|
+
t,
|
|
2872
|
+
o,
|
|
2873
|
+
s
|
|
2874
|
+
);
|
|
2875
|
+
this.logDebug(`Adaptive sizing result: height=${c.height}px`), await this.createImageCloud(e, c.height), this.showLoading(!1), this.imagesLoaded = !0;
|
|
2876
|
+
} catch (t) {
|
|
2877
|
+
console.error("Error loading images:", t), t instanceof Error && this.showError(t.message || "Failed to load images."), this.showLoading(!1);
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
/**
|
|
2881
|
+
* Helper for debug logging
|
|
2882
|
+
*/
|
|
2883
|
+
logDebug(...t) {
|
|
2884
|
+
this.fullConfig.debug && typeof console < "u" && console.log(...t);
|
|
2885
|
+
}
|
|
2886
|
+
async createImageCloud(t, e) {
|
|
2887
|
+
if (!this.containerEl) return;
|
|
2888
|
+
const i = this.getContainerBounds();
|
|
2889
|
+
this.currentImageHeight = e;
|
|
2890
|
+
const o = this.loadGeneration, s = this.layoutEngine.generateLayout(t.length, i, { fixedHeight: e });
|
|
2891
|
+
this.imageLayouts = s, this.displayQueue = [];
|
|
2892
|
+
let c = 0;
|
|
2893
|
+
const a = (l) => {
|
|
2894
|
+
this.containerEl && (this.containerEl.appendChild(l), this.imageElements.push(l), requestAnimationFrame(() => {
|
|
2895
|
+
if (l.offsetWidth, l.style.opacity = this.defaultStyles.opacity ?? "1", l.dataset.startX && (this.entryAnimationEngine.requiresJSAnimation() || this.entryAnimationEngine.requiresJSRotation() || this.entryAnimationEngine.requiresJSScale() || l.dataset.startRotation !== l.dataset.rotation || l.dataset.startScale !== l.dataset.scale)) {
|
|
2896
|
+
const d = {
|
|
2897
|
+
x: parseFloat(l.dataset.startX),
|
|
2898
|
+
y: parseFloat(l.dataset.startY)
|
|
2899
|
+
}, f = {
|
|
2900
|
+
x: parseFloat(l.dataset.endX),
|
|
2901
|
+
y: parseFloat(l.dataset.endY)
|
|
2902
|
+
}, y = parseFloat(l.dataset.imageWidth), p = parseFloat(l.dataset.imageHeight), u = parseFloat(l.dataset.rotation), m = parseFloat(l.dataset.scale), w = l.dataset.startRotation ? parseFloat(l.dataset.startRotation) : u, v = l.dataset.startScale ? parseFloat(l.dataset.startScale) : m, E = this.entryAnimationEngine.getTiming();
|
|
2903
|
+
ne({
|
|
2904
|
+
element: l,
|
|
2905
|
+
startPosition: d,
|
|
2906
|
+
endPosition: f,
|
|
2907
|
+
pathConfig: this.entryAnimationEngine.getPathConfig(),
|
|
2908
|
+
duration: E.duration,
|
|
2909
|
+
imageWidth: y,
|
|
2910
|
+
imageHeight: p,
|
|
2911
|
+
rotation: u,
|
|
2912
|
+
scale: m,
|
|
2913
|
+
rotationConfig: this.entryAnimationEngine.getRotationConfig(),
|
|
2914
|
+
startRotation: w,
|
|
2915
|
+
scaleConfig: this.entryAnimationEngine.getScaleConfig(),
|
|
2916
|
+
startScale: v
|
|
2917
|
+
});
|
|
2918
|
+
} else {
|
|
2919
|
+
const d = l.dataset.finalTransform || "";
|
|
2920
|
+
l.style.transform = d;
|
|
2921
|
+
}
|
|
2922
|
+
const r = parseInt(l.dataset.imageId || "0");
|
|
2923
|
+
if (this.fullConfig.debug && r < 3) {
|
|
2924
|
+
const d = l.dataset.finalTransform || "";
|
|
2925
|
+
console.log(`Image ${r} final state:`, {
|
|
2926
|
+
left: l.style.left,
|
|
2927
|
+
top: l.style.top,
|
|
2928
|
+
width: l.style.width,
|
|
2929
|
+
height: l.style.height,
|
|
2930
|
+
computedWidth: l.offsetWidth,
|
|
2931
|
+
computedHeight: l.offsetHeight,
|
|
2932
|
+
transform: d,
|
|
2933
|
+
pathType: this.entryAnimationEngine.getPathType()
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
}), c++);
|
|
2937
|
+
}, h = () => {
|
|
2938
|
+
if (this.logDebug("Starting queue processing, enabled:", this.fullConfig.animation.queue.enabled), !this.fullConfig.animation.queue.enabled) {
|
|
2939
|
+
for (; this.displayQueue.length > 0; ) {
|
|
2940
|
+
const l = this.displayQueue.shift();
|
|
2941
|
+
l && a(l);
|
|
2942
|
+
}
|
|
2943
|
+
return;
|
|
2944
|
+
}
|
|
2945
|
+
this.queueInterval !== null && clearInterval(this.queueInterval), this.queueInterval = window.setInterval(() => {
|
|
2946
|
+
if (o !== this.loadGeneration) {
|
|
2947
|
+
this.queueInterval !== null && (clearInterval(this.queueInterval), this.queueInterval = null);
|
|
2948
|
+
return;
|
|
2949
|
+
}
|
|
2950
|
+
if (this.displayQueue.length > 0) {
|
|
2951
|
+
const l = this.displayQueue.shift();
|
|
2952
|
+
l && a(l);
|
|
2953
|
+
}
|
|
2954
|
+
c >= t.length && this.displayQueue.length === 0 && this.queueInterval !== null && (clearInterval(this.queueInterval), this.queueInterval = null);
|
|
2955
|
+
}, this.fullConfig.animation.queue.interval);
|
|
2956
|
+
};
|
|
2957
|
+
if ("IntersectionObserver" in window && this.containerEl) {
|
|
2958
|
+
const l = new IntersectionObserver((g) => {
|
|
2959
|
+
g.forEach((r) => {
|
|
2960
|
+
r.isIntersecting && (h(), l.disconnect());
|
|
2961
|
+
});
|
|
2962
|
+
}, { threshold: 0.1, rootMargin: "50px" });
|
|
2963
|
+
l.observe(this.containerEl);
|
|
2964
|
+
} else
|
|
2965
|
+
h();
|
|
2966
|
+
this.fullConfig.layout.debugCenters && this.containerEl && (this.containerEl.querySelectorAll(".fbn-ic-debug-center").forEach((l) => l.remove()), s.forEach((l, g) => {
|
|
2967
|
+
const r = document.createElement("div");
|
|
2968
|
+
r.className = "fbn-ic-debug-center", r.style.position = "absolute", r.style.width = "12px", r.style.height = "12px", r.style.borderRadius = "50%", r.style.backgroundColor = "red", r.style.border = "2px solid yellow", r.style.zIndex = "9999", r.style.pointerEvents = "none";
|
|
2969
|
+
const d = l.x, f = l.y;
|
|
2970
|
+
r.style.left = `${d - 6}px`, r.style.top = `${f - 6}px`, r.title = `Image ${g}: center (${Math.round(d)}, ${Math.round(f)})`, this.containerEl.appendChild(r);
|
|
2971
|
+
})), t.forEach((l, g) => {
|
|
2972
|
+
var f, y;
|
|
2973
|
+
const r = document.createElement("img");
|
|
2974
|
+
r.referrerPolicy = "no-referrer", r.classList.add("fbn-ic-image"), r.dataset.imageId = String(g);
|
|
2975
|
+
const d = s[g];
|
|
2976
|
+
r.style.position = "absolute", r.style.width = "auto", r.style.height = `${e}px`, r.style.left = `${d.x}px`, r.style.top = `${d.y}px`, d.borderColor && !((y = (f = this.fullConfig.styling) == null ? void 0 : f.default) != null && y.border) && (r.style.border = `5px solid ${d.borderColor}`, r.style.boxSizing = "border-box"), d.zIndex && (r.style.zIndex = String(d.zIndex)), lt(r, this.defaultStyles), ht(r, this.defaultClassName), r.addEventListener("mouseenter", () => {
|
|
2977
|
+
this.hoveredImage = { element: r, layout: d }, this.zoomEngine.isInvolved(r) || (lt(r, this.hoverStyles), ht(r, this.hoverClassName));
|
|
2978
|
+
}), r.addEventListener("mouseleave", () => {
|
|
2979
|
+
this.hoveredImage = null, this.zoomEngine.isInvolved(r) || (lt(r, this.defaultStyles), Dt(r, this.hoverClassName), ht(r, this.defaultClassName));
|
|
2980
|
+
}), r.addEventListener("click", (p) => {
|
|
2981
|
+
p.stopPropagation(), this.handleImageClick(r, d);
|
|
2982
|
+
}), r.style.opacity = "0", r.style.transition = this.entryAnimationEngine.getTransitionCSS(), r.onload = () => {
|
|
2983
|
+
if (o !== this.loadGeneration)
|
|
2984
|
+
return;
|
|
2985
|
+
const p = r.naturalWidth / r.naturalHeight, u = e * p;
|
|
2986
|
+
r.style.width = `${u}px`;
|
|
2987
|
+
const m = { x: d.x, y: d.y }, w = { width: u, height: e }, v = this.entryAnimationEngine.calculateStartPosition(
|
|
2988
|
+
m,
|
|
2989
|
+
w,
|
|
2990
|
+
i,
|
|
2991
|
+
g,
|
|
2992
|
+
t.length
|
|
2993
|
+
), E = this.entryAnimationEngine.calculateStartRotation(d.rotation), M = this.entryAnimationEngine.calculateStartScale(d.scale), x = this.entryAnimationEngine.buildFinalTransform(
|
|
2994
|
+
d.rotation,
|
|
2995
|
+
d.scale,
|
|
2996
|
+
u,
|
|
2997
|
+
e
|
|
2998
|
+
), L = this.entryAnimationEngine.buildStartTransform(
|
|
2999
|
+
v,
|
|
3000
|
+
m,
|
|
3001
|
+
d.rotation,
|
|
3002
|
+
d.scale,
|
|
3003
|
+
u,
|
|
3004
|
+
e,
|
|
3005
|
+
E,
|
|
3006
|
+
M
|
|
3007
|
+
);
|
|
3008
|
+
this.fullConfig.debug && g < 3 && console.log(`Image ${g}:`, {
|
|
3009
|
+
finalPosition: m,
|
|
3010
|
+
imageSize: w,
|
|
3011
|
+
left: d.x,
|
|
3012
|
+
top: d.y,
|
|
3013
|
+
finalTransform: x,
|
|
3014
|
+
renderedWidth: u,
|
|
3015
|
+
renderedHeight: e
|
|
3016
|
+
}), r.style.transform = L, r.dataset.finalTransform = x, (this.entryAnimationEngine.requiresJSAnimation() || this.entryAnimationEngine.requiresJSRotation() || this.entryAnimationEngine.requiresJSScale() || E !== d.rotation || M !== d.scale) && (r.dataset.startX = String(v.x), r.dataset.startY = String(v.y), r.dataset.endX = String(m.x), r.dataset.endY = String(m.y), r.dataset.imageWidth = String(u), r.dataset.imageHeight = String(e), r.dataset.rotation = String(d.rotation), r.dataset.scale = String(d.scale), r.dataset.startRotation = String(E), r.dataset.startScale = String(M)), this.displayQueue.push(r);
|
|
3017
|
+
}, r.onerror = () => c++, r.src = l;
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
async handleImageClick(t, e) {
|
|
3021
|
+
if (!this.containerEl) return;
|
|
3022
|
+
const i = this.zoomEngine.isFocused(t), o = {
|
|
3023
|
+
width: this.containerEl.offsetWidth,
|
|
3024
|
+
height: this.containerEl.offsetHeight
|
|
3025
|
+
};
|
|
3026
|
+
if (i)
|
|
3027
|
+
await this.zoomEngine.unfocusImage(), this.currentFocusIndex = null;
|
|
3028
|
+
else {
|
|
3029
|
+
const s = t.dataset.imageId;
|
|
3030
|
+
this.currentFocusIndex = s !== void 0 ? parseInt(s, 10) : null, await this.zoomEngine.focusImage(t, o, e);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Clear the image cloud and reset state
|
|
3035
|
+
*/
|
|
3036
|
+
clearImageCloud() {
|
|
3037
|
+
this.queueInterval !== null && (clearInterval(this.queueInterval), this.queueInterval = null), this.loadGeneration++, this.displayQueue = [], this.containerEl && (this.containerEl.innerHTML = ""), this.imageElements = [], this.imageLayouts = [], this.currentFocusIndex = null, this.hoveredImage = null, this.layoutEngine.reset(), this.zoomEngine.reset(), this.imagesLoaded = !1;
|
|
3038
|
+
}
|
|
3039
|
+
showLoading(t) {
|
|
3040
|
+
!this.fullConfig.rendering.ui.showLoadingSpinner || !this.loadingEl || (t ? this.loadingEl.classList.remove("fbn-ic-hidden") : this.loadingEl.classList.add("fbn-ic-hidden"));
|
|
3041
|
+
}
|
|
3042
|
+
showError(t) {
|
|
3043
|
+
this.errorEl && (this.errorEl.textContent = t, this.errorEl.classList.remove("fbn-ic-hidden"));
|
|
3044
|
+
}
|
|
3045
|
+
hideError() {
|
|
3046
|
+
this.errorEl && this.errorEl.classList.add("fbn-ic-hidden");
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Destroy the gallery and clean up resources
|
|
3050
|
+
*/
|
|
3051
|
+
destroy() {
|
|
3052
|
+
this.clearImageCloud(), this.resizeTimeout !== null && clearTimeout(this.resizeTimeout);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
export {
|
|
3056
|
+
Vt as AnimationEngine,
|
|
3057
|
+
zt as BOUNCE_PRESETS,
|
|
3058
|
+
fe as ClusterPlacementGenerator,
|
|
3059
|
+
Re as CompositeLoader,
|
|
3060
|
+
b as DEFAULT_CONFIG,
|
|
3061
|
+
Tt as ELASTIC_PRESETS,
|
|
3062
|
+
ae as EntryAnimationEngine,
|
|
3063
|
+
Se as GoogleDriveLoader,
|
|
3064
|
+
he as GridPlacementGenerator,
|
|
3065
|
+
Ae as ImageCloud,
|
|
3066
|
+
Ee as ImageFilter,
|
|
3067
|
+
Ae as ImageGallery,
|
|
3068
|
+
ye as LayoutEngine,
|
|
3069
|
+
ce as RadialPlacementGenerator,
|
|
3070
|
+
re as RandomPlacementGenerator,
|
|
3071
|
+
ue as SpiralPlacementGenerator,
|
|
3072
|
+
Ie as StaticImageLoader,
|
|
3073
|
+
Mt as WAVE_PATH_PRESETS,
|
|
3074
|
+
xe as ZoomEngine,
|
|
3075
|
+
ne as animatePath,
|
|
3076
|
+
oe as requiresJSAnimation
|
|
3077
|
+
};
|
|
3078
|
+
//# sourceMappingURL=image-cloud.js.map
|