zumly 0.9.11 → 0.92.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.
@@ -0,0 +1,315 @@
1
+ import { showViewContent } from '../view-visibility.js'
2
+
3
+ /**
4
+ * Shared helpers for Zumly transition drivers.
5
+ *
6
+ * These utilities handle the repetitive DOM work that every driver needs:
7
+ * applying final states, removing views, parsing durations, and running
8
+ * lateral (same-level) transitions. Import what you need; skip what you don't.
9
+ *
10
+ * @module driver-helpers
11
+ * @see ../../docs/DRIVER_API.md for the full driver authoring guide
12
+ */
13
+
14
+ // ─── Duration parsing ────────────────────────────────────────────────
15
+
16
+ /**
17
+ * Parse a CSS-style duration string to milliseconds.
18
+ * Accepts "1s", "500ms", "0.3s", or a raw number (treated as ms).
19
+ * Returns a safe fallback (500ms) for garbage input.
20
+ *
21
+ * @param {string|number} duration
22
+ * @returns {number} Duration in milliseconds (≥ 0)
23
+ *
24
+ * @example
25
+ * parseDurationMs('1s') // → 1000
26
+ * parseDurationMs('200ms') // → 200
27
+ * parseDurationMs(300) // → 300
28
+ * parseDurationMs('nope') // → 500
29
+ */
30
+ export function parseDurationMs (duration) {
31
+ if (typeof duration === 'number' && !Number.isNaN(duration)) return Math.max(0, duration)
32
+ const str = String(duration)
33
+ const m = str.match(/^(\d+(?:\.\d+)?)\s*(ms|s)?$/i)
34
+ if (!m) return 500
35
+ const val = parseFloat(m[1])
36
+ const unit = (m[2] || 's').toLowerCase()
37
+ return unit === 'ms' ? Math.max(0, val) : Math.max(0, val * 1000)
38
+ }
39
+
40
+ /**
41
+ * Parse duration to seconds (convenience for libs like GSAP that use seconds).
42
+ *
43
+ * @param {string|number} duration
44
+ * @returns {number} Duration in seconds (≥ 0)
45
+ */
46
+ export function parseDurationSec (duration) {
47
+ return parseDurationMs(duration) / 1000
48
+ }
49
+
50
+ // ─── End-state application ───────────────────────────────────────────
51
+
52
+ /**
53
+ * Apply the final zoom-in state to an element based on its current class.
54
+ * This is the DOM cleanup that runs after the animation finishes.
55
+ *
56
+ * What it does for each role:
57
+ * - `is-new-current-view` → becomes `is-current-view`, no-events removed, final transform applied
58
+ * - `is-previous-view` → no-events removed, final transform applied
59
+ * - `is-last-view` → no-events removed, final transform applied
60
+ *
61
+ * @param {HTMLElement} element - The view element
62
+ * @param {object} currentStage - The snapshot with `views[]` array
63
+ */
64
+ export function applyZoomInEndState (element, currentStage) {
65
+ if (element.classList.contains('is-new-current-view')) {
66
+ const v = currentStage.views[0].forwardState
67
+ element.classList.replace('is-new-current-view', 'is-current-view')
68
+ element.classList.remove('zoom-current-view', 'has-no-events')
69
+ element.style.transformOrigin = v.origin
70
+ element.style.transform = v.transform
71
+ return
72
+ }
73
+ if (element.classList.contains('is-previous-view')) {
74
+ const v = currentStage.views[1].forwardState
75
+ element.classList.remove('zoom-previous-view', 'has-no-events')
76
+ element.style.transformOrigin = v.origin
77
+ element.style.transform = v.transform
78
+ return
79
+ }
80
+ if (element.classList.contains('is-last-view')) {
81
+ const v = currentStage.views[2].forwardState
82
+ element.classList.remove('zoom-last-view', 'has-no-events')
83
+ element.style.transformOrigin = v.origin
84
+ element.style.transform = v.transform
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Apply the final zoom-out state to the previous view (it becomes current).
90
+ *
91
+ * @param {HTMLElement} element
92
+ * @param {{ origin?: string, transform: string }} backwardState
93
+ */
94
+ export function applyZoomOutPreviousState (element, backwardState) {
95
+ element.classList.remove('zoom-previous-view-reverse', 'has-no-events', 'has-effect', 'has-effect-reverse')
96
+ element.style.removeProperty('--z-effect-filter')
97
+ element.style.transformOrigin = '0 0'
98
+ element.style.transform = backwardState.transform
99
+ }
100
+
101
+ /**
102
+ * Apply the final zoom-out state to the last view (it becomes previous).
103
+ *
104
+ * @param {HTMLElement} element
105
+ * @param {{ origin: string, transform: string }} backwardState
106
+ */
107
+ export function applyZoomOutLastState (element, backwardState) {
108
+ element.classList.remove('zoom-last-view-reverse', 'has-no-events', 'has-effect', 'has-effect-reverse')
109
+ element.style.removeProperty('--z-effect-filter')
110
+ element.style.transformOrigin = backwardState.origin
111
+ element.style.transform = backwardState.transform
112
+ }
113
+
114
+ // ─── DOM removal ─────────────────────────────────────────────────────
115
+
116
+ /**
117
+ * Safely remove a view from the canvas. Handles wrapped elements (e.g. Svelte
118
+ * components that get an extra parent div).
119
+ *
120
+ * @param {HTMLElement} element - The view to remove
121
+ * @param {HTMLElement} canvas - The canvas container
122
+ */
123
+ export function removeViewFromCanvas (element, canvas) {
124
+ try {
125
+ if (canvas) canvas.removeChild(element)
126
+ } catch (e) {
127
+ try {
128
+ if (element.parentElement) canvas.removeChild(element.parentElement)
129
+ } catch (e2) {
130
+ // Element already removed or re-parented — safe to ignore.
131
+ }
132
+ }
133
+ }
134
+
135
+ // ─── Visibility helpers ──────────────────────────────────────────────
136
+
137
+ /**
138
+ * Make a view visible and interactive before animating it.
139
+ * Views start hidden (class `hide` for opacity, `contentVisibility: hidden` via view-visibility)
140
+ * when inserted by prepareAndInsertView. Drivers must call this before running any animation.
141
+ *
142
+ * @param {...HTMLElement} elements - One or more view elements (nulls are skipped)
143
+ */
144
+ export function showViews (...elements) {
145
+ for (const el of elements) {
146
+ if (!el) continue
147
+ el.classList.remove('hide')
148
+ showViewContent(el)
149
+ }
150
+ }
151
+
152
+ // ─── Lateral (same-level) transition ─────────────────────────────────
153
+
154
+ /**
155
+ * Instant lateral navigation: swap current view without animation.
156
+ * This is the shared implementation that drivers use as a baseline.
157
+ * Drivers that want animated lateral transitions can override this.
158
+ *
159
+ * @param {object} spec - Full lateral spec from the engine
160
+ * @param {function} onComplete - Must be called when done
161
+ */
162
+ export function runLateralInstant (spec, onComplete) {
163
+ const {
164
+ currentView: incomingView,
165
+ previousView: outgoingView,
166
+ backView,
167
+ backViewState,
168
+ lastView,
169
+ lastViewState,
170
+ incomingTransformEnd,
171
+ currentStage,
172
+ canvas
173
+ } = spec
174
+ const v0 = currentStage.views[0]
175
+
176
+ showViews(incomingView)
177
+ incomingView.classList.replace('is-new-current-view', 'is-current-view')
178
+ incomingView.classList.remove('zoom-current-view', 'has-no-events')
179
+ incomingView.style.transformOrigin = v0.forwardState.origin
180
+ incomingView.style.transform = incomingTransformEnd || v0.forwardState.transform
181
+
182
+ if (backView && backViewState) backView.style.transform = backViewState.transformEnd
183
+ if (lastView && lastViewState) lastView.style.transform = lastViewState.transformEnd
184
+
185
+ if (!spec.keepAlive) removeViewFromCanvas(outgoingView, canvas)
186
+ onComplete()
187
+ }
188
+
189
+ // ─── Safety timeout ──────────────────────────────────────────────────
190
+
191
+ /**
192
+ * Default safety buffer (ms) beyond the parsed duration. Ensures onComplete
193
+ * fires even if animationend / Promise.all never settles (element removed,
194
+ * duration 0, browser quirk, etc.).
195
+ */
196
+ export const SAFETY_BUFFER_MS = 150
197
+
198
+ /**
199
+ * Create a finish-once guard: returns a `finish()` function that only runs
200
+ * the first time it's called, and clears the safety timer.
201
+ *
202
+ * Use this to avoid the "completed" flag boilerplate in every driver.
203
+ *
204
+ * @param {function} cleanup - The actual cleanup + onComplete work
205
+ * @param {number} timeoutMs - Safety timeout duration
206
+ * @returns {{ finish: function, safetyTimer: number }}
207
+ *
208
+ * @example
209
+ * const { finish, safetyTimer } = createFinishGuard(() => {
210
+ * cancelAnimations()
211
+ * applyFinalState()
212
+ * onComplete()
213
+ * }, durationMs + SAFETY_BUFFER_MS)
214
+ *
215
+ * // In your animation's onComplete:
216
+ * animation.onfinish = finish
217
+ *
218
+ * // The safetyTimer ensures finish() runs even if onfinish never fires.
219
+ */
220
+ export function createFinishGuard (cleanup, timeoutMs) {
221
+ let completed = false
222
+ const safetyTimer = setTimeout(() => {
223
+ if (!completed) { completed = true; cleanup() }
224
+ }, timeoutMs)
225
+
226
+ return {
227
+ finish () {
228
+ if (completed) return
229
+ completed = true
230
+ clearTimeout(safetyTimer)
231
+ cleanup()
232
+ },
233
+ safetyTimer
234
+ }
235
+ }
236
+
237
+ // ─── Matrix interpolation toolkit ────────────────────────────────────
238
+ // Used by matrix-based drivers (anime, motion, or any custom driver that
239
+ // needs to interpolate transforms through computed matrices).
240
+
241
+ /** Identity CSS matrix components. */
242
+ export function identityMatrix () {
243
+ return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }
244
+ }
245
+
246
+ /**
247
+ * Parse a CSS matrix() string into components.
248
+ * @param {string} mStr - e.g. "matrix(1, 0, 0, 1, 100, 50)"
249
+ * @returns {{ a, b, c, d, e, f }}
250
+ */
251
+ export function parseMatrixString (mStr) {
252
+ if (!mStr || mStr === 'none') return identityMatrix()
253
+ const m = String(mStr).match(
254
+ /matrix\(([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+)\)/
255
+ )
256
+ if (!m) return identityMatrix()
257
+ return {
258
+ a: parseFloat(m[1]) || 1,
259
+ b: parseFloat(m[2]) || 0,
260
+ c: parseFloat(m[3]) || 0,
261
+ d: parseFloat(m[4]) || 1,
262
+ e: parseFloat(m[5]) || 0,
263
+ f: parseFloat(m[6]) || 0,
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Convert matrix components back to a CSS matrix() string.
269
+ * @param {{ a, b, c, d, e, f }} m
270
+ * @returns {string}
271
+ */
272
+ export function matrixToString (m) {
273
+ return `matrix(${m.a}, ${m.b}, ${m.c}, ${m.d}, ${m.e}, ${m.f})`
274
+ }
275
+
276
+ /** Linear interpolation between two numbers. */
277
+ export function lerp (a, b, t) {
278
+ return a + (b - a) * t
279
+ }
280
+
281
+ /**
282
+ * Interpolate between two matrix objects.
283
+ * @param {{ a,b,c,d,e,f }} from
284
+ * @param {{ a,b,c,d,e,f }} to
285
+ * @param {number} t - Progress 0..1
286
+ * @returns {{ a,b,c,d,e,f }}
287
+ */
288
+ export function interpolateMatrix (from, to, t) {
289
+ const tt = Math.max(0, Math.min(1, t))
290
+ return {
291
+ a: lerp(from.a, to.a, tt),
292
+ b: lerp(from.b, to.b, tt),
293
+ c: lerp(from.c, to.c, tt),
294
+ d: lerp(from.d, to.d, tt),
295
+ e: lerp(from.e, to.e, tt),
296
+ f: lerp(from.f, to.f, tt),
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Read the browser-computed matrix for a given transform + origin pair.
302
+ * ⚠️ Forces a reflow (getBoundingClientRect). Use sparingly — once per
303
+ * element per animation setup, not per frame.
304
+ *
305
+ * @param {HTMLElement} element
306
+ * @param {string} origin - CSS transform-origin
307
+ * @param {string} transformStr - CSS transform value
308
+ * @returns {{ a,b,c,d,e,f }}
309
+ */
310
+ export function readComputedMatrix (element, origin, transformStr) {
311
+ element.style.transformOrigin = origin
312
+ element.style.transform = transformStr
313
+ try { element.getBoundingClientRect() } catch {}
314
+ return parseMatrixString(getComputedStyle(element).transform)
315
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Transition driver factory.
3
+ * Returns a driver object with runTransition(spec, onComplete).
4
+ *
5
+ * Built-in drivers: 'css' (default), 'waapi', 'none', 'anime', 'gsap', 'motion'.
6
+ * Custom drivers: pass a function(spec, onComplete) directly.
7
+ *
8
+ * @param {string|function} driverOption
9
+ * @returns {{ runTransition: function }}
10
+ */
11
+ import { runTransition as cssRun } from './css-transition.js'
12
+ import { runTransition as noneRun } from './none-transition.js'
13
+ import { runTransition as waapiRun } from './waapi-transition.js'
14
+ import { runTransition as animeRun } from './anime-transition.js'
15
+ import { runTransition as gsapRun } from './gsap-transition.js'
16
+ import { runTransition as motionRun } from './motion-transition.js'
17
+
18
+ const DRIVERS = {
19
+ css: cssRun,
20
+ waapi: waapiRun,
21
+ none: noneRun,
22
+ anime: animeRun,
23
+ gsap: gsapRun,
24
+ motion: motionRun,
25
+ }
26
+
27
+ export function getDriver (driverOption) {
28
+ if (typeof driverOption === 'function') {
29
+ return {
30
+ runTransition (spec, onComplete) {
31
+ driverOption(spec, onComplete)
32
+ }
33
+ }
34
+ }
35
+ const name = typeof driverOption === 'string' ? driverOption.toLowerCase() : 'css'
36
+ const run = DRIVERS[name] || DRIVERS.css
37
+ return { runTransition: run }
38
+ }
39
+
40
+ // Named exports for direct imports and tests
41
+ export { runTransition as cssTransition } from './css-transition.js'
42
+ export { runTransition as noneTransition } from './none-transition.js'
43
+ export { runTransition as waapiTransition } from './waapi-transition.js'
44
+ export { runTransition as animeTransition } from './anime-transition.js'
45
+ export { runTransition as gsapTransition } from './gsap-transition.js'
46
+ export { runTransition as motionTransition } from './motion-transition.js'
47
+
48
+ // Re-export helpers for community drivers
49
+ export * from './driver-helpers.js'
@@ -1,6 +0,0 @@
1
- /**
2
- * zumly v0.9.11
3
- * Author Juan Martín Muda, @license MIT
4
- * https://zumly.org
5
- */
6
- function t(t){if(void 0!==t&&"none"===t[0].toLowerCase())return!0;if(void 0!==t&&t.length>0){return(t=>[...new Set(t)])(t.map(t=>t.toLowerCase())).every(t=>-1!==["blur","sepia","saturate"].indexOf(t))}}function e(t){var e="",n="";if(void 0!==t)return t.map(t=>{e+=""+("blur"===t.toLowerCase()?"blur(0px) ":"sepia"===t.toLowerCase()?"sepia(0) ":"saturate"===t.toLowerCase()?"saturate(0) ":"none"),n+=""+("blur"===t.toLowerCase()?"blur(0.8px) ":"sepia"===t.toLowerCase()?"sepia(5) ":"saturate"===t.toLowerCase()?"saturate(8) ":"none")}),[e,n]}function n(t,e,n){t[e]=n}function s(t,e,s,i,o={isRequired:!1,defaultValue:0,allowedValues:0,hasValidation:0,hasAssignFunction:0}){var a=`'${e}' property is required when instance is defined`,d=`'${e}' property has problems`,c=void 0!==s,l=void 0!==o.defaultValue,u=void 0!==o.hasValidation,h=void 0!==o.hasAssignFunction;if("string"===i||"object"===i||"boolean"===i)var m=typeof s===i;else"array"===i&&(m=Array.isArray(s));o.isRequired&&(c&&m?n(t,e,s):r(!1,a,"error")),!l||u||h||(c&&m?n(t,e,s):void 0===s?n(t,e,o.defaultValue):r(!1,d,"error")),u&&l&&!h&&(c&&m&&o.hasValidation?n(t,e,s):void 0===s?n(t,e,o.defaultValue):r(!1,d,"error")),u&&l&&h&&(c&&m&&o.hasValidation?n(t,e,o.hasAssignFunction):void 0===s?n(t,e,o.defaultValue):r(!1,d,"error"))}function i(t,e,n){const s=e;[{name:"current-view",stage:s.views[0]},{name:"previous-view",stage:s.views[1]},{name:"last-view",stage:s.views[2]}].map(e=>{"zoomOut"===t&&void 0!==e.stage&&(document.documentElement.style.setProperty(`--${e.name}-transform-start-${n}`,e.stage.forwardState.transform),document.documentElement.style.setProperty(`--${e.name}-transform-end-${n}`,e.stage.backwardState.transform),document.documentElement.style.setProperty(`--${e.name}-transformOrigin-start-${n}`,e.stage.forwardState.origin),document.documentElement.style.setProperty(`--${e.name}-transformOrigin-end-${n}`,e.stage.backwardState.origin),document.documentElement.style.setProperty(`--${e.name}-opacity-start-${n}`,1),document.documentElement.style.setProperty(`--${e.name}-filter-start-${n}`,e.stage.forwardState.filter),document.documentElement.style.setProperty(`--${e.name}-filter-end-${n}`,e.stage.backwardState.filter),"current-view"===e.name?(document.documentElement.style.setProperty("--zoom-duration-"+n,e.stage.backwardState.duration),document.documentElement.style.setProperty("--zoom-ease-"+n,e.stage.backwardState.ease),document.documentElement.style.setProperty(`--${e.name}-opacity-end-${n}`,0),document.documentElement.style.setProperty(`--${e.name}-filter-start-${n}`,"none"),document.documentElement.style.setProperty(`--${e.name}-filter-end-${n}`,"none")):document.documentElement.style.setProperty(`--${e.name}-opacity-end-${n}`,1)),"zoomIn"===t&&void 0!==e.stage&&(document.documentElement.style.setProperty(`--${e.name}-transform-start-${n}`,e.stage.backwardState.transform),document.documentElement.style.setProperty(`--${e.name}-transform-end-${n}`,e.stage.forwardState.transform),document.documentElement.style.setProperty(`--${e.name}-transformOrigin-start-${n}`,e.stage.backwardState.origin),document.documentElement.style.setProperty(`--${e.name}-transformOrigin-end-${n}`,e.stage.forwardState.origin),document.documentElement.style.setProperty(`--${e.name}-filter-start-${n}`,e.stage.backwardState.filter),document.documentElement.style.setProperty(`--${e.name}-filter-end-${n}`,e.stage.forwardState.filter),"current-view"===e.name?(document.documentElement.style.setProperty("--zoom-duration-"+n,e.stage.forwardState.duration),document.documentElement.style.setProperty("--zoom-ease-"+n,e.stage.forwardState.ease),document.documentElement.style.setProperty(`--${e.name}-opacity-start-${n}`,0),document.documentElement.style.setProperty(`--${e.name}-filter-start-${n}`,"none"),document.documentElement.style.setProperty(`--${e.name}-filter-end-${n}`,"none")):document.documentElement.style.setProperty(`--${e.name}-opacity-start-${n}`,1),document.documentElement.style.setProperty(`--${e.name}-opacity-end-${n}`,1))})}async function o(t,e,n,s,i){var o=null;o=s?t:t.dataset.to;var r=document.createElement("template");if("object"==typeof n[o]&&void 0!==n[o].render)r.innerHTML=await n[o].render();else if("function"==typeof n[o]){var a=document.createElement("div");new n[o]({target:a,context:i,props:t.dataset});a.classList.add("z-view"),r.content.appendChild(a)}else r.innerHTML=n[o];let d=r.content.querySelector(".z-view");s?d.classList.add("is-current-view"):(d.classList.add("is-new-current-view"),d.classList.add("has-no-events"),d.classList.add("hide"),d.classList.add("performance")),d.style.transformOrigin="0 0",d.dataset.viewName=o;await e.append(r.content);"object"==typeof n[o]&&void 0!==n[o].mounted&&"function"==typeof n[o].mounted()&&await n[o].mounted()}function r(t,e,n){e&&"welcome"===n&&console.info("%c Zumly %c "+e,"background: #424085; color: white; border-radius: 3px;","color: #424085"),e&&t&&("info"===n||void 0===n)&&console.info("%c Zumly %c "+e,"background: #6679A3; color: #304157; border-radius: 3px;","color: #6679A3"),e&&"warn"===n&&console.warn("%c Zumly %c "+e,"background: #DCBF53; color: #424085; border-radius: 3px;","color: #424085"),e&&"error"===n&&console.error("%c Zumly %c "+e,"background: #BE4747; color: white; border-radius: 3px;","color: #424085")}window.requestIdleCallback=window.requestIdleCallback||function(t){var e=Date.now();return setTimeout(()=>{t({didTimeout:!1,timeRemaining:()=>Math.max(0,50-(Date.now()-e))})},1)};class a{constructor(i){var o,d;this.instance=a.counter,this.storedViews=[],this.currentStage=null,this.storedPreviousScale=[1],this.trace=[],this.blockEvents=!1,this.touchstartX=0,this.touchstartY=0,this.touchendX=0,this.touchendY=0,this.touching=!1,d=this,(o=i)&&"object"==typeof o?(n(d,"options",!0),s(d,"mount",o.mount,"string",{isRequired:!0}),s(d,"initialView",o.initialView,"string",{isRequired:!0}),s(d,"views",o.views,"object",{isRequired:!0}),s(d,"debug",o.debug,"boolean",{defaultValue:!1}),s(d,"componentContext",o.componentContext,"object",{isRequired:!1,defaultValue:new Map}),o.transitions&&"object"==typeof o.transitions?(s(d,"cover",o.transitions.cover,"string",{defaultValue:"width",hasValidation:()=>{["height","width"].indexOf(o.transitions.cover.toLowerCase())}}),s(d,"duration",o.transitions.duration,"string",{defaultValue:"1s"}),s(d,"ease",o.transitions.ease,"string",{defaultValue:"ease-in-out"}),s(d,"effects",o.transitions.effects,"array",{defaultValue:["none","none"],hasValidation:t(o.transitions.effects),hasAssignFunction:e(o.transitions.effects)})):(n(d,"cover","width"),n(d,"duration","1s"),n(d,"ease","ease-in-out"),n(d,"effects",["none","none"]))):r(!1,"'options' object has to be provided when instance is defined","error"),this.options?(this._onZoom=this.onZoom.bind(this),this._onZoomInHandlerStart=this.onZoomInHandlerStart.bind(this),this._onZoomInHandlerEnd=this.onZoomInHandlerEnd.bind(this),this._onZoomOutHandlerStart=this.onZoomOutHandlerStart.bind(this),this._onZoomOutHandlerEnd=this.onZoomOutHandlerEnd.bind(this),this._onTouchStart=this.onTouchStart.bind(this),this._onTouchEnd=this.onTouchEnd.bind(this),this._onKeyUp=this.onKeyUp.bind(this),this._onWeel=this.onWeel.bind(this),this.canvas=document.querySelector(this.mount),this.canvas.setAttribute("tabindex",0),this.canvas.addEventListener("mouseup",this._onZoom,!1),this.canvas.addEventListener("touchend",this._onZoom,!1),this.canvas.addEventListener("touchstart",this._onTouchStart,{passive:!0}),this.canvas.addEventListener("touchend",this._onTouchEnd,!1),this.canvas.addEventListener("keyup",this._onKeyUp,!1),this.canvas.addEventListener("wheel",this._onWeel,{passive:!0})):this.notify("is unable to start: no {options} have been passed to the Zumly's instance.","error")}storeViews(t){this.tracing("storedViews()"),this.storedViews.push(t)}setPreviousScale(t){this.tracing("setPreviousScale()"),this.storedPreviousScale.push(t)}tracing(t){if("ended"===t){const t=this.trace.map((t,e)=>""+(0===e?`Instance ${this.instance}: ${t}`:""+t)).join(" > ");this.notify(t),this.trace=[]}else this.trace.push(t)}static get counter(){return a._counter=(a._counter||0)+1,a._counter}notify(t,e){return r(this.debug,t,e)}zoomLevel(){return this.storedViews.length}async init(){this.options&&(this.tracing("init()"),function(t){var e=document.createElement("style");let n="";["current-view","previous-view","last-view"].map(e=>{n+=`\n .zoom-${e}-${t} {\n animation-name: zoom-${e}-${t};\n animation-duration: var(--zoom-duration-${t});\n animation-timing-function: var(--zoom-ease-${t});\n }\n @keyframes zoom-${e}-${t} {\n 0% {\n transform-origin: var(--${e}-transformOrigin-start-${t});\n transform: var(--${e}-transform-start-${t});\n opacity: var(--${e}-opacity-start-${t});\n filter: var(--${e}-filter-start-${t})\n }\n 100% {\n transform-origin: var(--${e}-transformOrigin-end-${t});\n transform: var(--${e}-transform-end-${t});\n opacity: var(--${e}-opacity-end-${t});\n filter: var(--${e}-filter-end-${t})\n }\n }\n `}),e.innerHTML=n,document.head.appendChild(e)}(this.instance),await o(this.initialView,this.canvas,this.views,"init",this.componentContext),this.storeViews({zoomLevel:this.storedViews.length,views:[{viewName:this.initialView,backwardState:{origin:"0 0",transform:""}}]}),this.notify(""+(this.instance>1?`instance nº ${this.instance} is active.`:`is running! Instance nº ${this.instance} is active. ${this.debug?"Debug is active, can be deactivate by setting 'debug: false' when you define the instance.":""}\n More tips & docs at https://zumly.org`),"welcome"))}async zoomIn(t){this.tracing("zoomIn()");var e=this.instance;const n=this.canvas,s=n.getBoundingClientRect();var r=s.left,a=s.top;const d=this.storedPreviousScale[this.storedPreviousScale.length-1];this.tracing("renderView()"),await o(t,n,this.views,!1,this.componentContext),t.classList.add("zoomed");const c=t.getBoundingClientRect();var l=n.querySelector(".is-new-current-view"),u=n.querySelector(".is-current-view"),h=n.querySelector(".is-previous-view"),m=n.querySelector(".is-last-view");null!==m&&n.removeChild(m);const v=l.getBoundingClientRect(),w=v.width/c.width,g=1/w,f=v.height/c.height,p=1/f;var y=t.dataset.withDuration||this.duration,$=t.dataset.withEease||this.ease,E=this.effects[0],S=this.effects[1],b=this.cover;if("width"===b)var L=w,z=g;else"height"===b&&(L=f,z=p);this.setPreviousScale(L);var V=`translate(${c.x-r+(c.width-v.width*z)/2}px, ${c.y-a+(c.height-v.height*z)/2}px) scale(${z})`;l.style.transform=V,u.classList.add("is-previous-view"),u.classList.remove("is-current-view");const k=u.getBoundingClientRect();var O=u.style.transform;u.style.transformOrigin=`${c.x+c.width/2-k.x}px ${c.y+c.height/2-k.y}px`;const P=s.width/2-c.width/2-c.x+k.x,Z=s.height/2-c.height/2-c.y+k.y,x=`translate(${P}px, ${Z}px) scale(${L})`;u.style.transform=x;var C=t.getBoundingClientRect(),H=`translate(${C.x-r+(C.width-v.width)/2}px, ${C.y-a+(C.height-v.height)/2}px)`;if(null!==h){h.classList.remove("is-previous-view"),h.classList.add("is-last-view");var _=h.style.transform,I=u.getBoundingClientRect();h.style.transform=`translate(${P-r}px, ${Z-a}px) scale(${L*d})`;var T=h.querySelector(".zoomed").getBoundingClientRect();h.style.transform=_,u.style.transform=O;var q=u.getBoundingClientRect(),R=`translate(${s.width/2-c.width/2-c.x+(q.x-T.x)+I.x-r+(I.width-T.width)/2}px, ${s.height/2-c.height/2-c.y+(q.y-T.y)+I.y-a+(I.height-T.height)/2}px) scale(${L*d})`}else u.style.transform=O;var Y={zoomLevel:this.storedViews.length,views:[]};const A=l?{viewName:l.dataset.viewName,backwardState:{origin:l.style.transformOrigin,duration:y,ease:$,transform:V,filter:E},forwardState:{origin:l.style.transformOrigin,duration:y,ease:$,transform:H,filter:S}}:null,B=u?{viewName:u.dataset.viewName,backwardState:{origin:u.style.transformOrigin,duration:y,ease:$,transform:O,filter:window.getComputedStyle(document.documentElement).getPropertyValue("--previous-view-filter-end-"+e)},forwardState:{origin:u.style.transformOrigin,duration:y,ease:$,transform:x,filter:S}}:null,N=h?{viewName:h.dataset.viewName,backwardState:{origin:h.style.transformOrigin,duration:y,ease:$,transform:_,filter:window.getComputedStyle(document.documentElement).getPropertyValue("--previous-view-filter-end-"+e)},forwardState:{origin:h.style.transformOrigin,duration:y,ease:$,transform:R,filter:S}}:null,j=m?{viewName:m}:null;null!==A&&Y.views.push(A),null!==B&&Y.views.push(B),null!==N&&Y.views.push(N),null!==j&&Y.views.push(j),this.storeViews(Y),this.currentStage=this.storedViews[this.storedViews.length-1],this.tracing("setCSSVariables()"),i("zoomIn",this.currentStage,this.instance),u.classList.add("performance"),null!==h&&h.classList.add("performance"),l.classList.remove("hide"),l.addEventListener("animationstart",this._onZoomInHandlerStart),l.addEventListener("animationend",this._onZoomInHandlerEnd),u.addEventListener("animationend",this._onZoomInHandlerEnd),null!==h&&h.addEventListener("animationend",this._onZoomInHandlerEnd),l.classList.add("zoom-current-view-"+e),u.classList.add("zoom-previous-view-"+e),null!==h&&h.classList.add("zoom-last-view-"+e)}zoomOut(){this.tracing("zoomOut()"),this.blockEvents=!0,this.storedPreviousScale.pop();var t=this.instance;const e=this.canvas;this.currentStage=this.storedViews[this.storedViews.length-1];const n=this.currentStage.views[3];var s=e.querySelector(".is-current-view"),o=e.querySelector(".is-previous-view"),r=e.querySelector(".is-last-view");(this.tracing("setCSSVariables()"),i("zoomOut",this.currentStage,this.instance),s.classList.remove("performance"),o.querySelector(".zoomed").classList.remove("zoomed"),o.classList.remove("is-previous-view"),o.classList.add("is-current-view"),o.classList.remove("performance"),null!==r&&(r.classList.add("performance"),r.classList.add("is-previous-view"),r.classList.remove("is-last-view"),r.classList.remove("hide")),void 0!==n)&&(e.prepend(n.viewName),e.querySelector(".z-view:first-child").classList.add("hide"));s.addEventListener("animationstart",this._onZoomOutHandlerStart),s.addEventListener("animationend",this._onZoomOutHandlerEnd),o.addEventListener("animationend",this._onZoomOutHandlerEnd),null!==r&&r.addEventListener("animationend",this._onZoomOutHandlerEnd),s.classList.add("zoom-current-view-"+t),o.classList.add("zoom-previous-view-"+t),null!==r&&r.classList.add("zoom-last-view-"+t),this.storedViews.pop()}onZoom(t){this.storedViews.length>1&&!this.blockEvents&&!t.target.classList.contains("zoom-me")&&null===t.target.closest(".is-current-view")&&!this.touching&&(this.tracing("onZoom()"),t.stopPropagation(),this.zoomOut()),this.blockEvents||!t.target.classList.contains("zoom-me")||this.touching||(this.tracing("onZoom()"),t.stopPropagation(),this.zoomIn(t.target))}onKeyUp(t){this.tracing("onKeyUp()"),"ArrowLeft"!==t.key&&"ArrowDown"!==t.key||(t.preventDefault(),this.storedViews.length>1&&!this.blockEvents?this.zoomOut():this.notify("is on level zero. Can't zoom out. Trigger: "+t.key,"warn")),"ArrowRight"!==t.key&&"ArrowUp"!==t.key||(t.preventDefault(),this.notify(t.key+"has not actions defined"))}onWeel(t){this.blockEvents||(this.tracing("onWeel()"),t.deltaY,t.deltaY>0&&this.storedViews.length>1&&!this.blockEvents&&this.zoomOut())}onTouchStart(t){this.tracing("onTouchStart()"),this.touching=!0,this.touchstartX=t.changedTouches[0].screenX,this.touchstartY=t.changedTouches[0].screenY}onTouchEnd(t){this.blockEvents||(this.tracing("onTouchEnd()"),this.touchendX=t.changedTouches[0].screenX,this.touchendY=t.changedTouches[0].screenY,this.handleGesture(t))}handleGesture(t){t.stopPropagation(),this.tracing("handleGesture()"),this.touchendX<this.touchstartX-30&&(this.storedViews.length>1&&!this.blockEvents?(this.tracing("swipe left"),this.zoomOut()):this.notify("is on level zero. Can't zoom out. Trigger: Swipe left","warn")),this.touchendY<this.touchstartY-10&&(this.storedViews.length>1&&!this.blockEvents?this.tracing("swipe up"):this.notify("is on level zero. Can't zoom out. Trigger: Swipe up","warn")),this.touchendY===this.touchstartY&&!this.blockEvents&&t.target.classList.contains("zoom-me")&&this.touching&&(this.touching=!1,this.tracing("tap"),t.preventDefault(),this.zoomIn(t.target)),this.touchendY===this.touchstartY&&this.storedViews.length>1&&!this.blockEvents&&!t.target.classList.contains("zoom-me")&&null===t.target.closest(".is-current-view")&&this.touching&&(this.touching=!1,this.tracing("tap"),this.zoomOut())}onZoomOutHandlerStart(t){this.tracing("onZoomOutHandlerStart()"),this.blockEvents=!0,t.target.removeEventListener("animationstart",this._onZoomOutHandlerStart)}onZoomOutHandlerEnd(t){this.tracing("onZoomOutHandlerEnd()");const e=t.target;var n=this.currentStage;if(e.removeEventListener("animationend",this._onZoomOutHandlerEnd),e.classList.contains("zoom-current-view-"+this.instance)){try{this.canvas.removeChild(e)}catch(t){console.debug("Error when trying to remove element after zoom out. Trying to remove its parent instead...");try{this.canvas.removeChild(e.parentElement)}catch(t){console.debug("Error when trying to remove elemont after zoom out:",t),console.debug("Element to remove was:",e)}}this.blockEvents=!1}if(e.classList.contains("zoom-previous-view-"+this.instance)){var s=n.views[1].backwardState.origin,i=n.views[1].backwardState.transform;e.classList.remove("performance"),e.classList.remove("zoom-previous-view-"+this.instance),e.style.transformOrigin=s,e.style.transform=i,e.style.filter="none",2===n.views.length&&this.tracing("ended")}e.classList.contains("zoom-last-view-"+this.instance)&&(s=n.views[2].backwardState.origin,i=n.views[2].backwardState.transform,e.classList.remove("performance"),e.classList.remove("zoom-last-view-"+this.instance),e.style.transformOrigin=s,e.style.transform=i,n.views.length>2&&this.tracing("ended"))}onZoomInHandlerStart(t){this.tracing("onZoomInHandlerStart()"),this.blockEvents=!0,t.target.removeEventListener("animationstart",this._onZoomInHandlerStart)}onZoomInHandlerEnd(t){this.tracing("onZoomInHandlerEnd()");const e=t.target;var n=this.currentStage;if(t.target.classList.contains("is-new-current-view")){this.blockEvents=!1;var s="current-view",i=n.views[0].forwardState.transform,o=n.views[0].forwardState.origin;e.classList.remove("is-new-current-view"),e.classList.add("is-current-view")}else t.target.classList.contains("is-previous-view")?(s="previous-view",i=n.views[1].forwardState.transform,o=n.views[1].forwardState.origin,2===n.views.length&&this.tracing("ended")):(s="last-view",i=n.views[2].forwardState.transform,o=n.views[2].forwardState.origin,n.views.length>2&&this.tracing("ended"));e.classList.remove("performance"),e.classList.remove(`zoom-${s}-${this.instance}`),e.classList.remove("has-no-events"),e.style.transformOrigin=o,e.style.transform=i,e.style.filter=window.getComputedStyle(document.documentElement).getPropertyValue(`--${s}-filter-end-${this.instance}`),e.removeEventListener("animationend",this._onZoomInHandlerEnd)}}export default a;