zumly 0.9.11 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,22 +5,27 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- Zumly is a Javascript library for building zooming user interfaces. Create zooming experiences using web standards.
8
+ Zumly is a JavaScript library for building zooming user interfaces. Create zooming experiences using web standards.
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/zumly"><img src="https://img.shields.io/npm/v/zumly.svg"></a>
13
13
  </p>
14
14
 
15
+ ## Status
16
+
17
+ Zumly is under active development. The library is usable in production with a stable core API; view preloading, prefetch on hover, and multiple view sources (HTML, URL, async functions, web components) are supported. See [docs/roadMap.md](docs/roadMap.md) for improvement topics and roadmap.
18
+
15
19
  ## Overview
16
20
 
17
- Zumly is a frontend library for creating zoomable user interfaces ([ZUI](https://en.wikipedia.org/wiki/Zooming_user_interface)). Instead of hyperlinks and windows, Zumly uses zooming as a metaphor for browsing through information. This way it offers an infinite virtual canvas in which elements can be zoomed themselves to reveal further details.
21
+ Zumly is a frontend library for creating **zoomable user interfaces** ([ZUI](https://en.wikipedia.org/wiki/Zooming_user_interface)). Instead of hyperlinks and windows, Zumly uses zooming as the main way to move through information: an infinite virtual canvas where you zoom into elements to reveal more detail.
18
22
 
19
- To be more flexible Zumly is primarily focused on zooming transitions without caring about visual design. Most CSS frameworks or custom designs work with Zumly.
23
+ The library focuses on **zoom transitions** and stays **UI-agnostic**—you bring your own CSS and layout. Any design system or custom styling works with Zumly.
20
24
 
21
25
  ## Installation
22
26
 
23
27
  ### NPM
28
+
24
29
  ```sh
25
30
  npm install zumly
26
31
 
@@ -29,235 +34,200 @@ npm install zumly
29
34
  yarn add zumly
30
35
  ```
31
36
 
32
- ### Content delivery networks (CDN)
33
- Include https://unpkg.com/zumly in your project in a `<script>` tag.
37
+ ### CDN
34
38
 
39
+ Include Zumly in your project via a `<script>` tag from [unpkg.com/zumly](https://unpkg.com/zumly).
35
40
 
36
41
  ### Direct download
37
42
 
38
- Download Zumly files from [unpkg.com](https://unpkg.com/zumly/). Files are in `dist` folder.
39
-
43
+ Download the built files from [unpkg.com/zumly](https://unpkg.com/zumly/) (see the `dist` folder).
40
44
 
41
45
  ## Setup
42
46
 
47
+ ### Browser bundle (global)
43
48
 
44
- ### ES6 modules
49
+ 1. Add the CSS in your `<head>`:
45
50
 
46
- 1. Add CSS inside `<head>` tag:
47
51
  ```html
48
-
49
52
  <link rel="stylesheet" href="zumly/dist/zumly.css">
50
-
51
- <!-- Or "https://unpkg.com/zumly@0.9.11/dist/zumly.css" -->
52
-
53
+ <!-- or https://unpkg.com/zumly/dist/zumly.css -->
53
54
  ```
54
55
 
55
- 2. Add Zumly as ES6 module:
56
- ```html
57
- <script type="module">
58
- import Zumly from "zumly/dist/zumly.mjs"
59
-
60
- // Or "https://unpkg.com/zumly@0.9.11/dist/zumly.mjs"
61
- </script>
62
- ```
63
-
64
- ### UMD modules
65
-
66
- 1. Add Zumly CSS Styles inside `<head>` tag:
67
- ```html
68
-
69
- <link rel="stylesheet" href="zumly/dist/zumly.css">
70
-
71
- <!-- Or "https://unpkg.com/zumly@0.9.11/dist/zumly.css" -->
72
-
73
- ```
56
+ 2. Load the JS bundle (it exposes `window.Zumly`):
74
57
 
75
- 2. Add Zumly as UMD module:
76
58
  ```html
77
-
78
- <script src="zumly/dist/zumly.umd.js"></script>
79
-
80
- // Or "https://unpkg.com/zumly"
81
-
59
+ <script src="zumly/dist/zumly.js"></script>
60
+ <!-- or https://unpkg.com/zumly/dist/zumly.js -->
82
61
  ```
83
62
 
84
-
85
63
  ## Hello World
86
64
 
87
- 1. Create a container for your Zumly app with `.zumly-canvas`:
65
+ 1. Add a container with the class `zumly-canvas`:
88
66
 
89
67
  ```html
90
-
91
68
  <div class="example zumly-canvas"></div>
92
-
93
69
  ```
94
70
 
95
- 2. Inside `script` tag write this code:
71
+ 2. Create your views and start Zumly:
96
72
 
97
73
  ```js
98
- // Some views
99
74
  const hello = `
100
75
  <div class="z-view">
101
- H E L L O <br>
102
- W <span class="zoom-me" data-to="world">O</span> R L D!
76
+ H E L L O <br>
77
+ W <span class="zoom-me" data-to="world">O</span> R L D!
103
78
  </div>
104
79
  `;
105
80
 
106
81
  const world = `
107
82
  <div class="z-view">
108
- <img src="https://raw.githubusercontent.com/zumly/website/gh-pages/images/world.png"/>
83
+ <img src="https://raw.githubusercontent.com/zumly/website/gh-pages/images/world.png" alt="World">
109
84
  </div>
110
85
  `;
111
86
 
112
- // Zumly instance
113
87
  const app = new Zumly({
114
88
  mount: '.example',
115
89
  initialView: 'hello',
116
- views: {
117
- hello,
118
- world
119
- }
120
- })
121
-
122
- app.init()
90
+ views: { hello, world },
91
+ });
123
92
 
93
+ await app.init();
124
94
  ```
125
95
 
126
- - See this example live at [codePen](https://codepen.io/zumly/pen/gOPQovd)
127
-
128
- ### Zumly options
129
-
130
- 1. The Zumly instance:
131
-
132
- ```js
133
- const app = new Zumly({
134
- // Mount DOM Element. String. Required
135
- mount: '.className',
136
- // First rendered view name. String. Required
137
- initialView: 'viewName',
138
- // Store all views. Object. Required
139
- views: {
140
- view1,
141
- view2,
142
- . . .
143
- },
144
- // Customize transitions. Object. Optional
145
- transitions: {
146
- // Effects for background views. Array. ['blur', 'sepia', 'saturate']
147
- effects: ['sepia'],
148
- // How new injected view is adapted. String. Default 'width'
149
- cover: 'height',
150
- // Transition duration. String. Default '1s'
151
- duration: '1300ms' ,
152
- // Transition ease. String. Default 'ease-in-out'
153
- ease: 'cubic-bezier(0.25,0.1,0.25,1)'
154
- },
155
- // Activate debug notifications. Boolean. Default false
156
- debug: true
157
- })
158
- // Initialize instance
159
- app.init()
160
- ```
96
+ - Live example: [CodePen](https://codepen.io/zumly/pen/gOPQovd)
161
97
 
162
- 2. Options for each zoomable element:
98
+ ### Options
163
99
 
164
- - Add `z-view` class in you view container:
100
+ **Zumly constructor:**
165
101
 
166
- ```html
102
+ | Option | Type | Required | Description |
103
+ |--------|------|----------|-------------|
104
+ | `mount` | string | Yes | CSS selector for the canvas element (must have class `zumly-canvas`). |
105
+ | `initialView` | string | Yes | Name of the first view to show. |
106
+ | `views` | object | Yes | Map of view names to view sources (see View sources below). |
107
+ | `preload` | string[] | No | View names to resolve and cache when the app initializes. |
108
+ | `transitions` | object | No | Duration, ease, cover, and effects for zoom transitions. |
109
+ | `debug` | boolean | No | Enable debug messages (default: `false`). |
110
+ | `componentContext` | object | No | Context passed to component-style views. |
167
111
 
168
- <div class="z-view"></div>
112
+ **Transitions (optional):**
169
113
 
114
+ ```js
115
+ transitions: {
116
+ driver: 'css', // 'css' | 'waapi' | 'anime' | 'gsap' | 'motion' | 'none' or custom function(spec, onComplete)
117
+ effects: ['blur', 'sepia', 'saturate'], // background view effects
118
+ cover: 'width', // or 'height' — how the previous view scales to cover the trigger
119
+ duration: '1s',
120
+ ease: 'ease-in-out',
121
+ }
170
122
  ```
171
123
 
172
- - Add `zoom-me` class to an HTML element to make it zoomable and add `data-to` attribute with the name of the target view
124
+ **Transition drivers:** Zoom animations are handled by a pluggable driver so you can swap the implementation without changing app logic.
173
125
 
174
- ```html
126
+ | Driver | Description |
127
+ |--------|-------------|
128
+ | `'css'` (default) | CSS keyframes and `animationend`; uses `zumly.css` variables. |
129
+ | `'waapi'` | Web Animations API (`element.animate()`). |
130
+ | `'anime'` | [Anime.js](https://animejs.com/) — load the library (e.g. from CDN) before use. |
131
+ | `'gsap'` | [GSAP](https://greensock.com/gsap/) — load the library (e.g. from CDN) before use. |
132
+ | `'motion'` | [Motion](https://motion.dev/) (motion.dev) — load the library (e.g. from CDN) before use. |
133
+ | `'none'` | No animation; applies final state immediately. Useful for tests or instant UX. |
134
+ | `function(spec, onComplete)` | Custom driver. Receives a transition spec and must call `onComplete()` when done. |
175
135
 
176
- <div class="zoom-me" data-to="anotherView">Zoom me!</div>
136
+ Example with instant transitions (e.g. for tests):
177
137
 
138
+ ```js
139
+ const app = new Zumly({
140
+ mount: '.canvas',
141
+ initialView: 'home',
142
+ views: { home, detail },
143
+ transitions: { driver: 'none', duration: '0s' },
144
+ });
178
145
  ```
179
146
 
180
- - Each zooming transition can be customized by adding some `data-` attributes:
147
+ **Zoomable elements:**
181
148
 
182
- ```html
149
+ - Give the view root the class `z-view`.
150
+ - Add class `zoom-me` and `data-to="viewName"` to the element that triggers zoom-in.
151
+ - Optional per-trigger: `data-with-duration`, `data-with-ease`.
183
152
 
184
- <div class="zoom-me" data-to="anotherView" data-with-duration="2s" data-with-ease="ease-in">
185
- Zoom me!
153
+ ```html
154
+ <div class="z-view">
155
+ <div class="zoom-me" data-to="detail" data-with-duration="2s" data-with-ease="ease-in">
156
+ Zoom in
157
+ </div>
186
158
  </div>
187
-
188
159
  ```
189
160
 
190
- ## Development
161
+ ### View sources
191
162
 
192
- ### Developer environment requirements
163
+ Views can be:
193
164
 
194
- To run this project, you will need:
165
+ - **HTML string** static markup.
166
+ - **Async function** — `(context) => string | Promise<string>` or return an `HTMLElement`.
167
+ - **Object with `render()`** — `{ render(): string | Promise<string>, mounted?(): void }`.
168
+ - **URL** — path or full URL to an HTML file (fetched and cached).
169
+ - **Web component tag name** — string containing a hyphen (e.g. `'my-view'`); the custom element is created after it is defined.
195
170
 
196
- - Node.js >= v10.5.0
171
+ Preloading: use `preload: ['viewA', 'viewB']` to resolve those views on init. Hovering over a `zoom-me` trigger prefetches its target view; when a view becomes current, its `zoom-me` targets are prefetched in the background.
197
172
 
198
- ### Dev mode
173
+ ## Development
199
174
 
200
- When developing you can run:
175
+ ### Requirements
201
176
 
202
- ```sh
203
- npm run dev
177
+ - Node.js >= 18 (or 16+ with ES module support)
204
178
 
205
- # or
206
-
207
- yarn dev
208
- ```
179
+ ### Commands
209
180
 
210
- This will regenerate the build files each time a source file is changed and serve on http://localhost:9090
181
+ ```sh
182
+ # Build the library
183
+ npm run compile
211
184
 
212
- ### Running tests
185
+ # Build and serve the demo at http://localhost:9090
186
+ npm run dev
213
187
 
214
- ```sh
188
+ # Run tests (Vitest + Playwright). Install browsers first:
189
+ npm run test:install-browsers
215
190
  npm run test
216
191
 
217
- # or
192
+ # Run tests with coverage
193
+ npm run test:coverage
218
194
 
219
- yarn test
195
+ # Build and pack for publish
196
+ npm run build
220
197
  ```
221
198
 
199
+ Tests use [Vitest](https://vitest.dev/) with the browser provider ([Playwright](https://playwright.dev/)), same setup as [SnapDOM](https://github.com/zumerlab/snapdom). Run `npm run test:install-browsers` once (or after upgrading Playwright) to install Chromium.
200
+
222
201
  ### Building
223
202
 
224
203
  ```sh
225
- npm run build
226
-
227
- # or
228
-
229
- yarn build
204
+ npm run compile
230
205
  ```
231
206
 
232
- ## Changelog
233
-
234
- Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
207
+ Output is in the `dist/` folder.
235
208
 
236
- ### Status: beta
209
+ ## Changelog
237
210
 
238
- Zumly is on early stages of development.
211
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
239
212
 
240
- ### Roadmap
213
+ ## Roadmap
241
214
 
242
- - Allow different template engines. Currently Zumly only accepts string literal templates.
243
- - Add lateral navigation for same zoom level elements.
244
- - Add a navegation widget.
245
- - Add programmatic navigation.
246
- - Add preseted navigation.
247
- - Add router. [#3](https://github.com/zumly/zumly/issues/3)
248
- - Allow recalculate zoom position on resize events.
215
+ - Additional view/template adapters
216
+ - Lateral navigation (same zoom level)
217
+ - Navigation widget and programmatic navigation
218
+ - Router integration (e.g. URL sync)
219
+ - Recalculate zoom on window resize
249
220
 
221
+ Details and more topics: [docs/roadMap.md](docs/roadMap.md).
250
222
 
251
- ## Stay in touch
223
+ ## Community
252
224
 
253
225
  - [Telegram group](https://t.me/ZumlyCommunity)
254
226
 
255
- ## Original idea
227
+ ## Origin
256
228
 
257
- Zumly is a new approach based on another library I made, [Zircle UI](https://github.com/zircleUI/zircleUI)
229
+ Zumly is a reimagined, framework-agnostic zoom engine inspired by [Zircle UI](https://github.com/zircleUI/zircleUI). It can be used together with [Orbit](https://github.com/zumerlab/orbit) for radial layouts.
258
230
 
259
231
  ## License
260
232
 
261
- The MIT License (MIT). Please see [License File](LICENSE) for more information.
262
-
263
-
233
+ MIT. See [LICENSE](LICENSE).
package/dist/zumly.css CHANGED
@@ -1,8 +1,4 @@
1
- /*
2
- * zumly v0.9.11
3
- * Author Juan Martín Muda, @license MIT
4
- * https://zumly.org
5
- */
1
+ /* src/style.css */
6
2
  .zumly-canvas {
7
3
  position: absolute;
8
4
  width: 100%;
@@ -12,39 +8,89 @@
12
8
  padding: 0;
13
9
  perspective: 1000px;
14
10
  cursor: zoom-out;
11
+ contain: strict;
15
12
  }
16
-
17
13
  .zumly-canvas:focus {
18
14
  outline: none;
19
15
  }
20
-
21
16
  .z-view {
22
17
  position: absolute;
18
+ contain: strict;
23
19
  }
24
-
25
20
  .z-view.is-current-view {
26
21
  cursor: default;
27
22
  }
28
-
29
- .z-view.is-new-current-view {
30
-
31
- }
32
-
33
- .z-view.is-previous-view, .z-view.is-last-view, .z-view.has-no-events {
23
+ .z-view.is-previous-view,
24
+ .z-view.is-last-view,
25
+ .z-view.has-no-events {
34
26
  pointer-events: none;
35
27
  user-select: none;
36
28
  }
37
-
38
- .z-view.performance {
39
- will-change: transform, opacity, filter
29
+ .z-view {
30
+ will-change: transform, opacity;
31
+ content-visibility: auto;
40
32
  }
41
-
42
33
  .z-view.hide {
43
- opacity: 0
34
+ opacity: 0;
35
+ content-visibility: hidden;
44
36
  }
45
-
46
37
  .zoom-me {
47
38
  cursor: zoom-in;
48
39
  }
49
-
50
- .zoom-me > .zoomed {}
40
+ .zoom-current-view-reverse {
41
+ animation-name: zoom-current-view;
42
+ animation-duration: var(--zoom-duration);
43
+ animation-timing-function: var(--zoom-ease);
44
+ animation-direction: reverse;
45
+ }
46
+ .zoom-current-view {
47
+ animation-name: zoom-current-view;
48
+ animation-duration: var(--zoom-duration);
49
+ animation-timing-function: var(--zoom-ease);
50
+ }
51
+ @keyframes zoom-current-view {
52
+ 0% {
53
+ transform: var(--current-view-transform-start);
54
+ }
55
+ 100% {
56
+ transform: var(--current-view-transform-end);
57
+ }
58
+ }
59
+ .zoom-previous-view {
60
+ animation-name: zoom-previous-view;
61
+ animation-duration: var(--zoom-duration);
62
+ animation-timing-function: var(--zoom-ease);
63
+ }
64
+ .zoom-previous-view-reverse {
65
+ animation-name: zoom-previous-view;
66
+ animation-duration: var(--zoom-duration);
67
+ animation-timing-function: var(--zoom-ease);
68
+ animation-direction: reverse;
69
+ }
70
+ @keyframes zoom-previous-view {
71
+ 0% {
72
+ transform: var(--previous-view-transform-start);
73
+ }
74
+ 100% {
75
+ transform: var(--previous-view-transform-end);
76
+ }
77
+ }
78
+ .zoom-last-view-reverse {
79
+ animation-name: zoom-last-view;
80
+ animation-duration: var(--zoom-duration);
81
+ animation-timing-function: var(--zoom-ease);
82
+ animation-direction: reverse;
83
+ }
84
+ .zoom-last-view {
85
+ animation-name: zoom-last-view;
86
+ animation-duration: var(--zoom-duration);
87
+ animation-timing-function: var(--zoom-ease);
88
+ }
89
+ @keyframes zoom-last-view {
90
+ 0% {
91
+ transform: var(--last-view-transform-start);
92
+ }
93
+ 100% {
94
+ transform: var(--last-view-transform-end);
95
+ }
96
+ }
package/dist/zumly.js ADDED
@@ -0,0 +1,7 @@
1
+ /*
2
+ * zumly
3
+ * v.0.18.1
4
+ * Author Juan Martin Muda - Zumerlab
5
+ * License MIT
6
+ */
7
+ var Et=s=>{throw TypeError(s)};var V=(s,t,r)=>t.has(s)||Et("Cannot "+r);var g=(s,t,r)=>(V(s,t,"read from private field"),r?r.call(s):t.get(s)),T=(s,t,r)=>t.has(s)?Et("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(s):t.set(s,r),A=(s,t,r,n)=>(V(s,t,"write to private field"),n?n.call(s,r):t.set(s,r),r),xt=(s,t,r)=>(V(s,t,"access private method"),r);function jt(s){var t="",r="";if(s!==void 0)return s.map(n=>{t+=`${n.toLowerCase()==="blur"?"blur(0px) ":n.toLowerCase()==="sepia"?"sepia(0) ":n.toLowerCase()==="saturate"?"saturate(0) ":"none"}`,r+=`${n.toLowerCase()==="blur"?"blur(0.8px) ":n.toLowerCase()==="sepia"?"sepia(5) ":n.toLowerCase()==="saturate"?"saturate(8) ":"none"}`}),[t,r]}async function tt(s,t,r,n,o,e){if(!s||!s.classList){let f=document.createElement("div");f.classList.add("z-view"),s&&f.appendChild(s),s=f}else s.classList.contains("z-view")||s.classList.add("z-view");return s.dataset.viewName=t,s.style.transformOrigin="0 0",n?s.classList.add("is-current-view"):s.classList.add("is-new-current-view","has-no-events","hide"),r.append(s),typeof o[t]=="object"&&typeof o[t].mounted=="function"&&await o[t].mounted(),s}function F(s,t,r){t&&r==="welcome"&&console.info(`%c Zumly %c ${t}`,"background: #424085; color: white; border-radius: 3px;","color: #424085"),t&&s&&(r==="info"||r===void 0)&&console.info(`%c Zumly %c ${t}`,"background: #6679A3; color: #304157; border-radius: 3px;","color: #6679A3"),t&&r==="warn"&&console.warn(`%c Zumly %c ${t}`,"background: #DCBF53; color: #424085; border-radius: 3px;","color: #424085"),t&&r==="error"&&console.error(`%c Zumly %c ${t}`,"background: #BE4747; color: white; border-radius: 3px;","color: #424085")}function zt(s,t){if(!s||typeof s!="object"){F(!1,"'options' object has to be provided when instance is defined","error");return}t.options=!0,typeof s.mount=="string"?t.mount=s.mount:F(!1,"'mount' must be a string selector","error"),typeof s.initialView=="string"?t.initialView=s.initialView:F(!1,"'initialView' must be a string","error"),s.views&&typeof s.views=="object"?t.views=s.views:F(!1,"'views' must be an object","error"),t.preload=Array.isArray(s.preload)?s.preload:[],t.debug=typeof s.debug=="boolean"?s.debug:!1,t.componentContext=s.componentContext&&typeof s.componentContext=="object"?s.componentContext:new Map;let r=s.transitions&&typeof s.transitions=="object"?s.transitions:null,n=r&&r.cover,o=typeof n=="string"?n.toLowerCase():null,e=o==="width"||o==="height";if(t.cover=e?o:"width",!e&&r&&n!==void 0&&F(!1,`'transitions.cover' must be either "width" or "height". Falling back to "width".`,"warn"),t.duration=r&&typeof r.duration=="string"?r.duration:"1s",t.ease=r&&typeof r.ease=="string"?r.ease:"ease-in-out",!r||r.effects===void 0)t.effects=["none","none"];else{let c=r.effects;if(!Array.isArray(c)||c.length===0||!c.every(i=>typeof i=="string"))F(!1,`'transitions.effects' must be an array of strings: blur|sepia|saturate (or ["none",...]). Falling back to ["none","none"].`,"warn"),t.effects=["none","none"];else{let i=c.map(h=>h.toLowerCase()),a=i[0]==="none",u=["blur","sepia","saturate"];a||[...new Set(i)].every(h=>u.indexOf(h)!==-1)?(t.effects=jt(i),t.effects||(t.effects=["none","none"])):(F(!1,`'transitions.effects' contains invalid values. Falling back to ["none","none"].`,"warn"),t.effects=["none","none"])}}let f=r&&r.driver,l=typeof f=="string"?f.toLowerCase():null,d=typeof f=="function"?f:null;if(l!==null){let c=["css","waapi","none","anime","gsap","motion"];t.transitionDriver=c.includes(l)?l:"css",c.includes(l)||F(!1,`'transitions.driver' must be "css", "waapi", "none", "anime", "gsap", "motion", or a function. Got "${f}". Falling back to "css".`,"warn")}else d!==null?t.transitionDriver=d:t.transitionDriver="css"}var M,W,kt,U=class{constructor(t={}){T(this,W);T(this,M,{});A(this,M,t)}async resolve(t,r=null){if(typeof t=="string"&&Object.prototype.hasOwnProperty.call(g(this,M),t)){let o=g(this,M)[t];return this.resolve(o,r)}switch(xt(this,W,kt).call(this,t)){case"html":{let o=document.createElement("div");o.innerHTML=t.trim();let e=o.firstElementChild;if(!e)throw new Error("Zumly: view produced no element (html)");return e}case"url":{let o=await fetch(t);if(!o.ok)throw new Error(`Zumly: fetch failed for "${t}" (${o.status})`);let e=await o.text(),f=document.createElement("div");f.innerHTML=e.trim();let l=f.firstElementChild;if(!l)throw new Error("Zumly: view produced no element (url)");return l}case"function":{let o=r&&r.target||document.createElement("div"),e=await t({...r,target:o});if(typeof e=="string")return this.resolve(e,r);if(e instanceof HTMLElement)return e;let f=o.firstElementChild||o;return f.classList.contains("z-view")||f.classList.add("z-view"),f}case"object":{let o=await t.render(r);if(typeof o!="string")throw new Error("Zumly: view render() must return string");return this.resolve(o,r)}case"element":return t.cloneNode(!0);case"webcomponent":return await customElements.whenDefined(t),document.createElement(t);default:throw new Error("Zumly: unknown view type for source")}}};M=new WeakMap,W=new WeakSet,kt=function(t){return typeof t=="function"?"function":t instanceof HTMLElement?"element":typeof t=="object"&&t!==null&&typeof t.render=="function"?"object":typeof t!="string"?"unknown":/^https?:\/\/|^\/|\.html$|\.php$/i.test(t)?"url":t.includes("<")?"html":t.includes("-")?"webcomponent":"unknown"};var C,K=class{constructor(){T(this,C,new Map)}set(t,r,n=null){g(this,C).set(t,{node:r.cloneNode(!0),expires:n?Date.now()+n:null})}get(t){let r=g(this,C).get(t);return r?r.expires!==null&&Date.now()>r.expires?(g(this,C).delete(t),null):r.node.cloneNode(!0):null}has(t){return this.get(t)!==null}invalidate(t){g(this,C).delete(t)}clear(){g(this,C).clear()}};C=new WeakMap;var Dt=300*1e3,_,I,B,$,Q=class{constructor(t={}){T(this,_);T(this,I);T(this,B);T(this,$,new Map);A(this,B,t),A(this,_,new U(t)),A(this,I,new K)}async get(t,r=null){let n=g(this,I).get(t);if(n)return n;if(g(this,$).has(t))return g(this,$).get(t);let o=(async()=>{let e=await g(this,_).resolve(t,r),f=g(this,B)[t];if(typeof f=="string"){let l=/^https?:\/\/|^\/|\.html$|\.php$/i.test(f)?Dt:null;g(this,I).set(t,e,l)}return g(this,$).delete(t),e})();return g(this,$).set(t,o),o}async preloadEager(t,r=null){!Array.isArray(t)||t.length===0||await Promise.all(t.map(n=>this.get(n,r)))}prefetchOnHover(t,r=null){this.get(t,r).catch(()=>{})}scanAndPrefetch(t,r=null){if(!t||!t.querySelectorAll)return;t.querySelectorAll(".zoom-me[data-to]").forEach(o=>{let e=o.dataset.to;e&&this.prefetchOnHover(e,r)})}};_=new WeakMap,I=new WeakMap,B=new WeakMap,$=new WeakMap;function st(s,t){let{type:r,currentView:n,previousView:o,lastView:e,currentStage:f,duration:l,ease:d,canvas:c}=s;if(!n||!o||!f){t();return}r==="zoomIn"?qt(n,o,e,f,l,d,t):r==="zoomOut"?Nt(n,o,e,f,l,d,c,t):t()}function qt(s,t,r,n,o,e,f){s.classList.remove("hide"),s.style.setProperty("--zoom-duration",o),s.style.setProperty("--zoom-ease",e),s.style.setProperty("--current-view-transform-start",n.views[0].backwardState.transform),s.style.setProperty("--current-view-transform-end",n.views[0].forwardState.transform),t.style.setProperty("--zoom-duration",o),t.style.setProperty("--zoom-ease",e),t.style.setProperty("--previous-view-transform-start",n.views[1].backwardState.transform),t.style.setProperty("--previous-view-transform-end",n.views[1].forwardState.transform),r&&(r.style.setProperty("--zoom-duration",o),r.style.setProperty("--zoom-ease",e),r.style.setProperty("--last-view-transform-start",n.views[2].backwardState.transform),r.style.setProperty("--last-view-transform-end",n.views[2].forwardState.transform)),s.style.contentVisibility="auto",t.style.contentVisibility="auto",r&&(r.style.contentVisibility="auto"),s.classList.add("zoom-current-view"),t.classList.add("zoom-previous-view"),r&&r.classList.add("zoom-last-view");let l=r?[s,t,r]:[s,t],d=l.length;function c(i){let a=i.target;a.removeEventListener("animationend",c),Rt(a,n),d--,d===0&&f()}l.forEach(i=>i.addEventListener("animationend",c))}function Rt(s,t){if(s.classList.contains("is-new-current-view")){let r=t.views[0].forwardState;s.classList.replace("is-new-current-view","is-current-view"),s.classList.remove("zoom-current-view","has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-previous-view")){let r=t.views[1].forwardState;s.classList.remove("zoom-previous-view"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-last-view")){let r=t.views[2].forwardState;s.classList.remove("zoom-last-view"),s.style.transformOrigin=r.origin,s.style.transform=r.transform}}function Nt(s,t,r,n,o,e,f,l){s.classList.add("zoom-current-view-reverse"),t.classList.add("zoom-previous-view-reverse"),r&&r.classList.add("zoom-last-view-reverse"),s.style.setProperty("--zoom-duration",o),s.style.setProperty("--zoom-ease",e),t.style.setProperty("--zoom-duration",o),t.style.setProperty("--zoom-ease",e),r&&r.style.setProperty("--zoom-duration",o);let d=r?[s,t,r]:[s,t],c=d.length;function i(a){let u=a.target;u.removeEventListener("animationend",i),Yt(u,n,f),c--,c===0&&l()}d.forEach(a=>a.addEventListener("animationend",i))}function Yt(s,t,r){if(s.classList.contains("zoom-current-view-reverse")){try{r&&r.removeChild(s)}catch{try{s.parentElement&&r.removeChild(s.parentElement)}catch{}}return}if(s.classList.contains("zoom-previous-view-reverse")){let n=t.views[1].backwardState;s.classList.remove("zoom-previous-view-reverse"),s.style.transformOrigin="0 0",s.style.transform=n.transform;return}if(s.classList.contains("zoom-last-view-reverse")){let n=t.views[2].backwardState;s.classList.remove("zoom-last-view-reverse"),s.style.transformOrigin=n.origin,s.style.transform=n.transform}}function nt(s,t){let{type:r,currentView:n,previousView:o,lastView:e,currentStage:f,canvas:l}=s;if(!n||!o||!f){t();return}r==="zoomIn"?(n.classList.remove("hide"),n.style.contentVisibility="auto",o.style.contentVisibility="auto",e&&(e.style.contentVisibility="auto"),rt(n,f),rt(o,f),e&&rt(e,f)):r==="zoomOut"&&(_t(n,l),o.style.contentVisibility="auto",e&&(e.style.contentVisibility="auto"),Bt(o,f.views[1].backwardState),e&&Ht(e,f.views[2].backwardState)),t()}function rt(s,t){if(s.classList.contains("is-new-current-view")){let r=t.views[0].forwardState;s.classList.replace("is-new-current-view","is-current-view"),s.classList.remove("zoom-current-view","has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-previous-view")){let r=t.views[1].forwardState;s.classList.remove("zoom-previous-view","has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-last-view")){let r=t.views[2].forwardState;s.classList.remove("zoom-last-view","has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform}}function _t(s,t){try{t&&t.removeChild(s)}catch{try{s.parentElement&&t.removeChild(s.parentElement)}catch{}}}function Bt(s,t){s.classList.remove("zoom-previous-view-reverse","has-no-events"),s.style.transformOrigin="0 0",s.style.transform=t.transform}function Ht(s,t){s.classList.remove("zoom-last-view-reverse","has-no-events"),s.style.transformOrigin=t.origin,s.style.transform=t.transform}function it(s,t){let{type:r,currentView:n,previousView:o,lastView:e,currentStage:f,duration:l,ease:d,canvas:c}=s;if(!n||!o||!f){t();return}let i=Xt(l);r==="zoomIn"?Gt(n,o,e,f,i,d,t):r==="zoomOut"?Ut(n,o,e,f,i,d,c,t):t()}function Xt(s){if(typeof s=="number")return s;let t=String(s),r=parseFloat(t);return t.includes("ms")?r:t.includes("s")?r*1e3:1e3}function Gt(s,t,r,n,o,e,f){s.classList.remove("hide"),s.style.contentVisibility="auto",t.style.contentVisibility="auto",r&&(r.style.contentVisibility="auto");let l=n.views[0],d=n.views[1],c=r&&n.views[2]?n.views[2]:null;s.style.transformOrigin=l.backwardState.origin,s.style.transform=l.backwardState.transform,t.style.transformOrigin=d.backwardState.origin,t.style.transform=d.backwardState.transform,c&&(r.style.transformOrigin=c.backwardState.origin,r.style.transform=c.backwardState.transform);let i=[];i.push(s.animate([{transform:l.backwardState.transform},{transform:l.forwardState.transform}],{duration:o,easing:e,fill:"forwards"})),i.push(t.animate([{transform:d.backwardState.transform},{transform:d.forwardState.transform}],{duration:o,easing:e,fill:"forwards"})),c&&i.push(r.animate([{transform:c.backwardState.transform},{transform:c.forwardState.transform}],{duration:o,easing:e,fill:"forwards"})),Promise.all(i.map(a=>a.finished)).then(()=>{i.forEach(a=>a.cancel()),ot(s,n),ot(t,n),r&&ot(r,n),f()}).catch(()=>{i.forEach(a=>a.cancel()),f()})}function ot(s,t){if(s.classList.contains("is-new-current-view")){let r=t.views[0].forwardState;s.classList.replace("is-new-current-view","is-current-view"),s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-previous-view")){let r=t.views[1].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-last-view")){let r=t.views[2].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform}}function Ut(s,t,r,n,o,e,f,l){let d=n.views[0],c=n.views[1],i=r&&n.views[2]?n.views[2]:null,a=c.backwardState,u=i?i.backwardState:null;s.style.transformOrigin=d.forwardState.origin,s.style.transform=d.forwardState.transform;let m;function h(){t.style.transformOrigin=c.forwardState.origin,r&&i&&(r.style.transformOrigin=i.forwardState.origin);let v=t.style.transform||getComputedStyle(t).transform||c.forwardState.transform,S=r&&i?r.style.transform||getComputedStyle(r).transform||i.forwardState.transform:null;m=[s.animate([{transform:d.forwardState.transform},{transform:d.backwardState.transform}],{duration:o,easing:e,fill:"forwards"})],m.push(t.animate([{transform:v},{transform:a.transform}],{duration:o,easing:e,fill:"forwards"})),r&&u&&m.push(r.animate([{transform:S},{transform:u.transform}],{duration:o,easing:e,fill:"forwards"})),t.style.removeProperty("transform"),r&&r.style.removeProperty("transform"),Promise.all(m.map(w=>w.finished)).then(b).catch(()=>{m.forEach(w=>w.cancel()),l()})}function b(){m.forEach(v=>v.cancel());try{f&&f.removeChild(s)}catch{try{s.parentElement&&f.removeChild(s.parentElement)}catch{}}t.classList.remove("has-no-events"),t.style.transformOrigin="0 0",t.style.transform=a.transform,r&&u&&(r.style.transformOrigin=u.origin,r.style.transform=u.transform),l()}requestAnimationFrame(()=>{requestAnimationFrame(h)})}function at(s,t){let r=typeof globalThis<"u"&&globalThis.anime;if(!r||typeof r!="function"){typeof console<"u"&&console.warn&&console.warn('Zumly anime driver: Anime.js not loaded. Add <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.2/anime.min.js"></script>'),t();return}let{type:n,currentView:o,previousView:e,lastView:f,currentStage:l,duration:d,ease:c,canvas:i}=s;if(!o||!e||!l){t();return}let a=Wt(d);n==="zoomIn"?Qt(r,o,e,f,l,a,c,t):n==="zoomOut"?Jt(r,o,e,f,l,a,c,i,t):t()}function Wt(s){if(typeof s=="number")return s;let t=String(s),r=parseFloat(t);return t.includes("ms")?r:t.includes("s")?r*1e3:1e3}function Ot(){return{a:1,b:0,c:0,d:1,e:0,f:0}}function Kt(s){if(!s||s==="none")return Ot();let t=String(s).match(/matrix\(([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+)\)/);return t?{a:parseFloat(t[1])||1,b:parseFloat(t[2])||0,c:parseFloat(t[3])||0,d:parseFloat(t[4])||1,e:parseFloat(t[5])||0,f:parseFloat(t[6])||0}:Ot()}function L(s){return`matrix(${s.a}, ${s.b}, ${s.c}, ${s.d}, ${s.e}, ${s.f})`}function j(s,t,r){return s+(t-s)*r}function D(s,t,r){let n=Math.max(0,Math.min(1,r));return{a:j(s.a,t.a,n),b:j(s.b,t.b,n),c:j(s.c,t.c,n),d:j(s.d,t.d,n),e:j(s.e,t.e,n),f:j(s.f,t.f,n)}}function E(s,t,r){s.style.transformOrigin=t,s.style.transform=r;try{s.getBoundingClientRect()}catch{}return Kt(getComputedStyle(s).transform)}function et(s,t){if(s.classList.contains("is-new-current-view")){let r=t.views[0].forwardState;s.classList.replace("is-new-current-view","is-current-view"),s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-previous-view")){let r=t.views[1].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-last-view")){let r=t.views[2].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform}}function Qt(s,t,r,n,o,e,f,l){t.classList.remove("hide"),t.style.contentVisibility="auto",r.style.contentVisibility="auto",n&&(n.style.contentVisibility="auto");let d=o.views[0],c=o.views[1],i=n&&o.views[2]?o.views[2]:null,a=E(t,d.backwardState.origin,d.backwardState.transform),u=E(t,d.backwardState.origin,d.forwardState.transform),m=E(r,c.backwardState.origin,c.backwardState.transform),h=E(r,c.backwardState.origin,c.forwardState.transform),b=i?E(n,i.backwardState.origin,i.backwardState.transform):null,v=i?E(n,i.backwardState.origin,i.forwardState.transform):null;t.style.transform=L(a),r.style.transform=L(m),i&&(n.style.transform=L(b));let S={value:0};s({targets:S,value:1,duration:e,easing:Tt(f),update:()=>{let y=S.value;t.style.transform=L(D(a,u,y)),r.style.transform=L(D(m,h,y)),i&&(n.style.transform=L(D(b,v,y)))},complete:()=>{et(t,o),et(r,o),n&&et(n,o),l()}})}function Jt(s,t,r,n,o,e,f,l,d){let c=o.views[0],i=o.views[1],a=n&&o.views[2]?o.views[2]:null,u=i.backwardState,m=a?a.backwardState:null,h=E(t,c.forwardState.origin,c.forwardState.transform),b=E(t,c.forwardState.origin,c.backwardState.transform),v=E(r,i.forwardState.origin,i.forwardState.transform),S=E(r,i.forwardState.origin,i.backwardState.transform),y=a?E(n,a.forwardState.origin,a.forwardState.transform):null,w=a?E(n,a.forwardState.origin,a.backwardState.transform):null;t.style.transform=L(h),r.style.transform=L(v),a&&(n.style.transform=L(y));let P={value:0};s({targets:P,value:1,duration:e,easing:Tt(f),update:()=>{let k=P.value;t.style.transform=L(D(h,b,k)),r.style.transform=L(D(v,S,k)),a&&(n.style.transform=L(D(y,w,k)))},complete:()=>{try{l&&l.removeChild(t)}catch{try{t.parentElement&&l.removeChild(t.parentElement)}catch{}}r.classList.remove("zoom-previous-view-reverse"),r.classList.remove("has-no-events"),r.style.transformOrigin="0 0",r.style.transform=u.transform,n&&m&&(n.classList.remove("zoom-last-view-reverse"),n.style.transformOrigin=m.origin,n.style.transform=m.transform),d()}})}function Tt(s){if(typeof s!="string")return"easeInOutQuad";let t=s.toLowerCase();return t==="linear"?"linear":t.includes("ease-in-out")?"easeInOutQuad":t.includes("ease-in")?"easeInQuad":t.includes("ease-out")?"easeOutQuad":t}function ft(s,t){let r=typeof globalThis<"u"&&globalThis.gsap;if(!r||typeof r.to!="function"){typeof console<"u"&&console.warn&&console.warn('Zumly GSAP driver: GSAP not loaded. Add <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>'),t();return}let{type:n,currentView:o,previousView:e,lastView:f,currentStage:l,duration:d,ease:c,canvas:i}=s;if(!o||!e||!l){t();return}let a=Vt(d);n==="zoomIn"?ts(r,o,e,f,l,a,c,t):n==="zoomOut"?ss(r,o,e,f,l,a,c,i,t):t()}function Vt(s){if(typeof s=="number")return s/1e3;let t=String(s),r=parseFloat(t);return t.includes("ms")?r/1e3:t.includes("s")?r:1}function ct(s,t){if(s.classList.contains("is-new-current-view")){let r=t.views[0].forwardState;s.classList.replace("is-new-current-view","is-current-view"),s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-previous-view")){let r=t.views[1].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-last-view")){let r=t.views[2].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform}}function ts(s,t,r,n,o,e,f,l){t.classList.remove("hide"),t.style.contentVisibility="auto",r.style.contentVisibility="auto",n&&(n.style.contentVisibility="auto");let d=o.views[0],c=o.views[1],i=n&&o.views[2]?o.views[2]:null;t.style.transformOrigin=d.backwardState.origin,t.style.transform=d.backwardState.transform,r.style.transformOrigin=c.backwardState.origin,r.style.transform=c.backwardState.transform,i&&(n.style.transformOrigin=i.backwardState.origin,n.style.transform=i.backwardState.transform);let a=s.timeline({onComplete:()=>{ct(t,o),ct(r,o),n&&ct(n,o),l()}});a.to(t,{transform:d.forwardState.transform,duration:e,ease:q(f)},0),a.to(r,{transform:c.forwardState.transform,duration:e,ease:q(f)},0),i&&a.to(n,{transform:i.forwardState.transform,duration:e,ease:q(f)},0)}function ss(s,t,r,n,o,e,f,l,d){let c=o.views[0],i=o.views[1],a=n&&o.views[2]?o.views[2]:null,u=i.backwardState,m=a?a.backwardState:null;t.style.transformOrigin=c.forwardState.origin,t.style.transform=c.forwardState.transform;let h=r.style.transform||getComputedStyle(r).transform||i.forwardState.transform,b=n&&a?n.style.transform||getComputedStyle(n).transform||a.forwardState.transform:null;r.style.transformOrigin=i.forwardState.origin,n&&a&&(n.style.transformOrigin=a.forwardState.origin);let v=s.timeline({onComplete:()=>{try{l&&l.removeChild(t)}catch{try{t.parentElement&&l.removeChild(t.parentElement)}catch{}}r.classList.remove("has-no-events"),r.style.transformOrigin="0 0",r.style.transform=u.transform,n&&m&&(n.style.transformOrigin=m.origin,n.style.transform=m.transform),d()}});v.fromTo(t,{transform:c.forwardState.transform},{transform:c.backwardState.transform,duration:e,ease:q(f)},0),v.fromTo(r,{transform:h},{transform:u.transform,duration:e,ease:q(f)},0),r.style.removeProperty("transform"),n&&m&&(v.fromTo(n,{transform:b},{transform:m.transform,duration:e,ease:q(f)},0),n.style.removeProperty("transform"))}function q(s){if(typeof s!="string")return"power2.inOut";let t=s.toLowerCase();return t==="linear"?"none":t.includes("ease-in-out")?"power2.inOut":t.includes("ease-in")?"power2.in":t.includes("ease-out")?"power2.out":t}function dt(s,t){let r=rs();if(!r||typeof r!="function"){typeof console<"u"&&console.warn&&console.warn('Zumly Motion driver: Motion not loaded. Add <script src="https://cdn.jsdelivr.net/npm/motion@11/dist/motion.min.js"></script>'),t();return}let{type:n,currentView:o,previousView:e,lastView:f,currentStage:l,duration:d,ease:c,canvas:i}=s;if(!o||!e||!l){t();return}let a=ns(d);n==="zoomIn"?is(r,o,e,f,l,a,c,t):n==="zoomOut"?es(r,o,e,f,l,a,c,i,t):t()}function rs(){let s=typeof globalThis<"u"?globalThis:typeof window<"u"?window:{};return s.motion?.animate||s.Motion?.animate||s.animate}function ns(s){if(typeof s=="number")return s/1e3;let t=String(s),r=parseFloat(t);return t.includes("ms")?r/1e3:t.includes("s")?r:1}function Ft(){return{a:1,b:0,c:0,d:1,e:0,f:0}}function os(s){if(!s||s==="none")return Ft();let t=String(s).match(/matrix\(([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+),\s*([-\d.eE]+)\)/);return t?{a:parseFloat(t[1])||1,b:parseFloat(t[2])||0,c:parseFloat(t[3])||0,d:parseFloat(t[4])||1,e:parseFloat(t[5])||0,f:parseFloat(t[6])||0}:Ft()}function x(s){return`matrix(${s.a}, ${s.b}, ${s.c}, ${s.d}, ${s.e}, ${s.f})`}function R(s,t,r){return s+(t-s)*r}function N(s,t,r){let n=Math.max(0,Math.min(1,r));return{a:R(s.a,t.a,n),b:R(s.b,t.b,n),c:R(s.c,t.c,n),d:R(s.d,t.d,n),e:R(s.e,t.e,n),f:R(s.f,t.f,n)}}function z(s,t,r){s.style.transformOrigin=t,s.style.transform=r;try{s.getBoundingClientRect()}catch{}return os(getComputedStyle(s).transform)}function lt(s,t){if(s.classList.contains("is-new-current-view")){let r=t.views[0].forwardState;s.classList.replace("is-new-current-view","is-current-view"),s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-previous-view")){let r=t.views[1].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform;return}if(s.classList.contains("is-last-view")){let r=t.views[2].forwardState;s.classList.remove("has-no-events"),s.style.transformOrigin=r.origin,s.style.transform=r.transform}}function is(s,t,r,n,o,e,f,l){t.classList.remove("hide"),t.style.contentVisibility="auto",r.style.contentVisibility="auto",n&&(n.style.contentVisibility="auto");let d=o.views[0],c=o.views[1],i=n&&o.views[2]?o.views[2]:null,a=z(t,d.backwardState.origin,d.backwardState.transform),u=z(t,d.backwardState.origin,d.forwardState.transform),m=z(r,c.backwardState.origin,c.backwardState.transform),h=z(r,c.backwardState.origin,c.forwardState.transform),b=i?z(n,i.backwardState.origin,i.backwardState.transform):null,v=i?z(n,i.backwardState.origin,i.forwardState.transform):null;t.style.transform=x(a),r.style.transform=x(m),i&&(n.style.transform=x(b)),s(0,1,{duration:e,ease:Ct(f),onUpdate:y=>{let w=y;t.style.transform=x(N(a,u,w)),r.style.transform=x(N(m,h,w)),i&&(n.style.transform=x(N(b,v,w)))}}).then(()=>{lt(t,o),lt(r,o),n&&lt(n,o),l()}).catch(()=>l())}function es(s,t,r,n,o,e,f,l,d){let c=o.views[0],i=o.views[1],a=n&&o.views[2]?o.views[2]:null,u=i.backwardState,m=a?a.backwardState:null,h=z(t,c.forwardState.origin,c.forwardState.transform),b=z(t,c.forwardState.origin,c.backwardState.transform),v=z(r,i.forwardState.origin,i.forwardState.transform),S=z(r,i.forwardState.origin,i.backwardState.transform),y=a?z(n,a.forwardState.origin,a.forwardState.transform):null,w=a?z(n,a.forwardState.origin,a.backwardState.transform):null;t.style.transform=x(h),r.style.transform=x(v),a&&(n.style.transform=x(y)),s(0,1,{duration:e,ease:Ct(f),onUpdate:k=>{let O=k;t.style.transform=x(N(h,b,O)),r.style.transform=x(N(v,S,O)),a&&(n.style.transform=x(N(y,w,O)))}}).then(()=>{try{l&&l.removeChild(t)}catch{try{t.parentElement&&l.removeChild(t.parentElement)}catch{}}r.classList.remove("zoom-previous-view-reverse"),r.classList.remove("has-no-events"),r.style.transformOrigin="0 0",r.style.transform=u.transform,n&&m&&(n.classList.remove("zoom-last-view-reverse"),n.style.transformOrigin=m.origin,n.style.transform=m.transform),d()}).catch(()=>d())}function Ct(s){if(typeof s!="string")return"easeInOut";let t=s.toLowerCase();return t==="linear"?"linear":t.includes("ease-in-out")?"easeInOut":t.includes("ease-in")?"easeIn":t.includes("ease-out")?"easeOut":s}function $t(s){if(typeof s=="function")return{runTransition(r,n){s(r,n)}};switch(typeof s=="string"?s.toLowerCase():"css"){case"waapi":return{runTransition:it};case"none":return{runTransition:nt};case"anime":return{runTransition:at};case"gsap":return{runTransition:ft};case"motion":return{runTransition:dt};default:return{runTransition:st}}}var J=class{constructor(t){this.storedViews=[],this.currentStage=null,this.storedPreviousScale=[1],this.debug=!1,this.trace=[],this.blockEvents=!1,this.touchstartX=0,this.touchstartY=0,this.touchendX=0,this.touchendY=0,this.touching=!1,zt(t,this),this.options?(this.transitionDriver=$t(this.transitionDriver),this._onZoom=this.onZoom.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.prefetcher=new Q(this.views),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.canvas.addEventListener("mouseover",this._onPrefetchHover=r=>{r.target.classList.contains("zoom-me")&&r.target.dataset.to&&this.prefetcher.prefetchOnHover(r.target.dataset.to,{trigger:r.target,...r.target.dataset})},{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),this.debug&&console.debug("Zumly storedViews",t)}setPreviousScale(t){this.tracing("setPreviousScale()"),this.storedPreviousScale.push(t)}tracing(t){if(this.debug)if(t==="ended"){let r=this.trace.map((n,o)=>`${o===0?`Instance ${this.instance}: ${n}`:`${n}`}`).join(" > ");this.notify(r),this.trace=[]}else this.trace.push(t)}notify(t,r){return F(this.debug,t,r)}zoomLevel(){return this.storedViews.length}async init(){if(this.options){this.tracing("init()"),this.preload&&this.preload.length&&await this.prefetcher.preloadEager(this.preload,null);let t=await this.prefetcher.get(this.initialView,null),r=await tt(t,this.initialView,this.canvas,!0,this.views,this.componentContext);this.prefetcher.scanAndPrefetch(r,null),this.storeViews({zoomLevel:this.storedViews.length,views:[{viewName:this.initialView,backwardState:{origin:"0 0",transform:""}}]})}}async zoomIn(t){this.tracing("zoomIn()");let r=this.canvas,n=r.getBoundingClientRect();var o=n.left,e=n.top;let f=this.storedPreviousScale[this.storedPreviousScale.length-1];this.tracing("getView()");let l={trigger:t,target:document.createElement("div"),context:this.componentContext,props:Object.assign({},t.dataset)},d=await this.prefetcher.get(t.dataset.to,l);this.prefetcher.scanAndPrefetch(d,l);var c=await tt(d,t.dataset.to,r,!1,this.views,this.componentContext);if(c){t.classList.add("zoomed");let p=t.getBoundingClientRect();var i=r.querySelector(".is-current-view"),a=r.querySelector(".is-previous-view"),u=r.querySelector(".is-last-view");c.style.contentVisibility="hidden",i.style.contentVisibility="hidden",a!==null&&(a.style.contentVisibility="hidden"),u!==null&&(u.style.contentVisibility="hidden"),u!==null&&r.removeChild(u);let Z=c.getBoundingClientRect(),ht=Z.width/p.width,At=1/ht,yt=Z.height/p.height,Mt=1/yt;var m=t.dataset.withDuration||this.duration,h=t.dataset.withEase||this.ease,b=this.effects[0],v=this.effects[1],S=this.cover,y,w;S==="width"?(y=ht,w=At):S==="height"&&(y=yt,w=Mt),this.setPreviousScale(y);var P=`translate(${p.x-o+(p.width-Z.width*w)/2}px, ${p.y-e+(p.height-Z.height*w)/2}px) scale(${w})`;c.style.transform=P,i.classList.replace("is-current-view","is-previous-view");let G=i.getBoundingClientRect();var k=i.style.transform;i.style.transformOrigin=`${p.x+p.width/2-G.x}px ${p.y+p.height/2-G.y}px`;let vt=n.width/2-p.width/2-p.x+G.x,pt=n.height/2-p.height/2-p.y+G.y,gt=`translate(${vt}px, ${pt}px) scale(${y})`;i.style.transform=gt;var O=t.getBoundingClientRect(),Pt=`translate(${O.x-o+(O.width-Z.width)/2}px, ${O.y-e+(O.height-Z.height)/2}px)`;if(a!==null){a.classList.replace("is-previous-view","is-last-view");var mt=a.style.transform,H=i.getBoundingClientRect();a.style.transform=`translate(${vt-o}px, ${pt-e}px) scale(${y*f})`;var X=a.querySelector(".zoomed").getBoundingClientRect();a.style.transform=mt,i.style.transform=k;var ut=i.getBoundingClientRect(),Zt=`translate(${n.width/2-p.width/2-p.x+(ut.x-X.x)+H.x-o+(H.width-X.width)/2}px, ${n.height/2-p.height/2-p.y+(ut.y-X.y)+H.y-e+(H.height-X.height)/2}px) scale(${y*f})`}else i.style.transform=k;var Y={zoomLevel:this.storedViews.length,views:[]};let wt=c?{viewName:c.dataset.viewName,backwardState:{origin:c.style.transformOrigin,duration:m,ease:h,transform:P},forwardState:{origin:c.style.transformOrigin,duration:m,ease:h,transform:Pt}}:null,bt=i?{viewName:i.dataset.viewName,backwardState:{origin:i.style.transformOrigin,duration:m,ease:h,transform:k},forwardState:{origin:i.style.transformOrigin,duration:m,ease:h,transform:gt}}:null,St=a?{viewName:a.dataset.viewName,backwardState:{origin:a.style.transformOrigin,duration:m,ease:h,transform:mt},forwardState:{origin:a.style.transformOrigin,duration:m,ease:h,transform:Zt}}:null,Lt=u?{viewName:u}:null;wt!==null&&Y.views.push(wt),bt!==null&&Y.views.push(bt),St!==null&&Y.views.push(St),Lt!==null&&Y.views.push(Lt),this.storeViews(Y),this.currentStage=this.storedViews[this.storedViews.length-1],this.tracing("setCSSVariables()"),this.blockEvents=!0;let It={type:"zoomIn",currentView:c,previousView:i,lastView:a,currentStage:this.currentStage,duration:m,ease:h};this.transitionDriver.runTransition(It,()=>{this.blockEvents=!1,this.tracing("ended")})}}zoomOut(){this.tracing("zoomOut()");let t=this.canvas;var r=t.querySelector(".is-current-view"),n=t.querySelector(".is-previous-view");if(!r||!n){this.notify("zoomOut: current or previous view not found (animation may still be running)","warn");return}this.blockEvents=!0,this.storedPreviousScale.pop(),this.currentStage=this.storedViews[this.storedViews.length-1];let o=this.currentStage.views[3];var e=t.querySelector(".is-last-view");this.tracing("setCSSVariables()");let f=n.querySelector(".zoomed");if(f&&f.classList.remove("zoomed"),n.classList.replace("is-previous-view","is-current-view"),e!==null&&(e.classList.replace("is-last-view","is-previous-view"),e.classList.remove("hide")),o!==void 0&&o.viewName instanceof Node){t.prepend(o.viewName);var l=t.querySelector(".z-view:first-child");l&&(l.style.contentVisibility="auto",l.classList.add("hide"))}let d=this.currentStage.views[0]?.forwardState?.duration??this.duration,c=this.currentStage.views[0]?.forwardState?.ease??this.ease,i={type:"zoomOut",currentView:r,previousView:n,lastView:e,currentStage:this.currentStage,duration:d,ease:c,canvas:t};this.transitionDriver.runTransition(i,()=>{this.blockEvents=!1,this.tracing("ended")}),this.storedViews.pop()}onZoom(t){this.storedViews.length>1&&!this.blockEvents&&!t.target.classList.contains("zoom-me")&&t.target.closest(".is-current-view")===null&&!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()"),(t.key==="ArrowLeft"||t.key==="ArrowDown")&&(t.preventDefault(),this.storedViews.length>1&&!this.blockEvents?this.zoomOut():this.notify(`is on level zero. Can't zoom out. Trigger: ${t.key}`,"warn")),(t.key==="ArrowRight"||t.key==="ArrowUp")&&(t.preventDefault(),this.notify(t.key+"has not actions defined"))}onWeel(t){this.blockEvents||(this.tracing("onWeel()"),t.deltaY<0,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")&&t.target.closest(".is-current-view")===null&&this.touching&&(this.touching=!1,this.tracing("tap"),this.zoomOut())}};window.Zumly=J;
@@ -1,5 +1 @@
1
- /*!
2
- * zumly v0.9.11
3
- * Author Juan Martín Muda, @license MIT
4
- * https://zumly.org
5
- */.zumly-canvas{position:absolute;width:100%;height:100%;overflow:hidden;margin:0;padding:0;perspective:1000px;cursor:zoom-out}.zumly-canvas:focus{outline:none}.z-view{position:absolute}.z-view.is-current-view{cursor:default}.z-view.has-no-events,.z-view.is-last-view,.z-view.is-previous-view{pointer-events:none;user-select:none}.z-view.performance{will-change:transform,opacity,filter}.z-view.hide{opacity:0}.zoom-me{cursor:zoom-in}
1
+ .zumly-canvas{position:absolute;width:100%;height:100%;overflow:hidden;margin:0;padding:0;perspective:1000px;cursor:zoom-out;contain:strict}.zumly-canvas:focus{outline:none}.z-view{position:absolute;contain:strict}.z-view.is-current-view{cursor:default}.z-view.is-previous-view,.z-view.is-last-view,.z-view.has-no-events{pointer-events:none;user-select:none}.z-view{will-change:transform,opacity;content-visibility:auto}.z-view.hide{opacity:0;content-visibility:hidden}.zoom-me{cursor:zoom-in}.zoom-current-view-reverse{animation-name:zoom-current-view;animation-duration:var(--zoom-duration);animation-timing-function:var(--zoom-ease);animation-direction:reverse}.zoom-current-view{animation-name:zoom-current-view;animation-duration:var(--zoom-duration);animation-timing-function:var(--zoom-ease)}@keyframes zoom-current-view{0%{transform:var(--current-view-transform-start)}to{transform:var(--current-view-transform-end)}}.zoom-previous-view{animation-name:zoom-previous-view;animation-duration:var(--zoom-duration);animation-timing-function:var(--zoom-ease)}.zoom-previous-view-reverse{animation-name:zoom-previous-view;animation-duration:var(--zoom-duration);animation-timing-function:var(--zoom-ease);animation-direction:reverse}@keyframes zoom-previous-view{0%{transform:var(--previous-view-transform-start)}to{transform:var(--previous-view-transform-end)}}.zoom-last-view-reverse{animation-name:zoom-last-view;animation-duration:var(--zoom-duration);animation-timing-function:var(--zoom-ease);animation-direction:reverse}.zoom-last-view{animation-name:zoom-last-view;animation-duration:var(--zoom-duration);animation-timing-function:var(--zoom-ease)}@keyframes zoom-last-view{0%{transform:var(--last-view-transform-start)}to{transform:var(--last-view-transform-end)}}