@aarsteinmedia/dotlottie-player 5.0.0 → 5.0.2

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/dist/index.js CHANGED
@@ -1,12 +1,72 @@
1
+ import { isServer as isServer$1, createElementID, PreserveAspectRatio as PreserveAspectRatio$1 } from '@aarsteinmedia/lottie-web/utils';
2
+ import Lottie from 'lottie-web';
1
3
  import { strToU8, strFromU8, zip, unzip as unzip$1 } from 'fflate';
2
- import * as Lottie from 'lottie-web/build/player/lottie.js';
4
+
5
+ /**
6
+ * Credit to: Leonardo Favre https://github.com/leofavre/observed-properties.
7
+ */ const updateOnConnected = Symbol('UPDATE_ON_CONNECTED');
8
+ if (isServer$1()) {
9
+ // Mock HTMLElement for server-side rendering
10
+ global.HTMLElement = // eslint-disable-next-line @typescript-eslint/no-extraneous-class
11
+ class EmptyHTMLElement {
12
+ };
13
+ }
14
+ /**
15
+ * HTMLElement enhanced to track property changes.
16
+ */ class PropertyCallbackElement extends HTMLElement {
17
+ constructor(){
18
+ super();
19
+ if (updateOnConnected in this) {
20
+ this[updateOnConnected] = [];
21
+ }
22
+ const { observedProperties = [] } = this.constructor;
23
+ const { length } = observedProperties;
24
+ for(let i = 0; i < length; i++){
25
+ const initialValue = this[observedProperties[i]], cachedValue = Symbol(observedProperties[i]);
26
+ this[cachedValue] = initialValue;
27
+ Object.defineProperty(this, observedProperties[i], {
28
+ get () {
29
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
30
+ return this[cachedValue];
31
+ },
32
+ set (value) {
33
+ const oldValue = this[cachedValue];
34
+ this[cachedValue] = value;
35
+ this.propertyChangedCallback(observedProperties[i], oldValue, value);
36
+ }
37
+ });
38
+ if (typeof initialValue !== 'undefined' && updateOnConnected in this && Array.isArray(this[updateOnConnected])) {
39
+ this[updateOnConnected].push(observedProperties[i]);
40
+ }
41
+ }
42
+ }
43
+ // eslint-disable-next-line @typescript-eslint/require-await
44
+ async connectedCallback() {
45
+ let arr = [];
46
+ if (updateOnConnected in this && Array.isArray(this[updateOnConnected])) {
47
+ arr = this[updateOnConnected];
48
+ }
49
+ const { length } = arr;
50
+ for(let i = 0; i < length; i++){
51
+ if (!('propertyChangedCallback' in this) || typeof this.propertyChangedCallback !== 'function') {
52
+ continue;
53
+ }
54
+ if (arr[i] in this) {
55
+ this.propertyChangedCallback(arr[i], undefined, this[arr[i]]);
56
+ }
57
+ }
58
+ }
59
+ propertyChangedCallback(_name, _oldValue, _value) {
60
+ throw new Error(`${this.constructor.name}: Method propertyChangedCallback is not implemented`);
61
+ }
62
+ }
3
63
 
4
64
  var ObjectFit = /*#__PURE__*/ function(ObjectFit) {
5
65
  ObjectFit["Contain"] = "contain";
6
66
  ObjectFit["Cover"] = "cover";
7
67
  ObjectFit["Fill"] = "fill";
8
- ObjectFit["ScaleDown"] = "scale-down";
9
68
  ObjectFit["None"] = "none";
69
+ ObjectFit["ScaleDown"] = "scale-down";
10
70
  return ObjectFit;
11
71
  }({});
12
72
  var PlayerState = /*#__PURE__*/ function(PlayerState) {
@@ -45,14 +105,298 @@ var PlayerEvents = /*#__PURE__*/ function(PlayerEvents) {
45
105
  var PreserveAspectRatio = /*#__PURE__*/ function(PreserveAspectRatio) {
46
106
  PreserveAspectRatio["Contain"] = "xMidYMid meet";
47
107
  PreserveAspectRatio["Cover"] = "xMidYMid slice";
48
- PreserveAspectRatio["None"] = "xMinYMin slice";
49
108
  PreserveAspectRatio["Initial"] = "none";
109
+ PreserveAspectRatio["None"] = "xMinYMin slice";
50
110
  return PreserveAspectRatio;
51
111
  }({});
112
+ var RendererType = /*#__PURE__*/ function(RendererType) {
113
+ RendererType["Canvas"] = "canvas";
114
+ RendererType["HTML"] = "html";
115
+ RendererType["SVG"] = "svg";
116
+ return RendererType;
117
+ }({});
118
+
119
+ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-toolbar-height: 35px;\n --lottie-player-toolbar-background-color: #fff;\n --lottie-player-toolbar-icon-color: #000;\n --lottie-player-toolbar-icon-hover-color: #000;\n --lottie-player-toolbar-icon-active-color: #4285f4;\n --lottie-player-seeker-track-color: rgb(0 0 0 / 20%);\n --lottie-player-seeker-thumb-color: #4285f4;\n --lottie-player-seeker-display: block;\n\n width: 100%;\n height: 100%;\n\n &:not([hidden]) {\n display: block;\n }\n\n .main {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n margin: 0;\n padding: 0;\n }\n\n .animation {\n width: 100%;\n height: 100%;\n display: flex;\n margin: 0;\n padding: 0;\n }\n\n [data-controls='true'] .animation {\n height: calc(100% - 35px);\n }\n\n .animation-container {\n position: relative;\n }\n\n .popover {\n position: absolute;\n right: 5px;\n bottom: 40px;\n background-color: var(--lottie-player-toolbar-background-color);\n border-radius: 5px;\n padding: 10px 15px;\n border: solid 2px var(--lottie-player-toolbar-icon-color);\n animation: fade-in 0.2s ease-in-out;\n\n &::before {\n content: '';\n right: 10px;\n border: 7px solid transparent;\n margin-right: -7px;\n height: 0;\n width: 0;\n position: absolute;\n pointer-events: none;\n top: 100%;\n border-top-color: var(--lottie-player-toolbar-icon-color);\n }\n }\n\n .error {\n display: flex;\n margin: auto;\n justify-content: center;\n height: 100%;\n align-items: center;\n\n & svg {\n width: 100%;\n height: auto;\n }\n }\n\n .toolbar {\n display: flex;\n place-items: center center;\n background: var(--lottie-player-toolbar-background-color);\n margin: 0;\n height: 35px;\n padding: 5px;\n border-radius: 5px;\n gap: 5px;\n\n &.has-error {\n pointer-events: none;\n opacity: 0.5;\n }\n\n & button {\n cursor: pointer;\n fill: var(--lottie-player-toolbar-icon-color);\n color: var(--lottie-player-toolbar-icon-color);\n background: none;\n border: 0;\n padding: 0;\n outline: 0;\n height: 100%;\n margin: 0;\n align-items: center;\n gap: 5px;\n opacity: 0.9;\n\n &:not([hidden]) {\n display: flex;\n }\n\n &:hover {\n opacity: 1;\n }\n\n &[data-active='true'] {\n opacity: 1;\n fill: var(--lottie-player-toolbar-icon-active-color);\n }\n\n &:disabled {\n opacity: 0.5;\n }\n\n &:focus {\n outline: 0;\n }\n\n & svg {\n pointer-events: none;\n\n & > * {\n fill: inherit;\n }\n }\n\n &.disabled svg {\n display: none;\n }\n }\n }\n\n .progress-container {\n position: relative;\n width: 100%;\n\n &.simple {\n margin-right: 12px;\n }\n }\n\n .seeker {\n appearance: none;\n outline: none;\n width: 100%;\n height: 20px;\n border-radius: 3px;\n border: 0;\n cursor: pointer;\n background-color: transparent;\n\n display: var(--lottie-player-seeker-display);\n color: var(--lottie-player-seeker-thumb-color);\n margin: 0;\n padding: 7.5px 0;\n position: relative;\n z-index: 1;\n\n &::-webkit-slider-runnable-track,\n &::-webkit-slider-thumb {\n appearance: none;\n outline: none;\n }\n\n &::-webkit-slider-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n border: 0;\n background-color: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -webkit-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-webkit-slider-thumb,\n &:focus::-webkit-slider-thumb {\n transform: scale(1);\n }\n\n &::-moz-range-progress {\n background-color: var(--lottie-player-seeker-thumb-color);\n height: 5px;\n border-radius: 3px;\n }\n\n &::-moz-range-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background-color: var(--lottie-player-seeker-thumb-color);\n border: 0;\n cursor: pointer;\n -moz-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-moz-range-thumb,\n &:focus::-moz-range-thumb {\n transform: scale(1);\n }\n\n &::-ms-track {\n width: 100%;\n height: 5px;\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n }\n\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n border-radius: 3px;\n }\n\n &::-ms-fill-lower {\n background-color: var(--lottie-player-seeker-thumb-color);\n border-radius: 3px;\n }\n\n &::-ms-thumb {\n border: 0;\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -ms-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-ms-thumb {\n transform: scale(1);\n }\n\n &:focus {\n &::-ms-thumb {\n transform: scale(1);\n }\n\n &::-ms-fill-lower,\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n }\n }\n }\n\n & progress {\n appearance: none;\n outline: none;\n position: absolute;\n width: 100%;\n height: 5px;\n border-radius: 3px;\n border: 0;\n top: 0;\n left: 0;\n margin: 7.5px 0;\n background-color: var(--lottie-player-seeker-track-color);\n pointer-events: none;\n\n &::-webkit-progress-inner-element {\n border-radius: 3px;\n overflow: hidden;\n }\n\n &::-webkit-slider-runnable-track {\n background-color: transparent;\n }\n\n &::-webkit-progress-value {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n }\n\n & *::-moz-progress-bar {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n}\n\n@keyframes fade-in {\n 0% {\n opacity: 0;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --lottie-player-toolbar-background-color: #000;\n --lottie-player-toolbar-icon-color: #fff;\n --lottie-player-toolbar-icon-hover-color: #fff;\n --lottie-player-seeker-track-color: rgb(255 255 255 / 60%);\n }\n}\n";
120
+
121
+ /**
122
+ * Render Controls.
123
+ */ function renderControls() {
124
+ if (!this.shadow) {
125
+ throw new Error('No Shadow Element');
126
+ }
127
+ const slot = this.shadow.querySelector('slot[name=controls]');
128
+ if (!slot) {
129
+ return;
130
+ }
131
+ if (!this.controls) {
132
+ slot.innerHTML = '';
133
+ return;
134
+ }
135
+ slot.innerHTML = /* HTML */ `<div class="lottie-controls toolbar ${this.playerState === PlayerState.Error ? 'has-error' : ''}" aria-label="Lottie Animation controls"><button class="togglePlay" data-active="false" aria-label="Toggle Play/Pause"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M8.016 5.016L18.985 12 8.016 18.984V5.015z"/></svg></button> <button class="stop" data-active="true" aria-label="Stop"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M6 6h12v12H6V6z"/></svg></button> <button class="prev" aria-label="Previous animation" hidden><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.9 18.2 8.1 12l9.8-6.2v12.4zm-10.3 0H6.1V5.8h1.5v12.4z"/></svg></button> <button class="next" aria-label="Next animation" hidden><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m6.1 5.8 9.8 6.2-9.8 6.2V5.8zM16.4 5.8h1.5v12.4h-1.5z"/></svg></button><form class="progress-container${this.simple ? ' simple' : ''}"><input class="seeker" type="range" min="0" max="100" step="1" value="${this._seeker.toString()}" aria-valuemin="0" aria-valuemax="100" role="slider" aria-valuenow="${this._seeker.toString()}" tabindex="0" aria-label="Slider for search"><progress max="100" value="${this._seeker}"></progress></form>${this.simple ? '' : /* HTML */ `<button class="toggleLoop" data-active="${this.loop}" tabindex="0" aria-label="Toggle loop"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg></button> <button class="toggleBoomerang" data-active="${this.mode === PlayMode.Bounce}" aria-label="Toggle boomerang" tabindex="0"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m11.8 13.2-.3.3c-.5.5-1.1 1.1-1.7 1.5-.5.4-1 .6-1.5.8-.5.2-1.1.3-1.6.3s-1-.1-1.5-.3c-.6-.2-1-.5-1.4-1-.5-.6-.8-1.2-.9-1.9-.2-.9-.1-1.8.3-2.6.3-.7.8-1.2 1.3-1.6.3-.2.6-.4 1-.5.2-.2.5-.2.8-.3.3 0 .7-.1 1 0 .3 0 .6.1.9.2.9.3 1.7.9 2.4 1.5.4.4.8.7 1.1 1.1l.1.1.4-.4c.6-.6 1.2-1.2 1.9-1.6.5-.3 1-.6 1.5-.7.4-.1.7-.2 1-.2h.9c1 .1 1.9.5 2.6 1.4.4.5.7 1.1.8 1.8.2.9.1 1.7-.2 2.5-.4.9-1 1.5-1.8 2-.4.2-.7.4-1.1.4-.4.1-.8.1-1.2.1-.5 0-.9-.1-1.3-.3-.8-.3-1.5-.9-2.1-1.5-.4-.4-.8-.7-1.1-1.1h-.3zm-1.1-1.1c-.1-.1-.1-.1 0 0-.3-.3-.6-.6-.8-.9-.5-.5-1-.9-1.6-1.2-.4-.3-.8-.4-1.3-.4-.4 0-.8 0-1.1.2-.5.2-.9.6-1.1 1-.2.3-.3.7-.3 1.1 0 .3 0 .6.1.9.1.5.4.9.8 1.2.5.4 1.1.5 1.7.5.5 0 1-.2 1.5-.5.6-.4 1.1-.8 1.6-1.3.1-.3.3-.5.5-.6zM13 12c.5.5 1 1 1.5 1.4.5.5 1.1.9 1.9 1 .4.1.8 0 1.2-.1.3-.1.6-.3.9-.5.4-.4.7-.9.8-1.4.1-.5 0-.9-.1-1.4-.3-.8-.8-1.2-1.7-1.4-.4-.1-.8-.1-1.2 0-.5.1-1 .4-1.4.7-.5.4-1 .8-1.4 1.2-.2.2-.4.3-.5.5z"/></svg></button> <button class="toggleSettings" aria-label="Settings" aria-haspopup="true" aria-expanded="${Boolean(this._isSettingsOpen)}" aria-controls="${this._identifier}-settings"><svg width="24" height="24" aria-hidden="true" focusable="false"><circle cx="12" cy="5.4" r="2.5"/><circle cx="12" cy="12" r="2.5"/><circle cx="12" cy="18.6" r="2.5"/></svg></button><div id="${this._identifier}-settings" class="popover" hidden><button class="convert" aria-label="Convert JSON animation to dotLottie format" hidden><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg> Convert to dotLottie</button> <button class="snapshot" aria-label="Download still image"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M16.8 10.8 12 15.6l-4.8-4.8h3V3.6h3.6v7.2h3zM12 15.6H3v4.8h18v-4.8h-9zm7.8 2.4h-2.4v-1.2h2.4V18z"/></svg> Download still image</button></div>`}</div>`;
136
+ const togglePlay = this.shadow.querySelector('.togglePlay');
137
+ if (togglePlay instanceof HTMLButtonElement) {
138
+ togglePlay.onclick = this.togglePlay;
139
+ }
140
+ const stop = this.shadow.querySelector('.stop');
141
+ if (stop instanceof HTMLButtonElement) {
142
+ stop.onclick = this.stop;
143
+ }
144
+ const prev = this.shadow.querySelector('.prev');
145
+ if (prev instanceof HTMLButtonElement) {
146
+ prev.onclick = this.prev;
147
+ }
148
+ const next = this.shadow.querySelector('.next');
149
+ if (next instanceof HTMLButtonElement) {
150
+ next.onclick = this.next;
151
+ }
152
+ const seeker = this.shadow.querySelector('.seeker');
153
+ if (seeker instanceof HTMLInputElement) {
154
+ seeker.onchange = this._handleSeekChange;
155
+ seeker.onmousedown = this._freeze;
156
+ }
157
+ if (!this.simple) {
158
+ const toggleLoop = this.shadow.querySelector('.toggleLoop');
159
+ if (toggleLoop instanceof HTMLButtonElement) {
160
+ toggleLoop.onclick = this.toggleLoop;
161
+ }
162
+ const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
163
+ if (toggleBoomerang instanceof HTMLButtonElement) {
164
+ toggleBoomerang.onclick = this.toggleBoomerang;
165
+ }
166
+ const convert = this.shadow.querySelector('.convert');
167
+ if (convert instanceof HTMLButtonElement) {
168
+ convert.onclick = this.convert;
169
+ }
170
+ const snapshot = this.shadow.querySelector('.snapshot');
171
+ if (snapshot instanceof HTMLButtonElement) {
172
+ snapshot.onclick = ()=>this.snapshot(true);
173
+ }
174
+ const toggleSettings = this.shadow.querySelector('.toggleSettings');
175
+ if (toggleSettings instanceof HTMLButtonElement) {
176
+ toggleSettings.onclick = this._handleSettingsClick;
177
+ toggleSettings.onblur = this._handleBlur;
178
+ }
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Render Player.
184
+ */ async function renderPlayer() {
185
+ if (!this.shadow) {
186
+ throw new Error('No Shadow Element');
187
+ }
188
+ this.template.innerHTML = /* HTML */ `<div class="animation-container main" data-controls="${this.controls ?? false}" lang="${this.description ? document.documentElement.lang : 'en'}" aria-label="${this.description ?? 'Lottie animation'}" data-loaded="${this._playerState.loaded}"><figure class="animation" style="background:${this.background}">${this.playerState === PlayerState.Error ? /* HTML */ `<div class="error"><svg preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1920" height="1080" viewBox="0 0 1920 1080"><path fill="#fff" d="M0 0h1920v1080H0z"/><path fill="#3a6d8b" d="M1190.2 531 1007 212.4c-22-38.2-77.2-38-98.8.5L729.5 531.3c-21.3 37.9 6.1 84.6 49.5 84.6l361.9.3c43.7 0 71.1-47.3 49.3-85.2zM937.3 288.7c.2-7.5 3.3-23.9 23.2-23.9 16.3 0 23 16.1 23 23.5 0 55.3-10.7 197.2-12.2 214.5-.1 1-.9 1.7-1.9 1.7h-18.3c-1 0-1.8-.7-1.9-1.7-1.4-17.5-13.4-162.9-11.9-214.1zm24.2 283.8c-13.1 0-23.7-10.6-23.7-23.7s10.6-23.7 23.7-23.7 23.7 10.6 23.7 23.7-10.6 23.7-23.7 23.7zM722.1 644h112.6v34.4h-70.4V698h58.8v31.7h-58.8v22.6h72.4v36.2H722.1V644zm162 57.1h.6c8.3-12.9 18.2-17.8 31.3-17.8 3 0 5.1.4 6.3 1v32.6h-.8c-22.4-3.8-35.6 6.3-35.6 29.5v42.3h-38.2V685.5h36.4v15.6zm78.9 0h.6c8.3-12.9 18.2-17.8 31.3-17.8 3 0 5.1.4 6.3 1v32.6h-.8c-22.4-3.8-35.6 6.3-35.6 29.5v42.3h-38.2V685.5H963v15.6zm39.5 36.2c0-31.3 22.2-54.8 56.6-54.8 34.4 0 56.2 23.5 56.2 54.8s-21.8 54.6-56.2 54.6c-34.4-.1-56.6-23.3-56.6-54.6zm74 0c0-17.4-6.1-29.1-17.8-29.1-11.7 0-17.4 11.7-17.4 29.1 0 17.4 5.7 29.1 17.4 29.1s17.8-11.8 17.8-29.1zm83.1-36.2h.6c8.3-12.9 18.2-17.8 31.3-17.8 3 0 5.1.4 6.3 1v32.6h-.8c-22.4-3.8-35.6 6.3-35.6 29.5v42.3h-38.2V685.5h36.4v15.6z"/><path fill="none" d="M718.9 807.7h645v285.4h-645z"/><text fill="#3a6d8b" style="text-align:center;position:absolute;left:100%;font-size:47px;font-family:system-ui,-apple-system,BlinkMacSystemFont,'.SFNSText-Regular',sans-serif" x="50%" y="848.017" text-anchor="middle">${this._errorMessage}</text></svg></div>` : ''}</figure><slot name="controls"></slot></div>`;
189
+ this.shadow.adoptedStyleSheets = [
190
+ await DotLottiePlayer.styles()
191
+ ];
192
+ this.shadow.appendChild(this.template.content.cloneNode(true));
193
+ }
52
194
 
53
195
  class CustomError extends Error {
54
196
  }
55
- const addExt = (ext, str)=>{
197
+ /**
198
+ * Methods used locally and exported.
199
+ */ const getManifest = (unzipped)=>{
200
+ const file = strFromU8(unzipped['manifest.json'], false), manifest = JSON.parse(file);
201
+ if (!('animations' in manifest)) {
202
+ throw new Error('Manifest not found');
203
+ }
204
+ if (manifest.animations.length === 0) {
205
+ throw new Error('No animations listed in manifest');
206
+ }
207
+ return manifest;
208
+ }, isServer = ()=>!(typeof window !== 'undefined' && window.document);
209
+ /**
210
+ * Methods used only locally.
211
+ */ const hasExt = (path)=>{
212
+ const lastDotIndex = path?.split('/').pop()?.lastIndexOf('.');
213
+ return (lastDotIndex ?? 0) > 1 && path && path.length - 1 > (lastDotIndex ?? 0);
214
+ };
215
+ /**
216
+ * Get extension from filename, URL or path.
217
+ */ const getExt = (str)=>{
218
+ if (typeof str !== 'string' || !str || !hasExt(str)) {
219
+ return;
220
+ }
221
+ return str.split('.').pop()?.toLowerCase();
222
+ };
223
+ const unzip = async (resp)=>{
224
+ const u8 = new Uint8Array(await resp.arrayBuffer()), unzipped = await new Promise((resolve, reject)=>{
225
+ unzip$1(u8, (err, file)=>{
226
+ if (err) {
227
+ reject(err);
228
+ }
229
+ resolve(file);
230
+ });
231
+ });
232
+ return unzipped;
233
+ }, getArrayBuffer = async (zippable)=>{
234
+ const arrayBuffer = await new Promise((resolve, reject)=>{
235
+ zip(zippable, {
236
+ level: 9
237
+ }, (err, data)=>{
238
+ if (err) {
239
+ reject(err);
240
+ return;
241
+ }
242
+ if (!(data.buffer instanceof ArrayBuffer)) {
243
+ reject(new Error('Data is not transferable'));
244
+ return;
245
+ }
246
+ resolve(data.buffer);
247
+ });
248
+ });
249
+ return arrayBuffer;
250
+ }, getMimeFromExt = (ext)=>{
251
+ switch(ext){
252
+ case 'svg':
253
+ case 'svg+xml':
254
+ {
255
+ return 'image/svg+xml';
256
+ }
257
+ case 'jpg':
258
+ case 'jpeg':
259
+ {
260
+ return 'image/jpeg';
261
+ }
262
+ case 'png':
263
+ case 'gif':
264
+ case 'webp':
265
+ case 'avif':
266
+ {
267
+ return `image/${ext}`;
268
+ }
269
+ case 'mp3':
270
+ case 'mpeg':
271
+ case 'wav':
272
+ {
273
+ return `audio/${ext}`;
274
+ }
275
+ default:
276
+ {
277
+ return '';
278
+ }
279
+ }
280
+ }, isAudio = (asset)=>!('h' in asset) && !('w' in asset) && 'p' in asset && 'e' in asset && 'u' in asset && 'id' in asset, isImage = (asset)=>'w' in asset && 'h' in asset && !('xt' in asset) && 'p' in asset, parseBase64 = (str)=>str.slice(Math.max(0, str.indexOf(',') + 1)), isBase64 = (str)=>{
281
+ if (!str) {
282
+ return false;
283
+ }
284
+ const regex = /^(?:[0-9a-z+/]{4})*(?:[0-9a-z+/]{2}==|[0-9a-z+/]{3}=)?$/i;
285
+ return regex.test(parseBase64(str));
286
+ }, resolveAssets = async (unzipped, assets)=>{
287
+ if (!Array.isArray(assets)) {
288
+ return;
289
+ }
290
+ const toResolve = [], { length } = assets;
291
+ for(let i = 0; i < length; i++){
292
+ if (!isAudio(assets[i]) && !isImage(assets[i])) {
293
+ continue;
294
+ }
295
+ const type = isImage(assets[i]) ? 'images' : 'audio', u8 = unzipped?.[`${type}/${assets[i].p}`];
296
+ if (!u8) {
297
+ continue;
298
+ }
299
+ toResolve.push(new Promise((resolveAsset)=>{
300
+ let assetB64;
301
+ if (isServer()) {
302
+ assetB64 = Buffer.from(u8).toString('base64');
303
+ } else {
304
+ let result = '';
305
+ const { length: jLen } = u8;
306
+ for(let j = 0; j < jLen; j++){
307
+ result += String.fromCharCode(u8[j]);
308
+ }
309
+ assetB64 = btoa(result);
310
+ }
311
+ assets[i].p = assets[i].p?.startsWith('data:') || isBase64(assets[i].p) ? assets[i].p : `data:${getMimeFromExt(getExt(assets[i].p))};base64,${assetB64}`;
312
+ assets[i].e = 1;
313
+ assets[i].u = '';
314
+ resolveAsset();
315
+ }));
316
+ }
317
+ await Promise.all(toResolve);
318
+ }, prepareString = (str)=>str.replaceAll(new RegExp(/"""/, 'g'), '""').replaceAll(/(["'])(.*?)\1/g, (_match, quote, content)=>{
319
+ const replacedContent = content.replaceAll(/[^\w\s.#]/g, '');
320
+ return `${quote}${replacedContent}${quote}`;
321
+ }), fileToBase64 = async (url)=>{
322
+ const response = await fetch(url), blob = await response.blob();
323
+ return new Promise((resolve, reject)=>{
324
+ try {
325
+ const reader = new FileReader();
326
+ reader.onload = ()=>{
327
+ if (typeof reader.result === 'string') {
328
+ resolve(reader.result);
329
+ return;
330
+ }
331
+ reject(new Error('Could not create bas64'));
332
+ };
333
+ reader.readAsDataURL(blob);
334
+ } catch (error) {
335
+ reject(error);
336
+ }
337
+ });
338
+ }, getLottieJSON = async (resp)=>{
339
+ const unzipped = await unzip(resp), manifest = getManifest(unzipped), data = [], toResolve = [], { length } = manifest.animations;
340
+ /**
341
+ * Check whether Lottie animations folder is abbreviated.
342
+ */ let animationsFolder = 'animations';
343
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
344
+ if (unzipped[`a/${manifest.animations[0].id}.json`]) {
345
+ animationsFolder = 'a';
346
+ }
347
+ for(let i = 0; i < length; i++){
348
+ const str = strFromU8(unzipped[`${animationsFolder}/${manifest.animations[i].id}.json`]), lottie = JSON.parse(prepareString(str));
349
+ // Handle Expressions
350
+ const { length: jLen } = lottie.layers;
351
+ for(let j = 0; j < jLen; j++){
352
+ const { ks: shape } = lottie.layers[j], props = Object.keys(shape), { length: pLen } = props;
353
+ for(let p = 0; p < pLen; p++){
354
+ const { e: isEncoded, x: expression } = shape[props[p]];
355
+ if (!expression || !isEncoded) {
356
+ continue;
357
+ }
358
+ // Base64 Decode to handle compression
359
+ // @ts-expect-error
360
+ lottie.layers[j].ks[props[p]].x = atob(expression);
361
+ }
362
+ }
363
+ toResolve.push(resolveAssets(unzipped, lottie.assets));
364
+ data.push(lottie);
365
+ }
366
+ await Promise.all(toResolve);
367
+ return {
368
+ data,
369
+ manifest
370
+ };
371
+ };
372
+ /**
373
+ * Download file, either SVG or dotLottie.
374
+ */ const download = (data, options)=>{
375
+ const blob = new Blob([
376
+ data
377
+ ], {
378
+ type: options?.mimeType
379
+ }), fileName = options?.name || createElementID(), dataURL = URL.createObjectURL(blob), link = document.createElement('a');
380
+ link.href = dataURL;
381
+ link.download = fileName;
382
+ link.hidden = true;
383
+ document.body.appendChild(link);
384
+ link.click();
385
+ setTimeout(()=>{
386
+ link.remove();
387
+ URL.revokeObjectURL(dataURL);
388
+ }, 1000);
389
+ }, /**
390
+ * Parse URL to get filename.
391
+ *
392
+ * @param src - The url string.
393
+ * @param keepExt - Whether to include file extension.
394
+ * @returns Filename, in lowercase.
395
+ */ getFilename = (src, keepExt)=>{
396
+ // Because the regex strips all special characters, we need to extract the file extension, so we can add it later if we need it
397
+ getExt(src);
398
+ return `${src.split('/').pop()?.replace(/\.[^.]*$/, '').replaceAll(/\W+/g, '-')}${''}`;
399
+ }, addExt = (ext, str)=>{
56
400
  if (!str) {
57
401
  return;
58
402
  }
@@ -67,30 +411,59 @@ const addExt = (ext, str)=>{
67
411
  switch(objectFit){
68
412
  case ObjectFit.Contain:
69
413
  case ObjectFit.ScaleDown:
70
- return 'xMidYMid meet';
414
+ {
415
+ return PreserveAspectRatio$1.Contain;
416
+ }
71
417
  case ObjectFit.Cover:
72
- return 'xMidYMid slice';
418
+ {
419
+ return PreserveAspectRatio$1.Cover;
420
+ }
73
421
  case ObjectFit.Fill:
74
- return 'none';
422
+ {
423
+ return PreserveAspectRatio$1.Initial;
424
+ }
75
425
  case ObjectFit.None:
76
- return 'xMinYMin slice';
426
+ {
427
+ return PreserveAspectRatio$1.None;
428
+ }
77
429
  default:
78
- return 'xMidYMid meet';
430
+ {
431
+ return PreserveAspectRatio$1.Contain;
432
+ }
433
+ }
434
+ }, /**
435
+ * Convert Base64 encoded string to Uint8Array.
436
+ *
437
+ * @param str - Base64 encoded string.
438
+ * @returns UTF-8/Latin-1 binary.
439
+ */ base64ToU8 = (str)=>strToU8(isServer() ? Buffer.from(parseBase64(str), 'base64').toString('binary') : atob(parseBase64(str)), true), getExtFromB64 = (str)=>{
440
+ const mime = str.split(':')[1].split(';')[0], ext = mime.split('/')[1].split('+')[0];
441
+ return ext;
442
+ }, handleErrors = (err)=>{
443
+ const res = {
444
+ message: 'Unknown error',
445
+ status: isServer() ? 500 : 400
446
+ };
447
+ if (err && typeof err === 'object') {
448
+ if ('message' in err && typeof err.message === 'string') {
449
+ res.message = err.message;
450
+ }
451
+ if ('status' in err) {
452
+ res.status = Number(err.status);
453
+ }
79
454
  }
455
+ return res;
80
456
  }, /**
81
- * Convert Base64 encoded string to Uint8Array
82
- * @param { string } str Base64 encoded string
83
- * @returns { Uint8Array } UTF-8/Latin-1 binary
84
- */ base64ToU8 = (str)=>strToU8(isServer() ? Buffer.from(parseBase64(str), 'base64').toString('binary') : atob(parseBase64(str)), true), /**
85
457
  * Convert a JSON Lottie to dotLottie or combine several animations and download new dotLottie file in your browser.
86
- */ createDotLottie = async ({ animations, fileName, manifest, shouldDownload = true })=>{
458
+ */ createDotLottie = async ({ animations = [], fileName, manifest, shouldDownload = true })=>{
87
459
  try {
88
460
  // Input validation
89
- if (!animations?.length || !manifest) {
90
- throw new Error(`Missing or malformed required parameter(s):\n ${animations?.length ? '- manifest\n' : ''} ${manifest ? '- animations\n' : ''}`);
461
+ if (animations.length === 0 || !manifest) {
462
+ throw new Error(`Missing or malformed required parameter(s):\n ${animations.length > 0 ? '- manifest\n' : ''} ${manifest ? '- animations\n' : ''}`);
91
463
  }
92
- const manifestCompressionLevel = 0, animationCompressionLevel = 9, // Prepare the dotLottie file
93
- name = addExt('lottie', fileName) || `${useId()}.lottie`, dotlottie = {
464
+ const manifestCompressionLevel = 0, animationCompressionLevel = 9, /**
465
+ * Prepare the dotLottie file.
466
+ */ name = addExt('lottie', fileName) || `${createElementID()}.lottie`, dotlottie = {
94
467
  'manifest.json': [
95
468
  strToU8(JSON.stringify(manifest), true),
96
469
  {
@@ -99,19 +472,31 @@ const addExt = (ext, str)=>{
99
472
  ]
100
473
  };
101
474
  // Add animations and assets to the dotLottie file
102
- for (const [i, animation] of animations.entries()){
103
- for (const asset of animation.assets ?? []){
475
+ const { length } = animations;
476
+ for(let i = 0; i < length; i++){
477
+ const { length: jLen } = animations[i].assets;
478
+ // Prepare assets
479
+ for(let j = 0; j < jLen; j++){
480
+ const asset = animations[i].assets[j];
104
481
  if (!asset.p || !isImage(asset) && !isAudio(asset)) {
105
482
  continue;
106
483
  }
107
- const { p: file, u: path } = asset, // Original asset.id caused issues with multianimations
108
- assetId = useId('asset'), isEncoded = file.startsWith('data:'), ext = isEncoded ? getExtFromB64(file) : getExt(file), // Check if the asset is already base64-encoded. If not, get path, fetch it, and encode it
109
- dataURL = isEncoded ? file : await fileToBase64(path ? path.endsWith('/') && `${path}${file}` || `${path}/${file}` : file);
110
- asset.p = `${assetId}.${ext}`;
111
- // Asset is embedded, so path empty string
112
- asset.u = '';
484
+ const { p: file, u: path } = asset;
485
+ if (!file) {
486
+ continue;
487
+ }
488
+ // Original asset.id caused issues with multianimations
489
+ const assetId = createElementID(), isEncoded = file.startsWith('data:'), ext = isEncoded ? getExtFromB64(file) : getExt(file), /**
490
+ * Check if the asset is already base64-encoded. If not, get path, fetch it, and encode it.
491
+ */ dataURL = isEncoded ? file : await fileToBase64(path ? path.endsWith('/') && `${path}${file}` || `${path}/${file}` : file);
113
492
  // Asset is encoded
114
- asset.e = 1;
493
+ // eslint-disable-next-line require-atomic-updates
494
+ animations[i].assets[j].e = 1;
495
+ // eslint-disable-next-line require-atomic-updates
496
+ animations[i].assets[j].p = `${assetId}.${ext}`;
497
+ // Asset is embedded, so path empty string
498
+ // eslint-disable-next-line require-atomic-updates
499
+ animations[i].assets[j].u = '';
115
500
  dotlottie[`${isAudio(asset) ? 'audio' : 'images'}/${assetId}.${ext}`] = [
116
501
  base64ToU8(dataURL),
117
502
  {
@@ -119,70 +504,59 @@ const addExt = (ext, str)=>{
119
504
  }
120
505
  ];
121
506
  }
122
- dotlottie[`animations/${manifest.animations[i].id}.json`] = [
123
- strToU8(JSON.stringify(animation), true),
507
+ // Prepare expressions
508
+ const { length: kLen } = animations[i].layers;
509
+ for(let k = 0; k < kLen; k++){
510
+ const { ks: shape } = animations[i].layers[k], props = Object.keys(shape), { length: pLen } = props;
511
+ for(let p = 0; p < pLen; p++){
512
+ const { x: expression } = shape[props[p]];
513
+ if (!expression) {
514
+ continue;
515
+ }
516
+ // Base64 Encode to handle compression
517
+ // @ts-expect-error: We have checked this property is set
518
+ animations[i].layers[k].ks[props[p]].x = btoa(expression);
519
+ // Set e (encoded) to 1
520
+ // @ts-expect-error: We have checked this property is set
521
+ animations[i].layers[k].ks[props[p]].e = 1;
522
+ }
523
+ }
524
+ dotlottie[`a/${manifest.animations[i].id}.json`] = [
525
+ strToU8(JSON.stringify(animations[i]), true),
124
526
  {
125
527
  level: animationCompressionLevel
126
528
  }
127
529
  ];
128
530
  }
129
531
  const buffer = await getArrayBuffer(dotlottie);
130
- return shouldDownload ? download(buffer, {
131
- mimeType: 'application/zip',
132
- name
133
- }) : buffer;
134
- } catch (err) {
135
- console.error(`❌ ${handleErrors(err).message}`);
136
- }
137
- }, createJSON = ({ animation, fileName, shouldDownload })=>{
532
+ if (shouldDownload) {
533
+ download(buffer, {
534
+ mimeType: 'application/zip',
535
+ name
536
+ });
537
+ return null;
538
+ }
539
+ return buffer;
540
+ } catch (error) {
541
+ console.error(`❌ ${handleErrors(error).message}`);
542
+ return null;
543
+ }
544
+ }, createJSON = ({ animation, fileName, shouldDownload })=>{
138
545
  try {
139
546
  if (!animation) {
140
- throw new Error("Missing or malformed required parameter(s):\n - animation\n'");
547
+ throw new Error('createJSON: Missing or malformed required parameter(s):\n - animation\n\'');
141
548
  }
142
- const name = addExt('json', fileName) || `${useId()}.json`, jsonString = JSON.stringify(animation);
143
- return shouldDownload ? download(jsonString, {
144
- mimeType: 'application/json',
145
- name
146
- }) : jsonString;
147
- } catch (err) {
148
- console.error(`❌ ${handleErrors(err).message}`);
149
- }
150
- }, /**
151
- * Download file, either SVG or dotLottie.
152
- * @param { string } data The data to be downloaded
153
- * @param { string } name Don't include file extension in the filename
154
- */ download = (data, options)=>{
155
- const blob = new Blob([
156
- data
157
- ], {
158
- type: options?.mimeType
159
- }), fileName = options?.name || useId(), dataURL = URL.createObjectURL(blob), link = document.createElement('a');
160
- link.href = dataURL;
161
- link.download = fileName;
162
- link.hidden = true;
163
- document.body.appendChild(link);
164
- link.click();
165
- setTimeout(()=>{
166
- link.remove();
167
- URL.revokeObjectURL(dataURL);
168
- }, 1000);
169
- }, fileToBase64 = async (url)=>{
170
- const response = await fetch(url), blob = await response.blob();
171
- return new Promise((resolve, reject)=>{
172
- try {
173
- const reader = new FileReader();
174
- reader.onload = ()=>{
175
- if (typeof reader.result === 'string') {
176
- resolve(reader.result);
177
- return;
178
- }
179
- reject();
180
- };
181
- reader.readAsDataURL(blob);
182
- } catch (e) {
183
- reject(e);
549
+ const name = addExt('json', fileName) || `${createElementID()}.json`, jsonString = JSON.stringify(animation);
550
+ if (shouldDownload) {
551
+ download(jsonString, {
552
+ mimeType: 'application/json',
553
+ name
554
+ });
184
555
  }
185
- });
556
+ return;
557
+ } catch (error) {
558
+ console.error(`❌ ${handleErrors(error).message}`);
559
+ }
186
560
  }, frameOutput = (frame)=>((frame ?? 0) + 1).toString().padStart(3, '0'), getAnimationData = async (input)=>{
187
561
  try {
188
562
  if (!input || typeof input !== 'string' && typeof input !== 'object') {
@@ -195,7 +569,7 @@ const addExt = (ext, str)=>{
195
569
  return {
196
570
  animations,
197
571
  isDotLottie: false,
198
- manifest: undefined
572
+ manifest: null
199
573
  };
200
574
  }
201
575
  const result = await fetch(input, {
@@ -209,19 +583,24 @@ const addExt = (ext, str)=>{
209
583
  throw error;
210
584
  }
211
585
  /**
212
- * Check if file is JSON, first by parsing file name for extension,
213
- * then – if filename has no extension – by cloning the response
214
- * and parsing it for content.
215
- */ const ext = getExt(input);
216
- if (ext === 'json' || !ext) {
217
- if (ext) {
586
+ * Check if file is JSON, first by parsing headers for content-type,
587
+ * than by parsing filename, then – if filename has no extension – by
588
+ * cloning the response and parsing response for content.
589
+ */ let isJSON = true;
590
+ const contentType = result.headers.get('content-type');
591
+ if (contentType === 'application/zip+dotlottie') {
592
+ isJSON = false;
593
+ }
594
+ if (isJSON) {
595
+ const ext = getExt(input);
596
+ if (ext === 'json') {
218
597
  const lottie = await result.json();
219
598
  return {
220
599
  animations: [
221
600
  lottie
222
601
  ],
223
602
  isDotLottie: false,
224
- manifest: undefined
603
+ manifest: null
225
604
  };
226
605
  }
227
606
  const text = await result.clone().text();
@@ -232,9 +611,9 @@ const addExt = (ext, str)=>{
232
611
  lottie
233
612
  ],
234
613
  isDotLottie: false,
235
- manifest: undefined
614
+ manifest: null
236
615
  };
237
- } catch (_e) {
616
+ } catch (error) {
238
617
  /* empty */ }
239
618
  }
240
619
  const { data, manifest } = await getLottieJSON(result);
@@ -243,321 +622,264 @@ const addExt = (ext, str)=>{
243
622
  isDotLottie: true,
244
623
  manifest
245
624
  };
246
- } catch (err) {
247
- console.error(`❌ ${handleErrors(err).message}`);
625
+ } catch (error) {
626
+ console.error(`❌ ${handleErrors(error).message}`);
248
627
  return {
249
628
  animations: undefined,
250
629
  isDotLottie: false,
251
- manifest: undefined
630
+ manifest: null
252
631
  };
253
632
  }
254
- }, getArrayBuffer = async (zippable)=>{
255
- const arrayBuffer = await new Promise((resolve, reject)=>{
256
- zip(zippable, {
257
- level: 9
258
- }, (err, data)=>{
259
- if (err) {
260
- reject(err);
261
- return;
262
- }
263
- if (!(data.buffer instanceof ArrayBuffer)) {
264
- reject('Data is not transferable');
265
- return;
266
- }
267
- resolve(data.buffer);
268
- });
269
- });
270
- return arrayBuffer;
271
- }, /**
272
- * Get extension from filename, URL or path
273
- * @param { string } str Filename, URL or path
274
- */ getExt = (str)=>{
275
- if (typeof str !== 'string' || !str || !hasExt(str)) {
276
- return;
633
+ };
634
+
635
+ /**
636
+ * DotLottie Player Web Component.
637
+ */ class DotLottiePlayer extends PropertyCallbackElement {
638
+ /**
639
+ * Attributes to observe.
640
+ */ static get observedAttributes() {
641
+ return [
642
+ 'animateOnScroll',
643
+ 'autoplay',
644
+ 'controls',
645
+ 'direction',
646
+ 'hover',
647
+ 'loop',
648
+ 'mode',
649
+ 'speed',
650
+ 'src',
651
+ 'subframe'
652
+ ];
277
653
  }
278
- return str.split('.').pop()?.toLowerCase();
279
- }, getExtFromB64 = (str)=>{
280
- const mime = str.split(':')[1].split(';')[0], ext = mime.split('/')[1].split('+')[0];
281
- return ext;
282
- }, /**
283
- * Parse URL to get filename
284
- * @param { string } src The url string
285
- * @param { boolean } keepExt Whether to include file extension
286
- * @returns { string } Filename, in lowercase
287
- */ getFilename = (src, keepExt)=>{
288
- // Because the regex strips all special characters, we need to extract the file extension, so we can add it later if we need it
289
- getExt(src);
290
- return `${src.split('/').pop()?.replace(/\.[^.]*$/, '').replace(/\W+/g, '-')}${''}` // .toLowerCase()
291
- ;
292
- }, getLottieJSON = async (resp)=>{
293
- const unzipped = await unzip(resp), manifest = getManifest(unzipped), data = [], toResolve = [];
294
- for (const { id } of manifest.animations){
295
- const str = strFromU8(unzipped[`animations/${id}.json`]), lottie = JSON.parse(prepareString(str));
296
- toResolve.push(resolveAssets(unzipped, lottie.assets));
297
- data.push(lottie);
654
+ static get observedProperties() {
655
+ return [
656
+ 'playerState',
657
+ '_isSettingsOpen',
658
+ '_seeker',
659
+ '_currentAnimation',
660
+ '_animations'
661
+ ];
298
662
  }
299
- await Promise.all(toResolve);
300
- return {
301
- data,
302
- manifest
303
- };
304
- }, getManifest = (unzipped)=>{
305
- const file = strFromU8(unzipped['manifest.json'], false), manifest = JSON.parse(file);
306
- if (!('animations' in manifest)) {
307
- throw new Error('Manifest not found');
663
+ /**
664
+ * Return the styles for the component.
665
+ */ static get styles() {
666
+ return async ()=>{
667
+ const styleSheet = new CSSStyleSheet();
668
+ await styleSheet.replace(css_248z);
669
+ return styleSheet;
670
+ };
308
671
  }
309
- if (!manifest.animations.length) {
310
- throw new Error('No animations listed in manifest');
672
+ /**
673
+ * Whether to trigger next frame with scroll.
674
+ */ set animateOnScroll(value) {
675
+ this.setAttribute('animateOnScroll', Boolean(value).toString());
311
676
  }
312
- return manifest;
313
- }, getMimeFromExt = (ext)=>{
314
- switch(ext){
315
- case 'svg':
316
- case 'svg+xml':
317
- return 'image/svg+xml';
318
- case 'jpg':
319
- case 'jpeg':
320
- return 'image/jpeg';
321
- case 'png':
322
- case 'gif':
323
- case 'webp':
324
- case 'avif':
325
- return `image/${ext}`;
326
- case 'mp3':
327
- case 'mpeg':
328
- case 'wav':
329
- return `audio/${ext}`;
330
- default:
331
- return '';
677
+ get animateOnScroll() {
678
+ const val = this.getAttribute('animateOnScroll');
679
+ return Boolean(val === 'true' || val === '' || val === '1');
332
680
  }
333
- }, handleErrors = (err)=>{
334
- const res = {
335
- message: 'Unknown error',
336
- status: isServer() ? 500 : 400
337
- };
338
- if (err && typeof err === 'object') {
339
- if ('message' in err && typeof err.message === 'string') {
340
- res.message = err.message;
681
+ /**
682
+ * Autoplay.
683
+ */ set autoplay(value) {
684
+ this.setAttribute('autoplay', Boolean(value).toString());
685
+ }
686
+ get autoplay() {
687
+ const val = this.getAttribute('autoplay');
688
+ return Boolean(val === 'true' || val === '' || val === '1');
689
+ }
690
+ /**
691
+ * Background color.
692
+ */ set background(value) {
693
+ this.setAttribute('background', value);
694
+ }
695
+ get background() {
696
+ return this.getAttribute('background') || 'transparent';
697
+ }
698
+ /**
699
+ * Show controls.
700
+ */ set controls(value) {
701
+ this.setAttribute('controls', Boolean(value).toString());
702
+ }
703
+ get controls() {
704
+ const val = this.getAttribute('controls');
705
+ return Boolean(val === 'true' || val === '' || val === '1');
706
+ }
707
+ /**
708
+ * Number of times to loop.
709
+ */ set count(value) {
710
+ this.setAttribute('count', value.toString());
711
+ }
712
+ get count() {
713
+ const val = this.getAttribute('count');
714
+ if (val) {
715
+ return Number(val);
341
716
  }
342
- if ('status' in err) {
343
- res.status = Number(err.status);
717
+ return 0;
718
+ }
719
+ /**
720
+ * Description for screen readers.
721
+ */ set description(value) {
722
+ if (value) {
723
+ this.setAttribute('description', value);
344
724
  }
345
725
  }
346
- return res;
347
- }, hasExt = (path)=>{
348
- const lastDotIndex = path?.split('/').pop()?.lastIndexOf('.');
349
- return (lastDotIndex ?? 0) > 1 && path && path.length - 1 > (lastDotIndex ?? 0);
350
- }, isAudio = (asset)=>!('h' in asset) && !('w' in asset) && 'p' in asset && 'e' in asset && 'u' in asset && 'id' in asset, isBase64 = (str)=>{
351
- if (!str) {
352
- return false;
726
+ get description() {
727
+ return this.getAttribute('description');
353
728
  }
354
- const regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
355
- return regex.test(parseBase64(str));
356
- }, isImage = (asset)=>'w' in asset && 'h' in asset && !('xt' in asset) && 'p' in asset, isServer = ()=>!(typeof window !== 'undefined' && window.document), parseBase64 = (str)=>str.substring(str.indexOf(',') + 1), prepareString = (str)=>str.replace(new RegExp(/"""/, 'g'), '""').replace(/(["'])(.*?)\1/g, (_match, quote, content)=>{
357
- const replacedContent = content.replace(/[^\w\s\d.#]/g, '');
358
- return `${quote}${replacedContent}${quote}`;
359
- }), resolveAssets = async (unzipped, assets)=>{
360
- if (!Array.isArray(assets)) {
361
- return;
729
+ /**
730
+ * Direction of animation.
731
+ */ set direction(value) {
732
+ this.setAttribute('direction', value.toString());
362
733
  }
363
- const toResolve = [];
364
- for (const asset of assets){
365
- if (!isAudio(asset) && !isImage(asset)) {
366
- continue;
367
- }
368
- const type = isImage(asset) ? 'images' : 'audio', u8 = unzipped?.[`${type}/${asset.p}`];
369
- if (!u8) {
370
- continue;
734
+ get direction() {
735
+ const val = Number(this.getAttribute('direction'));
736
+ if (val === -1) {
737
+ return val;
371
738
  }
372
- toResolve.push(new Promise((resolveAsset)=>{
373
- const assetB64 = isServer() ? Buffer.from(u8).toString('base64') : btoa(u8.reduce((dat, byte)=>`${dat}${String.fromCharCode(byte)}`, ''));
374
- asset.p = asset.p?.startsWith('data:') || isBase64(asset.p) ? asset.p : `data:${getMimeFromExt(getExt(asset.p))};base64,${assetB64}`;
375
- asset.e = 1;
376
- asset.u = '';
377
- resolveAsset();
378
- }));
739
+ return 1;
379
740
  }
380
- await Promise.all(toResolve);
381
- }, unzip = async (resp)=>{
382
- const u8 = new Uint8Array(await resp.arrayBuffer()), unzipped = await new Promise((resolve, reject)=>{
383
- unzip$1(u8, /* { filter }, */ (err, file)=>{
384
- if (err) {
385
- reject(err);
386
- }
387
- resolve(file);
388
- });
389
- });
390
- return unzipped;
391
- }, useId = (prefix)=>{
392
- const s4 = ()=>((1 + Math.random()) * 0x10000 | 0).toString(16).substring(1);
393
- return `${prefix ?? `:${s4()}`}_${s4()}`;
394
- };
395
-
396
- /**
397
- * Render Player
398
- */ function renderPlayer() {
399
- this.template.innerHTML = /* HTML */ `<div class="animation-container main" data-controls="${this.controls ?? false}" lang="${this.description ? document?.documentElement?.lang : 'en'}" aria-label="${this.description ?? 'Lottie animation'}" data-loaded="${this._playerState.loaded}"><figure class="animation" style="background:${this.background}">${this.playerState === PlayerState.Error ? /* HTML */ `<div class="error"><svg preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1920" height="1080" viewBox="0 0 1920 1080"><path fill="#fff" d="M0 0h1920v1080H0z"/><path fill="#3a6d8b" d="M1190.2 531 1007 212.4c-22-38.2-77.2-38-98.8.5L729.5 531.3c-21.3 37.9 6.1 84.6 49.5 84.6l361.9.3c43.7 0 71.1-47.3 49.3-85.2zM937.3 288.7c.2-7.5 3.3-23.9 23.2-23.9 16.3 0 23 16.1 23 23.5 0 55.3-10.7 197.2-12.2 214.5-.1 1-.9 1.7-1.9 1.7h-18.3c-1 0-1.8-.7-1.9-1.7-1.4-17.5-13.4-162.9-11.9-214.1zm24.2 283.8c-13.1 0-23.7-10.6-23.7-23.7s10.6-23.7 23.7-23.7 23.7 10.6 23.7 23.7-10.6 23.7-23.7 23.7zM722.1 644h112.6v34.4h-70.4V698h58.8v31.7h-58.8v22.6h72.4v36.2H722.1V644zm162 57.1h.6c8.3-12.9 18.2-17.8 31.3-17.8 3 0 5.1.4 6.3 1v32.6h-.8c-22.4-3.8-35.6 6.3-35.6 29.5v42.3h-38.2V685.5h36.4v15.6zm78.9 0h.6c8.3-12.9 18.2-17.8 31.3-17.8 3 0 5.1.4 6.3 1v32.6h-.8c-22.4-3.8-35.6 6.3-35.6 29.5v42.3h-38.2V685.5H963v15.6zm39.5 36.2c0-31.3 22.2-54.8 56.6-54.8 34.4 0 56.2 23.5 56.2 54.8s-21.8 54.6-56.2 54.6c-34.4-.1-56.6-23.3-56.6-54.6zm74 0c0-17.4-6.1-29.1-17.8-29.1-11.7 0-17.4 11.7-17.4 29.1 0 17.4 5.7 29.1 17.4 29.1s17.8-11.8 17.8-29.1zm83.1-36.2h.6c8.3-12.9 18.2-17.8 31.3-17.8 3 0 5.1.4 6.3 1v32.6h-.8c-22.4-3.8-35.6 6.3-35.6 29.5v42.3h-38.2V685.5h36.4v15.6z"/><path fill="none" d="M718.9 807.7h645v285.4h-645z"/><text fill="#3a6d8b" style="text-align:center;position:absolute;left:100%;font-size:47px;font-family:system-ui,-apple-system,BlinkMacSystemFont,'.SFNSText-Regular',sans-serif" x="50%" y="848.017" text-anchor="middle">${this._errorMessage}</text></svg></div>` : ''}</figure><slot name="controls"></slot></div>`;
400
- this.shadow.adoptedStyleSheets = [
401
- DotLottiePlayer.styles
402
- ];
403
- this.shadow.appendChild(this.template.content.cloneNode(true));
404
- }
405
-
406
- /**
407
- * Render Controls
408
- */ function renderControls() {
409
- const slot = this.shadow.querySelector('slot[name=controls]');
410
- if (!slot) {
411
- return;
741
+ /**
742
+ * Whether to play on mouseover.
743
+ */ set hover(value) {
744
+ this.setAttribute('hover', value.toString());
412
745
  }
413
- if (!this.controls) {
414
- slot.innerHTML = '';
415
- return;
746
+ get hover() {
747
+ const val = this.getAttribute('hover');
748
+ return Boolean(val === 'true' || val === '' || val === '1');
416
749
  }
417
- slot.innerHTML = /* HTML */ `<div class="lottie-controls toolbar ${this.playerState === PlayerState.Error ? 'has-error' : ''}" aria-label="Lottie Animation controls"><button class="togglePlay" data-active="false" aria-label="Toggle Play/Pause"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M8.016 5.016L18.985 12 8.016 18.984V5.015z"/></svg></button> <button class="stop" data-active="true" aria-label="Stop"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M6 6h12v12H6V6z"/></svg></button> <button class="prev" aria-label="Previous animation" hidden><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.9 18.2 8.1 12l9.8-6.2v12.4zm-10.3 0H6.1V5.8h1.5v12.4z"/></svg></button> <button class="next" aria-label="Next animation" hidden><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m6.1 5.8 9.8 6.2-9.8 6.2V5.8zM16.4 5.8h1.5v12.4h-1.5z"/></svg></button><form class="progress-container${this.simple ? ' simple' : ''}"><input class="seeker" type="range" min="0" max="100" step="1" value="${this._seeker.toString()}" aria-valuemin="0" aria-valuemax="100" role="slider" aria-valuenow="${this._seeker.toString()}" tabindex="0" aria-label="Slider for search"><progress max="100" value="${this._seeker}"></progress></form>${this.simple ? '' : /* HTML */ `<button class="toggleLoop" data-active="${this.loop}" tabindex="0" aria-label="Toggle loop"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg></button> <button class="toggleBoomerang" data-active="${this.mode === PlayMode.Bounce}" aria-label="Toggle boomerang" tabindex="0"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="m11.8 13.2-.3.3c-.5.5-1.1 1.1-1.7 1.5-.5.4-1 .6-1.5.8-.5.2-1.1.3-1.6.3s-1-.1-1.5-.3c-.6-.2-1-.5-1.4-1-.5-.6-.8-1.2-.9-1.9-.2-.9-.1-1.8.3-2.6.3-.7.8-1.2 1.3-1.6.3-.2.6-.4 1-.5.2-.2.5-.2.8-.3.3 0 .7-.1 1 0 .3 0 .6.1.9.2.9.3 1.7.9 2.4 1.5.4.4.8.7 1.1 1.1l.1.1.4-.4c.6-.6 1.2-1.2 1.9-1.6.5-.3 1-.6 1.5-.7.4-.1.7-.2 1-.2h.9c1 .1 1.9.5 2.6 1.4.4.5.7 1.1.8 1.8.2.9.1 1.7-.2 2.5-.4.9-1 1.5-1.8 2-.4.2-.7.4-1.1.4-.4.1-.8.1-1.2.1-.5 0-.9-.1-1.3-.3-.8-.3-1.5-.9-2.1-1.5-.4-.4-.8-.7-1.1-1.1h-.3zm-1.1-1.1c-.1-.1-.1-.1 0 0-.3-.3-.6-.6-.8-.9-.5-.5-1-.9-1.6-1.2-.4-.3-.8-.4-1.3-.4-.4 0-.8 0-1.1.2-.5.2-.9.6-1.1 1-.2.3-.3.7-.3 1.1 0 .3 0 .6.1.9.1.5.4.9.8 1.2.5.4 1.1.5 1.7.5.5 0 1-.2 1.5-.5.6-.4 1.1-.8 1.6-1.3.1-.3.3-.5.5-.6zM13 12c.5.5 1 1 1.5 1.4.5.5 1.1.9 1.9 1 .4.1.8 0 1.2-.1.3-.1.6-.3.9-.5.4-.4.7-.9.8-1.4.1-.5 0-.9-.1-1.4-.3-.8-.8-1.2-1.7-1.4-.4-.1-.8-.1-1.2 0-.5.1-1 .4-1.4.7-.5.4-1 .8-1.4 1.2-.2.2-.4.3-.5.5z"/></svg></button> <button class="toggleSettings" aria-label="Settings" aria-haspopup="true" aria-expanded="${!!this._isSettingsOpen}" aria-controls="${this._identifier}-settings"><svg width="24" height="24" aria-hidden="true" focusable="false"><circle cx="12" cy="5.4" r="2.5"/><circle cx="12" cy="12" r="2.5"/><circle cx="12" cy="18.6" r="2.5"/></svg></button><div id="${this._identifier}-settings" class="popover" hidden><button class="convert" aria-label="Convert JSON animation to dotLottie format" hidden><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M17.016 17.016v-4.031h1.969v6h-12v3l-3.984-3.984 3.984-3.984v3h10.031zM6.984 6.984v4.031H5.015v-6h12v-3l3.984 3.984-3.984 3.984v-3H6.984z"/></svg> Convert to dotLottie</button> <button class="snapshot" aria-label="Download still image"><svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M16.8 10.8 12 15.6l-4.8-4.8h3V3.6h3.6v7.2h3zM12 15.6H3v4.8h18v-4.8h-9zm7.8 2.4h-2.4v-1.2h2.4V18z"/></svg> Download still image</button></div>`}</div>`;
418
- const togglePlay = this.shadow.querySelector('.togglePlay');
419
- if (togglePlay instanceof HTMLButtonElement) {
420
- togglePlay.onclick = this.togglePlay;
750
+ /**
751
+ * Pause between loop intrations, in miliseconds.
752
+ */ set intermission(value) {
753
+ this.setAttribute('intermission', value.toString());
421
754
  }
422
- const stop = this.shadow.querySelector('.stop');
423
- if (stop instanceof HTMLButtonElement) {
424
- stop.onclick = this.stop;
755
+ get intermission() {
756
+ const val = Number(this.getAttribute('intermission'));
757
+ if (!isNaN(val)) {
758
+ return val;
759
+ }
760
+ return 0;
425
761
  }
426
- const prev = this.shadow.querySelector('.prev');
427
- if (prev instanceof HTMLButtonElement) {
428
- prev.onclick = this.prev;
762
+ /**
763
+ * Loop animation.
764
+ */ set loop(value) {
765
+ this.setAttribute('loop', Boolean(value).toString());
429
766
  }
430
- const next = this.shadow.querySelector('.next');
431
- if (next instanceof HTMLButtonElement) {
432
- next.onclick = this.next;
767
+ get loop() {
768
+ const val = this.getAttribute('loop');
769
+ return Boolean(val === 'true' || val === '' || val === '1');
433
770
  }
434
- const seeker = this.shadow.querySelector('.seeker');
435
- if (seeker instanceof HTMLInputElement) {
436
- seeker.onchange = this._handleSeekChange;
437
- seeker.onmousedown = this._freeze;
771
+ /**
772
+ * Play mode.
773
+ */ set mode(value) {
774
+ this.setAttribute('mode', value.toString());
438
775
  }
439
- if (!this.simple) {
440
- const toggleLoop = this.shadow.querySelector('.toggleLoop');
441
- if (toggleLoop instanceof HTMLButtonElement) {
442
- toggleLoop.onclick = this.toggleLoop;
443
- }
444
- const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
445
- if (toggleBoomerang instanceof HTMLButtonElement) {
446
- toggleBoomerang.onclick = this.toggleBoomerang;
447
- }
448
- const convert = this.shadow.querySelector('.convert');
449
- if (convert instanceof HTMLButtonElement) {
450
- convert.onclick = this.convert;
451
- }
452
- const snapshot = this.shadow.querySelector('.snapshot');
453
- if (snapshot instanceof HTMLButtonElement) {
454
- snapshot.onclick = ()=>this.snapshot(true);
776
+ get mode() {
777
+ const val = this.getAttribute('mode');
778
+ if (val === PlayMode.Bounce) {
779
+ return val;
455
780
  }
456
- const toggleSettings = this.shadow.querySelector('.toggleSettings');
457
- if (toggleSettings instanceof HTMLButtonElement) {
458
- toggleSettings.onclick = this._handleSettingsClick;
459
- toggleSettings.onblur = this._handleBlur;
781
+ return PlayMode.Normal;
782
+ }
783
+ /**
784
+ * Resizing to container.
785
+ */ set objectfit(value) {
786
+ this.setAttribute('objectfit', value);
787
+ }
788
+ get objectfit() {
789
+ const val = this.getAttribute('objectfit');
790
+ if (val && Object.values(ObjectFit).includes(val)) {
791
+ return val;
460
792
  }
793
+ return ObjectFit.Contain;
461
794
  }
462
- }
463
-
464
- /**
465
- * Credit to:
466
- * @author Leonardo Favre <https://github.com/leofavre/observed-properties>
467
- * @description Enhanced HTML element with reactive state handlers
468
- */ /* eslint-disable @typescript-eslint/ban-ts-comment */ const UPDATE_ON_CONNECTED = Symbol('UPDATE_ON_CONNECTED');
469
- if (isServer()) {
470
- // Mock HTMLElement for server-side rendering
471
- // @ts-ignore
472
- global.HTMLElement = class EmptyHTMLElement {
473
- };
474
- }
475
- /**
476
- * HTMLElement enhanced to track property changes
477
- */ class EnhancedElement extends HTMLElement {
478
- constructor(){
479
- super();
480
- // @ts-ignore
481
- const { observedProperties = [] } = this.constructor;
482
- if (UPDATE_ON_CONNECTED in this) {
483
- this[UPDATE_ON_CONNECTED] = [];
484
- }
485
- if ('propertyChangedCallback' in this && typeof this.propertyChangedCallback === 'function') {
486
- for (const propName of observedProperties){
487
- const initialValue = this[propName], CACHED_VALUE = Symbol(propName);
488
- // @ts-ignore
489
- this[CACHED_VALUE] = initialValue;
490
- Object.defineProperty(this, propName, {
491
- get () {
492
- return this[CACHED_VALUE];
493
- },
494
- set (value) {
495
- const oldValue = this[CACHED_VALUE];
496
- this[CACHED_VALUE] = value;
497
- this.propertyChangedCallback(propName, oldValue, value);
498
- }
499
- });
500
- if (typeof initialValue !== 'undefined') {
501
- if (UPDATE_ON_CONNECTED in this && Array.isArray(this[UPDATE_ON_CONNECTED])) {
502
- this[UPDATE_ON_CONNECTED].push(propName);
503
- }
504
- }
505
- }
795
+ /**
796
+ * Resizing to container (Deprecated).
797
+ */ set preserveAspectRatio(value) {
798
+ this.setAttribute('preserveAspectRatio', value || PreserveAspectRatio.Contain);
799
+ }
800
+ get preserveAspectRatio() {
801
+ const val = this.getAttribute('preserveAspectRatio');
802
+ if (val && Object.values(PreserveAspectRatio).includes(val)) {
803
+ return val;
506
804
  }
805
+ return null;
507
806
  }
508
- connectedCallback() {
509
- let arr = [];
510
- if (UPDATE_ON_CONNECTED in this && Array.isArray(this[UPDATE_ON_CONNECTED])) {
511
- arr = this[UPDATE_ON_CONNECTED];
807
+ /**
808
+ * Renderer to use: svg, canvas or html.
809
+ */ set renderer(value) {
810
+ this.setAttribute('renderer', value);
811
+ }
812
+ get renderer() {
813
+ const val = this.getAttribute('renderer');
814
+ if (val === RendererType.Canvas || val === RendererType.HTML) {
815
+ return val;
512
816
  }
513
- for (const propName of arr){
514
- if (!('propertyChangedCallback' in this) || typeof this.propertyChangedCallback !== 'function') {
515
- continue;
516
- }
517
- if (propName in this) {
518
- this.propertyChangedCallback(propName, undefined, this[propName]);
519
- }
817
+ return RendererType.SVG;
818
+ }
819
+ /**
820
+ * Hide advanced controls.
821
+ */ set simple(value) {
822
+ this.setAttribute('simple', value.toString());
823
+ }
824
+ get simple() {
825
+ const val = this.getAttribute('simple');
826
+ return Boolean(val === 'true' || val === '' || val === '1');
827
+ }
828
+ /**
829
+ * Speed.
830
+ */ set speed(value) {
831
+ this.setAttribute('speed', value.toString());
832
+ }
833
+ get speed() {
834
+ const val = this.getAttribute('speed');
835
+ if (val !== null && !isNaN(Number(val))) {
836
+ return Number(val);
520
837
  }
838
+ return 1;
839
+ }
840
+ /**
841
+ * Source, either path or JSON string.
842
+ */ set src(value) {
843
+ this.setAttribute('src', value || '');
844
+ }
845
+ get src() {
846
+ return this.getAttribute('src');
847
+ }
848
+ /**
849
+ * Subframe.
850
+ */ set subframe(value) {
851
+ this.setAttribute('subframe', Boolean(value).toString());
852
+ }
853
+ get subframe() {
854
+ const val = this.getAttribute('subframe');
855
+ return Boolean(val === 'true' || val === '' || val === '1');
521
856
  }
522
- }
523
-
524
- var name="@aarsteinmedia/dotlottie-player";var version="5.0.0";var description="Web Component for playing Lottie animations in your web app. Previously @johanaarstein/dotlottie-player";var exports={".":{"import":"./dist/index.js",node:"./dist/index.js",types:"./dist/index.d.ts"}};var main="./dist/index.js";var unpkg="./dist/unpkg/index.js";var module="./dist/index.js";var types="./dist/index.d.ts";var type="module";var homepage="https://www.aarstein.media/en/dotlottie-player";var repository={url:"git+https://github.com/aarsteinmedia/dotlottie-player.git",type:"git"};var bugs="https://github.com/aarsteinmedia/dotlottie-player/issues";var author={name:"Johan Martin Aarstein",email:"johan@aarstein.media",url:"https://www.aarstein.media",organization:"Aarstein Media"};var contributors=[{name:"Anthony Colpron",email:"anthonycolpron@gmail.com",url:"https://github.com/anthony-colpron"}];var license="GPL-2.0-or-later";var scripts={test:"wtr",prebuild:"rimraf ./dist",build:"rollup -c","prebuild:types":"rimraf ./types","build:types":"tsc -p ./tsconfig.prod.json && tsc-alias","build:cem":"npx cem analyze --config cem.config.js",prod:"pnpm build:types && pnpm build && pnpm build:cem",dev:"rollup -c -w --environment NODE_ENV:development","lint:js":"eslint","lint:js:fix":"eslint --fix","lint:css":"npx stylelint **/*.css","lint:css:fix":"npx stylelint **/*.css --fix","lint:pkg":"npmPkgJsonLint .","lint:pkg:fix":"npmPkgJsonLint . --fix"};var dependencies={fflate:"^0.8.2","lottie-web":"^5.12.2"};var peerDependencies={"@types/react":">= 16.0.0"};var devDependencies={"@custom-elements-manifest/analyzer":"^0.10.4","@eslint/compat":"^1.2.7","@eslint/js":"^9.21.0","@esm-bundle/chai":"4.3.4-fix.0","@open-wc/testing":"^4.0.0","@rollup/plugin-commonjs":"^28.0.2","@rollup/plugin-json":"^6.1.0","@rollup/plugin-node-resolve":"^16.0.0","@rollup/plugin-typescript":"^12.1.2","@swc/core":"^1.11.1","@types/mocha":"^10.0.10","@types/node":"^22.13.5","@types/path-browserify":"^1.0.3","@types/react":"^19.0.10","@web/dev-server-esbuild":"^1.0.4","@web/dev-server-import-maps":"^0.2.1","@web/dev-server-rollup":"^0.6.4","@web/test-runner":"^0.20.0","@web/test-runner-playwright":"^0.11.0",autoprefixer:"^10.4.20",esbuild:"^0.25.0",eslint:"^9.21.0","eslint-config-prettier":"^10.0.1","eslint-import-resolver-typescript":"^3.8.3","eslint-plugin-import":"^2.31.0","eslint-plugin-jsdoc":"^50.6.3","eslint-plugin-perfectionist":"^4.9.0","eslint-plugin-prettier":"^5.2.3",globals:"^16.0.0","npm-package-json-lint":"^8.0.0","npm-package-json-lint-config-default":"^7.0.1","postcss-flexbugs-fixes":"^5.0.2",prettier:"^3.5.2",react:"^19.0.0",rimraf:"^6.0.1",rollup:"^4.34.8","rollup-plugin-dts":"^6.1.1","rollup-plugin-html-literals":"^1.1.8","rollup-plugin-livereload":"^2.0.5","rollup-plugin-postcss":"^4.0.2","rollup-plugin-serve":"^3.0.0","rollup-plugin-summary":"^3.0.0","rollup-plugin-swc3":"^0.12.1","rollup-plugin-typescript-paths":"^1.5.0",stylelint:"^16.14.1","stylelint-config-recommended":"^15.0.0","tsc-alias":"^1.8.10",tslib:"^2.8.1",typescript:"^5.7.3","typescript-eslint":"^8.25.0"};var pnpm={onlyBuiltDependencies:["@parcel/watcher","@swc/core","esbuild"]};var browserslist={production:[">0.3%","not dead","not op_mini all"],development:["last 1 chrome version","last 1 firefox version","last 1 safari version"]};var customElements$1="custom-elements.json";var files=["CHANGELOG.md","custom-elements.json","dist","README.md"];var keywords=["lottie","dotlottie","animation","web component","svg","vector","player"];var publishConfig={access:"public"};var engines={node:">= 12.17.0"};var funding={type:"paypal",url:"https://www.paypal.com/donate/?hosted_button_id=E7C7DMN8KSQ6A"};var pkg = {name:name,version:version,description:description,exports:exports,main:main,unpkg:unpkg,module:module,types:types,type:type,homepage:homepage,repository:repository,bugs:bugs,author:author,contributors:contributors,license:license,scripts:scripts,dependencies:dependencies,peerDependencies:peerDependencies,devDependencies:devDependencies,pnpm:pnpm,browserslist:browserslist,customElements:customElements$1,files:files,keywords:keywords,publishConfig:publishConfig,engines:engines,funding:funding};
525
-
526
- var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-toolbar-height: 35px;\n --lottie-player-toolbar-background-color: #fff;\n --lottie-player-toolbar-icon-color: #000;\n --lottie-player-toolbar-icon-hover-color: #000;\n --lottie-player-toolbar-icon-active-color: #4285f4;\n --lottie-player-seeker-track-color: rgb(0 0 0 / 20%);\n --lottie-player-seeker-thumb-color: #4285f4;\n --lottie-player-seeker-display: block;\n\n width: 100%;\n height: 100%;\n\n &:not([hidden]) {\n display: block;\n }\n\n .main {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n margin: 0;\n padding: 0;\n }\n\n .animation {\n width: 100%;\n height: 100%;\n display: flex;\n margin: 0;\n padding: 0;\n }\n\n [data-controls='true'] .animation {\n height: calc(100% - 35px);\n }\n\n .animation-container {\n position: relative;\n }\n\n .popover {\n position: absolute;\n right: 5px;\n bottom: 40px;\n background-color: var(--lottie-player-toolbar-background-color);\n border-radius: 5px;\n padding: 10px 15px;\n border: solid 2px var(--lottie-player-toolbar-icon-color);\n animation: fade-in 0.2s ease-in-out;\n\n &::before {\n content: '';\n right: 10px;\n border: 7px solid transparent;\n margin-right: -7px;\n height: 0;\n width: 0;\n position: absolute;\n pointer-events: none;\n top: 100%;\n border-top-color: var(--lottie-player-toolbar-icon-color);\n }\n }\n\n .error {\n display: flex;\n margin: auto;\n justify-content: center;\n height: 100%;\n align-items: center;\n\n & svg {\n width: 100%;\n height: auto;\n }\n }\n\n .toolbar {\n display: flex;\n place-items: center center;\n background: var(--lottie-player-toolbar-background-color);\n margin: 0;\n height: 35px;\n padding: 5px;\n border-radius: 5px;\n gap: 5px;\n\n &.has-error {\n pointer-events: none;\n opacity: 0.5;\n }\n\n & button {\n cursor: pointer;\n fill: var(--lottie-player-toolbar-icon-color);\n color: var(--lottie-player-toolbar-icon-color);\n background: none;\n border: 0;\n padding: 0;\n outline: 0;\n height: 100%;\n margin: 0;\n align-items: center;\n gap: 5px;\n opacity: 0.9;\n\n &:not([hidden]) {\n display: flex;\n }\n\n &:hover {\n opacity: 1;\n }\n\n &[data-active='true'] {\n opacity: 1;\n fill: var(--lottie-player-toolbar-icon-active-color);\n }\n\n &:disabled {\n opacity: 0.5;\n }\n\n &:focus {\n outline: 0;\n }\n\n & svg {\n pointer-events: none;\n\n & > * {\n fill: inherit;\n }\n }\n\n &.disabled svg {\n display: none;\n }\n }\n }\n\n .progress-container {\n position: relative;\n width: 100%;\n\n &.simple {\n margin-right: 12px;\n }\n }\n\n .seeker {\n appearance: none;\n outline: none;\n width: 100%;\n height: 20px;\n border-radius: 3px;\n border: 0;\n cursor: pointer;\n background-color: transparent;\n\n display: var(--lottie-player-seeker-display);\n color: var(--lottie-player-seeker-thumb-color);\n margin: 0;\n padding: 7.5px 0;\n position: relative;\n z-index: 1;\n\n &::-webkit-slider-runnable-track,\n &::-webkit-slider-thumb {\n appearance: none;\n outline: none;\n }\n\n &::-webkit-slider-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n border: 0;\n background-color: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -webkit-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-webkit-slider-thumb,\n &:focus::-webkit-slider-thumb {\n transform: scale(1);\n }\n\n &::-moz-range-progress {\n background-color: var(--lottie-player-seeker-thumb-color);\n height: 5px;\n border-radius: 3px;\n }\n\n &::-moz-range-thumb {\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background-color: var(--lottie-player-seeker-thumb-color);\n border: 0;\n cursor: pointer;\n -moz-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-moz-range-thumb,\n &:focus::-moz-range-thumb {\n transform: scale(1);\n }\n\n &::-ms-track {\n width: 100%;\n height: 5px;\n cursor: pointer;\n background: transparent;\n border-color: transparent;\n color: transparent;\n }\n\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n border-radius: 3px;\n }\n\n &::-ms-fill-lower {\n background-color: var(--lottie-player-seeker-thumb-color);\n border-radius: 3px;\n }\n\n &::-ms-thumb {\n border: 0;\n height: 15px;\n width: 15px;\n border-radius: 50%;\n background: var(--lottie-player-seeker-thumb-color);\n cursor: pointer;\n -ms-transition: transform 0.2s ease-in-out;\n transition: transform 0.2s ease-in-out;\n transform: scale(0);\n }\n\n &:hover::-ms-thumb {\n transform: scale(1);\n }\n\n &:focus {\n &::-ms-thumb {\n transform: scale(1);\n }\n\n &::-ms-fill-lower,\n &::-ms-fill-upper {\n background: var(--lottie-player-seeker-track-color);\n }\n }\n }\n\n & progress {\n appearance: none;\n outline: none;\n position: absolute;\n width: 100%;\n height: 5px;\n border-radius: 3px;\n border: 0;\n top: 0;\n left: 0;\n margin: 7.5px 0;\n background-color: var(--lottie-player-seeker-track-color);\n pointer-events: none;\n\n &::-webkit-progress-inner-element {\n border-radius: 3px;\n overflow: hidden;\n }\n\n &::-webkit-slider-runnable-track {\n background-color: transparent;\n }\n\n &::-webkit-progress-value {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n }\n\n & *::-moz-progress-bar {\n background-color: var(--lottie-player-seeker-thumb-color);\n }\n}\n\n@keyframes fade-in {\n 0% {\n opacity: 0;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n@media (prefers-color-scheme: dark) {\n :host {\n --lottie-player-toolbar-background-color: #000;\n --lottie-player-toolbar-icon-color: #fff;\n --lottie-player-toolbar-icon-hover-color: #fff;\n --lottie-player-seeker-track-color: rgb(255 255 255 / 60%);\n }\n}\n";
527
-
528
- /**
529
- * dotLottie Player Web Component
530
- * @export
531
- * @class DotLottiePlayer
532
- * @extends { EnhancedElement }
533
- * @description Web Component for playing Lottie animations in your web app.
534
- */ class DotLottiePlayer extends EnhancedElement {
535
857
  constructor(){
536
- super(), this._renderControls = renderControls, this._render = renderPlayer, /**
537
- * Multi-animation settings
538
- */ this._multiAnimationSettings = [], /**
539
- * Animation Container
540
- */ this._container = null, /**
541
- * @state
542
- * Player state
858
+ super(), /**
859
+ * Player state.
543
860
  */ this.playerState = PlayerState.Loading, /**
544
- * @state
545
- * Whether settings toolbar is open
546
- */ this._isSettingsOpen = false, /**
547
- * @state
548
- * Seeker
549
- */ this._seeker = 0, /**
550
- * @state
551
- * Which animation to show, if several
552
- */ this._currentAnimation = 0, this._lottieInstance = null, this._identifier = this.id || useId('dotlottie'), this._errorMessage = 'Something went wrong', this._isBounce = false, this._isDotLottie = false, this._playerState = {
861
+ * Animation Container.
862
+ */ this._container = null, this._errorMessage = 'Something went wrong', this._identifier = this.id || createElementID(), /**
863
+ * Whether settings toolbar is open.
864
+ */ this._isSettingsOpen = false, this._playerState = {
553
865
  count: 0,
554
866
  loaded: false,
555
867
  prev: PlayerState.Loading,
556
868
  scrollTimeout: null,
557
869
  scrollY: 0,
558
870
  visible: false
559
- }, /**
560
- * Handle settings click event
871
+ }, this._render = renderPlayer, this._renderControls = renderControls, /**
872
+ * Seeker.
873
+ */ this._seeker = 0, /**
874
+ * This is included in watched properties,
875
+ * so that next-button will show up
876
+ * on load, if controls are visible.
877
+ */ this._animations = [], /**
878
+ * Which animation to show, if several.
879
+ */ this._currentAnimation = 0, this._isBounce = false, this._isDotLottie = false, this._lottieInstance = null, /**
880
+ * Multi-animation settings.
881
+ */ this._multiAnimationSettings = [], /**
882
+ * Handle settings click event.
561
883
  */ this._handleSettingsClick = ({ target })=>{
562
884
  this._toggleSettings();
563
885
  // Because Safari does not add focus on click, we need to add it manually, so the onblur event will fire
@@ -596,55 +918,58 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
596
918
  });
597
919
  }
598
920
  /**
599
- * Initialize everything on component first render
600
- */ async connectedCallback() {
601
- super.connectedCallback();
602
- this._render();
603
- this._container = this.shadow.querySelector('.animation');
604
- this._renderControls();
605
- // Add listener for Visibility API's change event.
606
- if (typeof document.hidden !== 'undefined') {
607
- document.addEventListener('visibilitychange', this._onVisibilityChange);
608
- }
609
- // Add intersection observer for detecting component being out-of-view.
610
- this._addIntersectionObserver();
611
- // Setup lottie player
612
- await this.load(this.src);
613
- this.dispatchEvent(new CustomEvent(PlayerEvents.Rendered));
614
- }
615
- /**
616
- * Cleanup on component destroy
617
- */ disconnectedCallback() {
618
- // Remove intersection observer for detecting component being out-of-view
619
- if (this._intersectionObserver) {
620
- this._intersectionObserver.disconnect();
621
- this._intersectionObserver = undefined;
622
- }
623
- // Destroy the animation instance
624
- if (this._lottieInstance) {
625
- this._lottieInstance.destroy();
921
+ * Creates a new dotLottie file, by combinig several animations.
922
+ * If set to false the function returns an ArrayBuffer. Defaults to true.
923
+ */ async addAnimation(configs, fileName, shouldDownload = true) {
924
+ // Initialize meta object for animation, with fallbacks for
925
+ // when the method is called indepenently
926
+ const { animations = [], manifest = {
927
+ animations: this.src ? [
928
+ {
929
+ id: this._identifier
930
+ }
931
+ ] : []
932
+ } } = this.src ? await getAnimationData(this.src) : {};
933
+ try {
934
+ if (!manifest) {
935
+ throw new Error('Manifest is not set');
936
+ }
937
+ manifest.generator = '@aarsteinmedia/dotlottie-player';
938
+ const { length } = configs;
939
+ for(let i = 0; i < length; i++){
940
+ const { url } = configs[i], { animations: animationsToAdd } = await getAnimationData(url);
941
+ if (!animationsToAdd) {
942
+ throw new Error('No animation loaded');
943
+ }
944
+ if (manifest.animations.some(({ id })=>id === configs[i].id)) {
945
+ throw new Error('Duplicate id for animation');
946
+ }
947
+ manifest.animations = [
948
+ ...manifest.animations,
949
+ {
950
+ id: configs[i].id
951
+ }
952
+ ];
953
+ animations.push(...animationsToAdd);
954
+ }
955
+ return {
956
+ result: await createDotLottie({
957
+ animations,
958
+ fileName,
959
+ manifest,
960
+ shouldDownload
961
+ }),
962
+ success: true
963
+ };
964
+ } catch (error) {
965
+ return {
966
+ error: handleErrors(error).message,
967
+ success: false
968
+ };
626
969
  }
627
- // Remove the attached Visibility API's change event listener
628
- document.removeEventListener('visibilitychange', this._onVisibilityChange);
629
- }
630
- /**
631
- * Attributes to observe
632
- */ static get observedAttributes() {
633
- return [
634
- 'animateOnScroll',
635
- 'autoplay',
636
- 'controls',
637
- 'direction',
638
- 'hover',
639
- 'loop',
640
- 'mode',
641
- 'speed',
642
- 'src',
643
- 'subframe'
644
- ];
645
970
  }
646
971
  /**
647
- * Runs when the value of an attribute is changed on the component
972
+ * Runs when the value of an attribute is changed on the component.
648
973
  */ async attributeChangedCallback(name, _oldValue, value) {
649
974
  if (!this._lottieInstance) {
650
975
  return;
@@ -675,7 +1000,8 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
675
1000
  }
676
1001
  if (name === 'direction') {
677
1002
  if (Number(value) === -1) {
678
- return this.setDirection(-1);
1003
+ this.setDirection(-1);
1004
+ return;
679
1005
  }
680
1006
  this.setDirection(1);
681
1007
  }
@@ -689,14 +1015,14 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
689
1015
  this._container.removeEventListener('mouseleave', this._mouseLeave);
690
1016
  }
691
1017
  if (name === 'loop') {
692
- const toggleLoop = this.shadow.querySelector('.toggleLoop');
1018
+ const toggleLoop = this.shadow?.querySelector('.toggleLoop');
693
1019
  if (toggleLoop instanceof HTMLButtonElement) {
694
1020
  toggleLoop.dataset.active = value;
695
1021
  }
696
1022
  this.setLoop(value === '' || Boolean(value));
697
1023
  }
698
1024
  if (name === 'mode') {
699
- const toggleBoomerang = this.shadow.querySelector('.toggleBoomerang');
1025
+ const toggleBoomerang = this.shadow?.querySelector('.toggleBoomerang');
700
1026
  if (toggleBoomerang instanceof HTMLButtonElement) {
701
1027
  toggleBoomerang.dataset.active = (value === PlayMode.Bounce).toString();
702
1028
  }
@@ -715,395 +1041,112 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
715
1041
  this.setSubframe(value === '' || Boolean(value));
716
1042
  }
717
1043
  }
718
- static get observedProperties() {
719
- return [
720
- 'playerState',
721
- '_isSettingsOpen',
722
- '_seeker',
723
- '_currentAnimation',
724
- '_animations'
725
- ];
726
- }
727
- // name: string, oldValue: string, newValue: string
728
- propertyChangedCallback(name, _oldValue, value) {
1044
+ /**
1045
+ * Initialize everything on component first render.
1046
+ */ async connectedCallback() {
1047
+ await super.connectedCallback();
1048
+ await this._render();
729
1049
  if (!this.shadow) {
730
- return;
731
- }
732
- const togglePlay = this.shadow.querySelector('.togglePlay'), stop = this.shadow.querySelector('.stop'), prev = this.shadow.querySelector('.prev'), next = this.shadow.querySelector('.next'), seeker = this.shadow.querySelector('.seeker'), progress = this.shadow.querySelector('progress'), popover = this.shadow.querySelector('.popover'), convert = this.shadow.querySelector('.convert');
733
- if (!(togglePlay instanceof HTMLButtonElement) || !(stop instanceof HTMLButtonElement) || !(next instanceof HTMLButtonElement) || !(prev instanceof HTMLButtonElement) || !(seeker instanceof HTMLInputElement) || !(progress instanceof HTMLProgressElement)) {
734
- return;
735
- }
736
- if (name === 'playerState') {
737
- togglePlay.dataset.active = (value === PlayerState.Playing || value === PlayerState.Paused).toString();
738
- stop.dataset.active = (value === PlayerState.Stopped).toString();
739
- if (value === PlayerState.Playing) {
740
- togglePlay.innerHTML = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M14.016 5.016H18v13.969h-3.984V5.016zM6 18.984V5.015h3.984v13.969H6z"/></svg>`;
741
- } else {
742
- togglePlay.innerHTML = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M8.016 5.016L18.985 12 8.016 18.984V5.015z"/></svg>`;
743
- }
1050
+ throw new Error('Missing Shadow element');
744
1051
  }
745
- if (name === '_seeker' && typeof value === 'number') {
746
- seeker.value = value.toString();
747
- seeker.ariaValueNow = value.toString();
748
- progress.value = value;
1052
+ this._container = this.shadow.querySelector('.animation');
1053
+ this._renderControls();
1054
+ // Add listener for Visibility API's change event.
1055
+ if (typeof document.hidden !== 'undefined') {
1056
+ document.addEventListener('visibilitychange', this._onVisibilityChange);
749
1057
  }
750
- if (name === '_animations' && Array.isArray(value)) {
751
- if (this._currentAnimation + 1 < value.length) {
752
- next.hidden = false;
753
- }
1058
+ // Add intersection observer for detecting component being out-of-view.
1059
+ this._addIntersectionObserver();
1060
+ // Setup lottie player
1061
+ await this.load(this.src);
1062
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Rendered));
1063
+ }
1064
+ async convert({ animations: animationsFromProps, fileName, manifest, shouldDownload = true, src: srcFromProps, typeCheck }) {
1065
+ const src = srcFromProps || this.src || this.source;
1066
+ if (!src) {
1067
+ throw new Error('No animation to convert');
754
1068
  }
755
- if (name === '_currentAnimation' && typeof value === 'number') {
756
- if (value + 1 >= this._animations.length) {
757
- next.hidden = true;
758
- } else {
759
- next.hidden = false;
760
- }
761
- if (value) {
762
- prev.hidden = false;
763
- } else {
764
- prev.hidden = true;
765
- }
1069
+ if (typeCheck || this._isDotLottie) {
1070
+ const animationData = await getAnimationData(src);
1071
+ createJSON({
1072
+ animation: animationData.animations?.[0],
1073
+ fileName: `${getFilename(fileName || src || 'converted')}.json`,
1074
+ shouldDownload
1075
+ });
1076
+ return;
766
1077
  }
767
- if (name === '_isSettingsOpen' && typeof value === 'boolean' && popover instanceof HTMLDivElement && convert instanceof HTMLButtonElement) {
768
- popover.hidden = !value;
769
- convert.hidden = this._isDotLottie;
1078
+ let animations = animationsFromProps;
1079
+ if (!animations) {
1080
+ const animationData = await getAnimationData(src);
1081
+ animations = animationData.animations;
770
1082
  }
1083
+ return createDotLottie({
1084
+ animations,
1085
+ fileName: `${getFilename(fileName || src || 'converted')}.lottie`,
1086
+ manifest: {
1087
+ ...manifest ?? this._manifest,
1088
+ generator: '[[GENERATOR]]'
1089
+ },
1090
+ shouldDownload
1091
+ });
771
1092
  }
772
1093
  /**
773
- * Whether to trigger next frame with scroll
774
- */ set animateOnScroll(value) {
775
- this.setAttribute('animateOnScroll', (!!value).toString());
776
- }
777
- get animateOnScroll() {
778
- const val = this.getAttribute('animateOnScroll');
779
- if (val === 'true' || val === '' || val === '1') {
780
- return true;
1094
+ * Destroy animation and element.
1095
+ */ destroy() {
1096
+ if (!this._lottieInstance) {
1097
+ return;
781
1098
  }
782
- return false;
783
- }
784
- /**
785
- * Autoplay
786
- */ set autoplay(value) {
787
- this.setAttribute('autoplay', (!!value).toString());
788
- }
789
- get autoplay() {
790
- const val = this.getAttribute('autoplay');
791
- if (val === 'true' || val === '' || val === '1') {
792
- return true;
793
- }
794
- return false;
795
- }
796
- /**
797
- * Background color
798
- */ set background(value) {
799
- this.setAttribute('background', value);
800
- }
801
- get background() {
802
- return this.getAttribute('background') || 'transparent';
803
- }
804
- /**
805
- * Show controls
806
- */ set controls(value) {
807
- this.setAttribute('controls', (!!value).toString());
808
- }
809
- get controls() {
810
- const val = this.getAttribute('controls');
811
- if (val === 'true' || val === '' || val === '1') {
812
- return true;
813
- }
814
- return false;
815
- }
816
- /**
817
- * Number of times to loop
818
- */ set count(value) {
819
- this.setAttribute('count', value.toString());
820
- }
821
- get count() {
822
- const val = this.getAttribute('count');
823
- if (val) {
824
- return Number(val);
825
- }
826
- return 0;
827
- }
828
- /**
829
- * Description for screen readers
830
- */ set description(value) {
831
- if (value) {
832
- this.setAttribute('description', value);
833
- }
834
- }
835
- get description() {
836
- return this.getAttribute('description');
837
- }
838
- /**
839
- * Direction of animation
840
- */ set direction(value) {
841
- this.setAttribute('direction', value.toString());
842
- }
843
- get direction() {
844
- const val = Number(this.getAttribute('direction'));
845
- if (val === -1) {
846
- return val;
847
- }
848
- return 1;
849
- }
850
- /**
851
- * Whether to play on mouseover
852
- */ set hover(value) {
853
- this.setAttribute('hover', value.toString());
854
- }
855
- get hover() {
856
- const val = this.getAttribute('hover');
857
- if (val === 'true' || val === '' || val === '1') {
858
- return true;
859
- }
860
- return false;
861
- }
862
- /**
863
- * Pause between loop intrations, in miliseconds
864
- */ set intermission(value) {
865
- this.setAttribute('intermission', value.toString());
866
- }
867
- get intermission() {
868
- const val = Number(this.getAttribute('intermission'));
869
- if (!isNaN(val)) {
870
- return val;
871
- }
872
- return 0;
873
- }
874
- /**
875
- * Loop animation
876
- */ set loop(value) {
877
- this.setAttribute('loop', (!!value).toString());
878
- }
879
- get loop() {
880
- const val = this.getAttribute('loop');
881
- if (val === 'true' || val === '' || val === '1') {
882
- return true;
883
- }
884
- return false;
885
- }
886
- /**
887
- * Play mode
888
- */ set mode(value) {
889
- this.setAttribute('mode', value.toString());
890
- }
891
- get mode() {
892
- const val = this.getAttribute('mode');
893
- if (val === PlayMode.Bounce) {
894
- return val;
895
- }
896
- return PlayMode.Normal;
897
- }
898
- /**
899
- * Resizing to container
900
- */ set objectfit(value) {
901
- this.setAttribute('objectfit', value);
902
- }
903
- get objectfit() {
904
- const val = this.getAttribute('objectfit');
905
- if (val && Object.values(ObjectFit).includes(val)) {
906
- return val;
907
- }
908
- return ObjectFit.Contain;
909
- }
910
- /**
911
- * Resizing to container (Deprecated)
912
- */ set preserveAspectRatio(value) {
913
- this.setAttribute('preserveAspectRatio', value || PreserveAspectRatio.Contain);
914
- }
915
- get preserveAspectRatio() {
916
- const val = this.getAttribute('preserveAspectRatio');
917
- if (val && Object.values(PreserveAspectRatio).includes(val)) {
918
- return val;
919
- }
920
- return null;
921
- }
922
- /**
923
- * Renderer to use: svg, canvas or html
924
- */ set renderer(value) {
925
- this.setAttribute('renderer', value);
926
- }
927
- get renderer() {
928
- const val = this.getAttribute('renderer');
929
- if (val === 'canvas' || val === 'html') {
930
- return val;
931
- }
932
- return 'svg';
1099
+ this.playerState = PlayerState.Destroyed;
1100
+ this._lottieInstance.destroy();
1101
+ this._lottieInstance = null;
1102
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Destroyed));
1103
+ this.remove();
1104
+ document.removeEventListener('visibilitychange', this._onVisibilityChange);
933
1105
  }
934
1106
  /**
935
- * Hide advanced controls
936
- */ set simple(value) {
937
- this.setAttribute('simple', value.toString());
938
- }
939
- get simple() {
940
- const val = this.getAttribute('simple');
941
- if (val === 'true' || val === '' || val === '1') {
942
- return true;
1107
+ * Cleanup on component destroy.
1108
+ */ disconnectedCallback() {
1109
+ // Remove intersection observer for detecting component being out-of-view
1110
+ if (this._intersectionObserver) {
1111
+ this._intersectionObserver.disconnect();
1112
+ this._intersectionObserver = undefined;
943
1113
  }
944
- return false;
945
- }
946
- /**
947
- * Speed
948
- */ set speed(value) {
949
- this.setAttribute('speed', value?.toString());
950
- }
951
- get speed() {
952
- const val = this.getAttribute('speed');
953
- if (val !== null && !isNaN(Number(val))) {
954
- return Number(val);
1114
+ // Destroy the animation instance
1115
+ if (this._lottieInstance) {
1116
+ this._lottieInstance.destroy();
955
1117
  }
956
- return 1;
1118
+ // Remove the attached Visibility API's change event listener
1119
+ document.removeEventListener('visibilitychange', this._onVisibilityChange);
957
1120
  }
958
1121
  /**
959
- * Source, either path or JSON string
960
- */ set src(value) {
961
- this.setAttribute('src', value || '');
962
- }
963
- get src() {
964
- return this.getAttribute('src');
1122
+ * Returns the lottie-web instance used in the component.
1123
+ */ getLottie() {
1124
+ return this._lottieInstance;
965
1125
  }
966
1126
  /**
967
- * Subframe
968
- */ set subframe(value) {
969
- this.setAttribute('subframe', (!!value).toString());
970
- }
971
- get subframe() {
972
- const val = this.getAttribute('subframe');
973
- if (val === 'true' || val === '' || val === '1') {
974
- return true;
975
- }
976
- return false;
1127
+ * Get Lottie Manifest.
1128
+ */ getManifest() {
1129
+ return this._manifest;
977
1130
  }
978
1131
  /**
979
- * Get Multi-animation settings
980
- * @returns { AnimationSettings[] }
1132
+ * Get Multi-animation settings.
1133
+ *
981
1134
  */ getMultiAnimationSettings() {
982
1135
  return this._multiAnimationSettings;
983
1136
  }
984
1137
  /**
985
- * Set Multi-animation settings
986
- * @param { AnimationSettings[] } settings
987
- */ setMultiAnimationSettings(settings) {
988
- this._multiAnimationSettings = settings;
989
- }
990
- /**
991
- * Set playback segment
992
- * @param { AnimationSegment } settings
993
- */ setSegment(segment) {
994
- this._segment = segment;
995
- }
996
- /**
997
- * Get playback segment
998
- * @returns { AnimationSegment }
1138
+ * Get playback segment.
1139
+ *
999
1140
  */ getSegment() {
1000
1141
  return this._segment;
1001
1142
  }
1002
1143
  /**
1003
- * Get options from props
1004
- * @returns { AnimationConfig }
1005
- */ _getOptions() {
1006
- if (!this._container) {
1007
- throw new Error('Container not rendered');
1008
- }
1009
- const preserveAspectRatio = this.preserveAspectRatio ?? (this.objectfit && aspectRatio(this.objectfit)), currentAnimationSettings = this._multiAnimationSettings?.length ? this._multiAnimationSettings?.[this._currentAnimation] : undefined, currentAnimationManifest = this._manifest.animations?.[this._currentAnimation];
1010
- // Loop
1011
- let loop = !!this.loop;
1012
- if (currentAnimationManifest.loop !== undefined && this.loop === undefined) {
1013
- loop = !!currentAnimationManifest.loop;
1014
- }
1015
- if (currentAnimationSettings?.loop !== undefined) {
1016
- loop = !!currentAnimationSettings.loop;
1017
- }
1018
- // Autoplay
1019
- let autoplay = !!this.autoplay;
1020
- if (currentAnimationManifest.autoplay !== undefined && this.autoplay === undefined) {
1021
- autoplay = !!currentAnimationManifest.autoplay;
1022
- }
1023
- if (currentAnimationSettings?.autoplay !== undefined) {
1024
- autoplay = !!currentAnimationSettings.autoplay;
1025
- }
1026
- if (this.animateOnScroll) {
1027
- autoplay = false;
1028
- }
1029
- // Segment
1030
- let initialSegment = this._segment;
1031
- if (this._segment?.every((val)=>val > 0)) {
1032
- initialSegment = [
1033
- this._segment[0] - 1,
1034
- this._segment[1] - 1
1035
- ];
1036
- }
1037
- if (this._segment?.some((val)=>val < 0)) {
1038
- initialSegment = undefined;
1039
- }
1040
- const options = {
1041
- autoplay,
1042
- container: this._container,
1043
- initialSegment,
1044
- loop,
1045
- renderer: this.renderer,
1046
- rendererSettings: {
1047
- imagePreserveAspectRatio: preserveAspectRatio
1048
- }
1049
- };
1050
- switch(this.renderer){
1051
- case 'svg':
1052
- options.rendererSettings = {
1053
- ...options.rendererSettings,
1054
- hideOnTransparent: true,
1055
- preserveAspectRatio,
1056
- progressiveLoad: true
1057
- };
1058
- break;
1059
- case 'canvas':
1060
- options.rendererSettings = {
1061
- ...options.rendererSettings,
1062
- clearCanvas: true,
1063
- preserveAspectRatio,
1064
- progressiveLoad: true
1065
- };
1066
- break;
1067
- case 'html':
1068
- options.rendererSettings = {
1069
- ...options.rendererSettings,
1070
- hideOnTransparent: true
1071
- };
1072
- }
1073
- return options;
1074
- }
1075
- /**
1076
- * Add IntersectionObserver
1077
- */ _addIntersectionObserver() {
1078
- if (!this._container || this._intersectionObserver || !('IntersectionObserver' in window)) {
1079
- return;
1080
- }
1081
- this._intersectionObserver = new IntersectionObserver((entries)=>{
1082
- for (const entry of entries){
1083
- if (!entry.isIntersecting || document.hidden) {
1084
- if (this.playerState === PlayerState.Playing) {
1085
- this._freeze();
1086
- }
1087
- this._playerState.visible = false;
1088
- continue;
1089
- }
1090
- if (!this.animateOnScroll && this.playerState === PlayerState.Frozen) {
1091
- this.play();
1092
- }
1093
- if (!this._playerState.scrollY) {
1094
- this._playerState.scrollY = scrollY;
1095
- }
1096
- this._playerState.visible = true;
1097
- }
1098
- });
1099
- this._intersectionObserver.observe(this._container);
1100
- }
1101
- /**
1102
- * Initialize Lottie Web player
1144
+ * Initialize Lottie Web player.
1103
1145
  */ async load(src) {
1104
1146
  if (!this.shadowRoot || !src) {
1105
1147
  return;
1106
1148
  }
1149
+ this.source = src;
1107
1150
  // Load the resource
1108
1151
  try {
1109
1152
  const { animations, isDotLottie, manifest } = await getAnimationData(src);
@@ -1111,19 +1154,17 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
1111
1154
  throw new Error('Broken or corrupted file');
1112
1155
  }
1113
1156
  this._isBounce = this.mode === PlayMode.Bounce;
1114
- if (this._multiAnimationSettings?.length) {
1115
- if (this._multiAnimationSettings[this._currentAnimation]?.mode) {
1116
- this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
1117
- }
1157
+ if (this._multiAnimationSettings.length > 0 && this._multiAnimationSettings[this._currentAnimation]?.mode) {
1158
+ this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
1118
1159
  }
1119
- this._isDotLottie = !!isDotLottie;
1160
+ this._isDotLottie = Boolean(isDotLottie);
1120
1161
  this._animations = animations;
1121
1162
  this._manifest = manifest ?? {
1122
1163
  animations: [
1123
1164
  {
1124
1165
  autoplay: !this.animateOnScroll && this.autoplay,
1125
1166
  direction: this.direction,
1126
- id: useId(),
1167
+ id: createElementID(),
1127
1168
  loop: this.loop,
1128
1169
  mode: this.mode,
1129
1170
  speed: this.speed
@@ -1135,26 +1176,27 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
1135
1176
  this._lottieInstance.destroy();
1136
1177
  }
1137
1178
  this.playerState = PlayerState.Stopped;
1138
- if (!this.animateOnScroll && (this.autoplay || this._multiAnimationSettings?.[this._currentAnimation]?.autoplay)) {
1179
+ if (!this.animateOnScroll && (this.autoplay || this._multiAnimationSettings[this._currentAnimation]?.autoplay)) {
1139
1180
  this.playerState = PlayerState.Playing;
1140
1181
  }
1141
1182
  // Initialize lottie player and load animation
1142
- this._lottieInstance = Lottie.default.loadAnimation({
1183
+ // @ts-expect-error TODO:
1184
+ this._lottieInstance = Lottie.loadAnimation({
1143
1185
  ...this._getOptions(),
1144
1186
  animationData: animations[this._currentAnimation]
1145
1187
  });
1146
- } catch (err) {
1147
- this._errorMessage = handleErrors(err).message;
1188
+ } catch (error) {
1189
+ this._errorMessage = handleErrors(error).message;
1148
1190
  this.playerState = PlayerState.Error;
1149
1191
  this.dispatchEvent(new CustomEvent(PlayerEvents.Error));
1150
1192
  return;
1151
1193
  }
1152
1194
  this._addEventListeners();
1153
- const speed = this._multiAnimationSettings?.[this._currentAnimation]?.speed ?? this.speed ?? this._manifest.animations[this._currentAnimation].speed, direction = this._multiAnimationSettings?.[this._currentAnimation]?.direction ?? this.direction ?? this._manifest.animations[this._currentAnimation].direction ?? 1;
1195
+ const speed = this._multiAnimationSettings[this._currentAnimation]?.speed ?? this.speed, direction = this._multiAnimationSettings[this._currentAnimation]?.direction ?? this.direction;
1154
1196
  // Set initial playback speed and direction
1155
1197
  this._lottieInstance.setSpeed(speed);
1156
1198
  this._lottieInstance.setDirection(direction);
1157
- this._lottieInstance.setSubframe(!!this.subframe);
1199
+ this._lottieInstance.setSubframe(Boolean(this.subframe));
1158
1200
  // Start playing if autoplay is enabled
1159
1201
  if (this.autoplay || this.animateOnScroll) {
1160
1202
  if (this.direction === -1) {
@@ -1170,313 +1212,215 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
1170
1212
  }
1171
1213
  }
1172
1214
  /**
1173
- * Get Lottie Manifest
1174
- */ getManifest() {
1175
- return this._manifest;
1215
+ * Skip to next animation.
1216
+ */ next() {
1217
+ this._currentAnimation++;
1218
+ this._switchInstance();
1176
1219
  }
1177
1220
  /**
1178
- * Toggle event listeners
1179
- */ _toggleEventListeners(action) {
1180
- const method = action === 'add' ? 'addEventListener' : 'removeEventListener';
1181
- if (this._lottieInstance) {
1182
- this._lottieInstance[method]('enterFrame', this._enterFrame);
1183
- this._lottieInstance[method]('complete', this._complete);
1184
- this._lottieInstance[method]('loopComplete', this._loopComplete);
1185
- this._lottieInstance[method]('DOMLoaded', this._DOMLoaded);
1186
- this._lottieInstance[method]('data_ready', this._dataReady);
1187
- this._lottieInstance[method]('data_failed', this._dataFailed);
1188
- }
1189
- if (this._container && this.hover) {
1190
- this._container[method]('mouseenter', this._mouseEnter);
1191
- this._container[method]('mouseleave', this._mouseLeave);
1221
+ * Pause.
1222
+ */ pause() {
1223
+ if (!this._lottieInstance) {
1224
+ return;
1192
1225
  }
1193
- window[method]('focus', this._handleWindowBlur, {
1194
- capture: false,
1195
- passive: true
1196
- });
1197
- window[method]('blur', this._handleWindowBlur, {
1198
- capture: false,
1199
- passive: true
1200
- });
1201
- if (this.animateOnScroll) {
1202
- window[method]('scroll', this._handleScroll, {
1203
- capture: true,
1204
- passive: true
1205
- });
1226
+ this._playerState.prev = this.playerState;
1227
+ try {
1228
+ this._lottieInstance.pause();
1229
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Pause));
1230
+ } finally{
1231
+ this.playerState = PlayerState.Paused;
1206
1232
  }
1207
1233
  }
1208
1234
  /**
1209
- * Add event listeners
1210
- */ _addEventListeners() {
1211
- this._toggleEventListeners('add');
1212
- }
1213
- /**
1214
- * Remove event listeners
1215
- */ _removeEventListeners() {
1216
- this._toggleEventListeners('remove');
1217
- }
1218
- _loopComplete() {
1235
+ * Play.
1236
+ */ play() {
1219
1237
  if (!this._lottieInstance) {
1220
1238
  return;
1221
1239
  }
1222
- const { playDirection, // firstFrame,
1223
- totalFrames } = this._lottieInstance, inPoint = this._segment ? this._segment[0] : 0, outPoint = this._segment ? this._segment[0] : totalFrames;
1224
- if (this.count) {
1225
- if (this._isBounce) {
1226
- this._playerState.count += 0.5;
1227
- } else {
1228
- this._playerState.count += 1;
1229
- }
1230
- if (this._playerState.count >= this.count) {
1231
- this.setLoop(false);
1232
- this.playerState = PlayerState.Completed;
1233
- this.dispatchEvent(new CustomEvent(PlayerEvents.Complete));
1234
- return;
1235
- }
1236
- }
1237
- this.dispatchEvent(new CustomEvent(PlayerEvents.Loop));
1238
- if (this._isBounce) {
1239
- this._lottieInstance.goToAndStop(playDirection === -1 ? inPoint : outPoint * 0.99, true);
1240
- this._lottieInstance.setDirection(playDirection * -1);
1241
- return setTimeout(()=>{
1242
- if (!this.animateOnScroll) {
1243
- this._lottieInstance?.play();
1244
- }
1245
- }, this.intermission);
1240
+ this._playerState.prev = this.playerState;
1241
+ try {
1242
+ this._lottieInstance.play();
1243
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Play));
1244
+ } finally{
1245
+ this.playerState = PlayerState.Playing;
1246
1246
  }
1247
- this._lottieInstance.goToAndStop(playDirection === -1 ? outPoint * 0.99 : inPoint, true);
1248
- return setTimeout(()=>{
1249
- if (!this.animateOnScroll) {
1250
- this._lottieInstance?.play();
1251
- }
1252
- }, this.intermission);
1253
1247
  }
1254
- _enterFrame() {
1255
- if (!this._lottieInstance) {
1248
+ /**
1249
+ * Skip to previous animation.
1250
+ */ prev() {
1251
+ this._currentAnimation--;
1252
+ this._switchInstance(true);
1253
+ }
1254
+ /**
1255
+ * Name: string, oldValue: string, newValue: string.
1256
+ */ propertyChangedCallback(name, _oldValue, value) {
1257
+ if (!this.shadow) {
1256
1258
  return;
1257
1259
  }
1258
- const { currentFrame, totalFrames } = this._lottieInstance;
1259
- this._seeker = Math.round(currentFrame / totalFrames * 100);
1260
- this.dispatchEvent(new CustomEvent(PlayerEvents.Frame, {
1261
- detail: {
1262
- frame: currentFrame,
1263
- seeker: this._seeker
1264
- }
1265
- }));
1266
- }
1267
- _complete() {
1268
- if (!this._lottieInstance) {
1260
+ const togglePlay = this.shadow.querySelector('.togglePlay'), stop = this.shadow.querySelector('.stop'), prev = this.shadow.querySelector('.prev'), next = this.shadow.querySelector('.next'), seeker = this.shadow.querySelector('.seeker'), progress = this.shadow.querySelector('progress'), popover = this.shadow.querySelector('.popover'), convert = this.shadow.querySelector('.convert');
1261
+ if (!(togglePlay instanceof HTMLButtonElement) || !(stop instanceof HTMLButtonElement) || !(next instanceof HTMLButtonElement) || !(prev instanceof HTMLButtonElement) || !(seeker instanceof HTMLInputElement) || !(progress instanceof HTMLProgressElement)) {
1269
1262
  return;
1270
1263
  }
1271
- if (this._animations.length > 1) {
1272
- if (this._multiAnimationSettings?.[this._currentAnimation + 1]?.autoplay) {
1273
- return this.next();
1274
- }
1275
- if (this.loop && this._currentAnimation === this._animations.length - 1) {
1276
- this._currentAnimation = 0;
1277
- return this._switchInstance();
1264
+ if (name === 'playerState') {
1265
+ togglePlay.dataset.active = (value === PlayerState.Playing || value === PlayerState.Paused).toString();
1266
+ stop.dataset.active = (value === PlayerState.Stopped).toString();
1267
+ if (value === PlayerState.Playing) {
1268
+ togglePlay.innerHTML = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M14.016 5.016H18v13.969h-3.984V5.016zM6 18.984V5.015h3.984v13.969H6z"/></svg>`;
1269
+ } else {
1270
+ togglePlay.innerHTML = /* HTML */ `<svg width="24" height="24" aria-hidden="true" focusable="false"><path d="M8.016 5.016L18.985 12 8.016 18.984V5.015z"/></svg>`;
1278
1271
  }
1279
1272
  }
1280
- const { currentFrame, totalFrames } = this._lottieInstance;
1281
- this._seeker = Math.round(currentFrame / totalFrames * 100);
1282
- this.playerState = PlayerState.Completed;
1283
- this.dispatchEvent(new CustomEvent(PlayerEvents.Complete, {
1284
- detail: {
1285
- frame: currentFrame,
1286
- seeker: this._seeker
1287
- }
1288
- }));
1289
- }
1290
- _DOMLoaded() {
1291
- this._playerState.loaded = true;
1292
- this.dispatchEvent(new CustomEvent(PlayerEvents.Ready));
1293
- }
1294
- _dataReady() {
1295
- this.dispatchEvent(new CustomEvent(PlayerEvents.Load));
1296
- }
1297
- _dataFailed() {
1298
- this.playerState = PlayerState.Error;
1299
- this.dispatchEvent(new CustomEvent(PlayerEvents.Error));
1300
- }
1301
- _handleWindowBlur({ type }) {
1302
- if (this.playerState === PlayerState.Playing && type === 'blur') {
1303
- this._freeze();
1273
+ if (name === '_seeker' && typeof value === 'number') {
1274
+ seeker.value = value.toString();
1275
+ seeker.ariaValueNow = value.toString();
1276
+ progress.value = value;
1304
1277
  }
1305
- if (this.playerState === PlayerState.Frozen && type === 'focus') {
1306
- this.play();
1278
+ if (name === '_animations' && Array.isArray(value) && this._currentAnimation + 1 < value.length) {
1279
+ next.hidden = false;
1307
1280
  }
1308
- }
1309
- /**
1310
- * Handle MouseEnter
1311
- */ _mouseEnter() {
1312
- if (this.hover && this.playerState !== PlayerState.Playing) {
1313
- this.play();
1281
+ if (name === '_currentAnimation' && typeof value === 'number') {
1282
+ if (value + 1 >= this._animations.length) {
1283
+ next.hidden = true;
1284
+ } else {
1285
+ next.hidden = false;
1286
+ }
1287
+ if (value) {
1288
+ prev.hidden = false;
1289
+ } else {
1290
+ prev.hidden = true;
1291
+ }
1314
1292
  }
1315
- }
1316
- /**
1317
- * Handle MouseLeave
1318
- */ _mouseLeave() {
1319
- if (this.hover && this.playerState === PlayerState.Playing) {
1320
- this.stop();
1293
+ if (name === '_isSettingsOpen' && typeof value === 'boolean' && popover instanceof HTMLDivElement && convert instanceof HTMLButtonElement) {
1294
+ popover.hidden = !value;
1295
+ convert.hidden = this._isDotLottie;
1321
1296
  }
1322
1297
  }
1323
1298
  /**
1324
- * Handle visibility change events
1325
- */ _onVisibilityChange() {
1326
- if (document.hidden && this.playerState === PlayerState.Playing) {
1327
- this._freeze();
1299
+ * Reload animation.
1300
+ */ async reload() {
1301
+ if (!this._lottieInstance || !this.src) {
1328
1302
  return;
1329
1303
  }
1330
- if (this.playerState === PlayerState.Frozen) {
1331
- this.play();
1332
- }
1304
+ this._lottieInstance.destroy();
1305
+ await this.load(this.src);
1333
1306
  }
1334
1307
  /**
1335
- * Handle scroll
1336
- */ _handleScroll() {
1337
- if (!this.animateOnScroll || !this._lottieInstance) {
1308
+ * Seek to a given frame.
1309
+ *
1310
+ * @param value - Frame to seek to.
1311
+ */ seek(value) {
1312
+ if (!this._lottieInstance) {
1338
1313
  return;
1339
1314
  }
1340
- if (isServer()) {
1341
- console.warn('DotLottie: Scroll animations might not work properly in a Server Side Rendering context. Try to wrap this in a client component.');
1315
+ // Extract frame number from either number or percentage value
1316
+ const matches = value.toString().match(/^(\d+)(%?)$/);
1317
+ if (!matches) {
1342
1318
  return;
1343
1319
  }
1344
- if (this._playerState.visible) {
1345
- if (this._playerState.scrollTimeout) {
1346
- clearTimeout(this._playerState.scrollTimeout);
1347
- }
1348
- this._playerState.scrollTimeout = setTimeout(()=>{
1349
- this.playerState = PlayerState.Paused;
1350
- }, 400);
1351
- const adjustedScroll = scrollY > this._playerState.scrollY ? scrollY - this._playerState.scrollY : this._playerState.scrollY - scrollY, clampedScroll = Math.min(Math.max(adjustedScroll / 3, 1), this._lottieInstance.totalFrames * 3), roundedScroll = clampedScroll / 3;
1352
- requestAnimationFrame(()=>{
1353
- if (roundedScroll < (this._lottieInstance?.totalFrames ?? 0)) {
1354
- this.playerState = PlayerState.Playing;
1355
- this._lottieInstance?.goToAndStop(roundedScroll, true);
1356
- } else {
1357
- this.playerState = PlayerState.Paused;
1358
- }
1359
- });
1320
+ // Calculate and set the frame number
1321
+ const frame = Math.round(matches[2] === '%' ? this._lottieInstance.totalFrames * Number(matches[1]) / 100 : Number(matches[1]));
1322
+ // Set seeker to new frame number
1323
+ this._seeker = frame;
1324
+ // Send lottie player to the new frame
1325
+ if (this.playerState === PlayerState.Playing || this.playerState === PlayerState.Frozen && this._playerState.prev === PlayerState.Playing) {
1326
+ this._lottieInstance.goToAndPlay(frame, true);
1327
+ this.playerState = PlayerState.Playing;
1328
+ return;
1360
1329
  }
1330
+ this._lottieInstance.goToAndStop(frame, true);
1331
+ this._lottieInstance.pause();
1361
1332
  }
1362
1333
  /**
1363
- * Handles click and drag actions on the progress track
1364
- * @param { Event & { HTMLInputElement } } event
1365
- */ _handleSeekChange({ target }) {
1366
- if (!(target instanceof HTMLInputElement) || !this._lottieInstance || isNaN(Number(target.value))) {
1334
+ * Dynamically set count for loops.
1335
+ */ setCount(value) {
1336
+ this.count = value;
1337
+ }
1338
+ /**
1339
+ * Animation play direction.
1340
+ *
1341
+ * @param value - Animation direction.
1342
+ */ setDirection(value) {
1343
+ if (!this._lottieInstance) {
1367
1344
  return;
1368
1345
  }
1369
- this.seek(Math.round(Number(target.value) / 100 * this._lottieInstance.totalFrames));
1370
- }
1371
- _isLottie(json) {
1372
- const mandatory = [
1373
- 'v',
1374
- 'ip',
1375
- 'op',
1376
- 'layers',
1377
- 'fr',
1378
- 'w',
1379
- 'h'
1380
- ];
1381
- return mandatory.every((field)=>Object.prototype.hasOwnProperty.call(json, field));
1346
+ this._lottieInstance.setDirection(value);
1382
1347
  }
1383
1348
  /**
1384
- * Creates a new dotLottie file, by combinig several animations
1385
- * @param { [ AnimationConfig ] } configs
1386
- * @param { string } fileName
1387
- * @param { boolean } shouldDownload Whether to trigger a download in the browser.
1388
- * If set to false the function returns an ArrayBuffer. Defaults to true.
1349
+ * Set loop.
1389
1350
  *
1390
- */ async addAnimation(configs, fileName, shouldDownload = true) {
1391
- // Initialize meta object for animation, with fallbacks for
1392
- // when the method is called indepenently
1393
- const { animations = [], manifest = {
1394
- animations: this.src ? [
1395
- {
1396
- id: this._identifier
1397
- }
1398
- ] : []
1399
- } } = this.src ? await getAnimationData(this.src) : {};
1400
- try {
1401
- manifest.generator = pkg.name;
1402
- for (const config of configs){
1403
- const { url } = config, { animations: animationsToAdd } = await getAnimationData(url);
1404
- if (!animationsToAdd) {
1405
- throw new Error('No animation loaded');
1406
- }
1407
- if (manifest.animations.some(({ id })=>id === config.id)) {
1408
- throw new Error('Duplicate id for animation');
1409
- }
1410
- manifest.animations = [
1411
- ...manifest.animations,
1412
- {
1413
- id: config.id
1414
- }
1415
- ];
1416
- animations?.push(...animationsToAdd);
1417
- }
1418
- return {
1419
- result: await createDotLottie({
1420
- animations,
1421
- fileName,
1422
- manifest,
1423
- shouldDownload
1424
- }),
1425
- success: true
1426
- };
1427
- } catch (err) {
1428
- return {
1429
- error: handleErrors(err).message,
1430
- success: false
1431
- };
1351
+ */ setLoop(value) {
1352
+ if (!this._lottieInstance) {
1353
+ return;
1432
1354
  }
1355
+ this._lottieInstance.setLoop(value);
1433
1356
  }
1434
1357
  /**
1435
- * Returns the lottie-web instance used in the component
1436
- */ getLottie() {
1437
- return this._lottieInstance;
1358
+ * Set Multi-animation settings.
1359
+ *
1360
+ */ setMultiAnimationSettings(settings) {
1361
+ this._multiAnimationSettings = settings;
1438
1362
  }
1439
1363
  /**
1440
- * Play
1441
- */ async play() {
1364
+ * Set playback segment.
1365
+ *
1366
+ */ setSegment(segment) {
1367
+ this._segment = segment;
1368
+ }
1369
+ /**
1370
+ * Set animation playback speed.
1371
+ *
1372
+ * @param value - Playback speed.
1373
+ */ setSpeed(value = 1) {
1442
1374
  if (!this._lottieInstance) {
1443
1375
  return;
1444
1376
  }
1445
- if (this.playerState) {
1446
- this._playerState.prev = this.playerState;
1447
- }
1448
- try {
1449
- this._lottieInstance.play();
1450
- this.dispatchEvent(new CustomEvent(PlayerEvents.Play));
1451
- } finally{
1452
- this.playerState = PlayerState.Playing;
1453
- }
1377
+ this._lottieInstance.setSpeed(value);
1454
1378
  }
1455
1379
  /**
1456
- * Pause
1457
- */ pause() {
1380
+ * Toggles subframe, for more smooth animations.
1381
+ *
1382
+ * @param value - Whether animation uses subframe.
1383
+ */ setSubframe(value) {
1458
1384
  if (!this._lottieInstance) {
1459
1385
  return;
1460
1386
  }
1461
- if (this.playerState) {
1462
- this._playerState.prev = this.playerState;
1463
- }
1387
+ this._lottieInstance.setSubframe(value);
1388
+ }
1389
+ /**
1390
+ * Snapshot and download the current frame as SVG.
1391
+ */ snapshot(shouldDownload = true, name = 'AM Lottie') {
1464
1392
  try {
1465
- this._lottieInstance.pause();
1466
- this.dispatchEvent(new CustomEvent(PlayerEvents.Pause));
1467
- } finally{
1468
- this.playerState = PlayerState.Paused;
1393
+ if (!this.shadowRoot) {
1394
+ throw new Error('Unknown error');
1395
+ }
1396
+ // Get SVG element and serialize markup
1397
+ const svgElement = this.shadowRoot.querySelector('.animation svg');
1398
+ if (!svgElement) {
1399
+ throw new Error('Could not retrieve animation from DOM');
1400
+ }
1401
+ const data = svgElement instanceof Node ? new XMLSerializer().serializeToString(svgElement) : null;
1402
+ if (!data) {
1403
+ throw new Error('Could not serialize SVG element');
1404
+ }
1405
+ if (shouldDownload) {
1406
+ download(data, {
1407
+ mimeType: 'image/svg+xml',
1408
+ name: `${getFilename(this.src || name)}-${frameOutput(this._seeker)}.svg`
1409
+ });
1410
+ }
1411
+ return data;
1412
+ } catch (error) {
1413
+ console.error(error);
1414
+ return null;
1469
1415
  }
1470
1416
  }
1471
1417
  /**
1472
- * Stop
1418
+ * Stop.
1473
1419
  */ stop() {
1474
1420
  if (!this._lottieInstance) {
1475
1421
  return;
1476
1422
  }
1477
- if (this.playerState) {
1478
- this._playerState.prev = this.playerState;
1479
- }
1423
+ this._playerState.prev = this.playerState;
1480
1424
  this._playerState.count = 0;
1481
1425
  try {
1482
1426
  this._lottieInstance.stop();
@@ -1486,204 +1430,367 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
1486
1430
  }
1487
1431
  }
1488
1432
  /**
1489
- * Destroy animation and element
1490
- */ destroy() {
1491
- if (!this._lottieInstance) {
1433
+ * Toggle Boomerang.
1434
+ */ toggleBoomerang() {
1435
+ const curr = this._multiAnimationSettings[this._currentAnimation];
1436
+ if (curr.mode !== undefined) {
1437
+ if (curr.mode === PlayMode.Normal) {
1438
+ curr.mode = PlayMode.Bounce;
1439
+ this._isBounce = true;
1440
+ return;
1441
+ }
1442
+ curr.mode = PlayMode.Normal;
1443
+ this._isBounce = false;
1492
1444
  return;
1493
1445
  }
1494
- this.playerState = PlayerState.Destroyed;
1495
- this._lottieInstance.destroy();
1496
- this._lottieInstance = null;
1497
- this.dispatchEvent(new CustomEvent(PlayerEvents.Destroyed));
1498
- this.remove();
1499
- document.removeEventListener('visibilitychange', this._onVisibilityChange);
1446
+ if (this.mode === PlayMode.Normal) {
1447
+ this.mode = PlayMode.Bounce;
1448
+ this._isBounce = true;
1449
+ return;
1450
+ }
1451
+ this.mode = PlayMode.Normal;
1452
+ this._isBounce = false;
1500
1453
  }
1501
1454
  /**
1502
- * Seek to a given frame
1503
- * @param { number | string } value Frame to seek to
1504
- */ seek(value) {
1455
+ * Toggle loop.
1456
+ */ toggleLoop() {
1457
+ const hasLoop = !this.loop;
1458
+ this.loop = hasLoop;
1459
+ this.setLoop(hasLoop);
1460
+ }
1461
+ /**
1462
+ * Toggle playing state.
1463
+ */ togglePlay() {
1505
1464
  if (!this._lottieInstance) {
1506
1465
  return;
1507
1466
  }
1508
- // Extract frame number from either number or percentage value
1509
- const matches = value.toString().match(/^([0-9]+)(%?)$/);
1510
- if (!matches) {
1467
+ const { currentFrame, playDirection, totalFrames } = this._lottieInstance;
1468
+ if (this.playerState === PlayerState.Playing) {
1469
+ this.pause();
1511
1470
  return;
1512
1471
  }
1513
- // Calculate and set the frame number
1514
- const frame = Math.round(matches[2] === '%' ? this._lottieInstance.totalFrames * Number(matches[1]) / 100 : Number(matches[1]));
1515
- // Set seeker to new frame number
1516
- this._seeker = frame;
1517
- // Send lottie player to the new frame
1518
- if (this.playerState === PlayerState.Playing || this.playerState === PlayerState.Frozen && this._playerState.prev === PlayerState.Playing) {
1519
- this._lottieInstance.goToAndPlay(frame, true);
1520
- this.playerState = PlayerState.Playing;
1472
+ if (this.playerState !== PlayerState.Completed) {
1473
+ this.play();
1521
1474
  return;
1522
1475
  }
1523
- this._lottieInstance.goToAndStop(frame, true);
1524
- this._lottieInstance.pause();
1476
+ this.playerState = PlayerState.Playing;
1477
+ if (this._isBounce) {
1478
+ this.setDirection(playDirection * -1);
1479
+ this._lottieInstance.goToAndPlay(currentFrame, true);
1480
+ return;
1481
+ }
1482
+ if (playDirection === -1) {
1483
+ this._lottieInstance.goToAndPlay(totalFrames, true);
1484
+ return;
1485
+ }
1486
+ this._lottieInstance.goToAndPlay(0, true);
1525
1487
  }
1526
1488
  /**
1527
- * Snapshot and download the current frame as SVG
1528
- */ snapshot(shouldDownload = true, name = 'AM Lottie') {
1489
+ * Freeze animation.
1490
+ * This internal state pauses animation and is used to differentiate between
1491
+ * user requested pauses and component instigated pauses.
1492
+ */ _freeze() {
1493
+ if (!this._lottieInstance) {
1494
+ return;
1495
+ }
1496
+ this._playerState.prev = this.playerState;
1529
1497
  try {
1530
- if (!this.shadowRoot) {
1531
- throw new Error('Unknown error');
1532
- }
1533
- // Get SVG element and serialize markup
1534
- const svgElement = this.shadowRoot.querySelector('.animation svg');
1535
- if (!svgElement) {
1536
- throw new Error('Could not retrieve animation from DOM');
1498
+ this._lottieInstance.pause();
1499
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Freeze));
1500
+ } finally{
1501
+ this.playerState = PlayerState.Frozen;
1502
+ }
1503
+ }
1504
+ /**
1505
+ * Handle blur.
1506
+ */ _handleBlur() {
1507
+ setTimeout(()=>{
1508
+ this._toggleSettings(false);
1509
+ }, 200);
1510
+ }
1511
+ /**
1512
+ * Handles click and drag actions on the progress track.
1513
+ *
1514
+ */ _handleSeekChange({ target }) {
1515
+ if (!(target instanceof HTMLInputElement) || !this._lottieInstance || isNaN(Number(target.value))) {
1516
+ return;
1517
+ }
1518
+ this.seek(Math.round(Number(target.value) / 100 * this._lottieInstance.totalFrames));
1519
+ }
1520
+ /**
1521
+ * Add event listeners.
1522
+ */ _addEventListeners() {
1523
+ this._toggleEventListeners('add');
1524
+ }
1525
+ /**
1526
+ * Add IntersectionObserver.
1527
+ */ _addIntersectionObserver() {
1528
+ if (!this._container || this._intersectionObserver || !('IntersectionObserver' in window)) {
1529
+ return;
1530
+ }
1531
+ this._intersectionObserver = new IntersectionObserver((entries)=>{
1532
+ const { length } = entries;
1533
+ for(let i = 0; i < length; i++){
1534
+ if (!entries[i].isIntersecting || document.hidden) {
1535
+ if (this.playerState === PlayerState.Playing) {
1536
+ this._freeze();
1537
+ }
1538
+ this._playerState.visible = false;
1539
+ continue;
1540
+ }
1541
+ if (!this.animateOnScroll && this.playerState === PlayerState.Frozen) {
1542
+ this.play();
1543
+ }
1544
+ if (!this._playerState.scrollY) {
1545
+ this._playerState.scrollY = scrollY;
1546
+ }
1547
+ this._playerState.visible = true;
1537
1548
  }
1538
- const data = svgElement instanceof Node ? new XMLSerializer().serializeToString(svgElement) : null;
1539
- if (!data) {
1540
- throw new Error('Could not serialize SVG element');
1549
+ });
1550
+ this._intersectionObserver.observe(this._container);
1551
+ }
1552
+ _complete() {
1553
+ if (!this._lottieInstance) {
1554
+ return;
1555
+ }
1556
+ if (this._animations.length > 1) {
1557
+ if (this._multiAnimationSettings[this._currentAnimation + 1]?.autoplay) {
1558
+ this.next();
1559
+ return;
1541
1560
  }
1542
- if (shouldDownload) {
1543
- download(data, {
1544
- mimeType: 'image/svg+xml',
1545
- name: `${getFilename(this.src || name)}-${frameOutput(this._seeker)}.svg`
1546
- });
1561
+ if (this.loop && this._currentAnimation === this._animations.length - 1) {
1562
+ this._currentAnimation = 0;
1563
+ this._switchInstance();
1564
+ return;
1547
1565
  }
1548
- return data;
1549
- } catch (err) {
1550
- console.error(err);
1551
- return null;
1552
1566
  }
1567
+ const { currentFrame, totalFrames } = this._lottieInstance;
1568
+ this._seeker = Math.round(currentFrame / totalFrames * 100);
1569
+ this.playerState = PlayerState.Completed;
1570
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Complete, {
1571
+ detail: {
1572
+ frame: currentFrame,
1573
+ seeker: this._seeker
1574
+ }
1575
+ }));
1576
+ }
1577
+ _dataFailed() {
1578
+ this.playerState = PlayerState.Error;
1579
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Error));
1553
1580
  }
1554
- /**
1555
- * Toggles subframe, for more smooth animations
1556
- * @param { boolean } value Whether animation uses subframe
1557
- */ setSubframe(value) {
1581
+ _dataReady() {
1582
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Load));
1583
+ }
1584
+ _DOMLoaded() {
1585
+ this._playerState.loaded = true;
1586
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Ready));
1587
+ }
1588
+ _enterFrame() {
1558
1589
  if (!this._lottieInstance) {
1559
1590
  return;
1560
1591
  }
1561
- this._lottieInstance.setSubframe(value);
1562
- }
1563
- /**
1564
- * Dynamically set count for loops
1565
- */ setCount(value) {
1566
- this.count = value;
1592
+ const { currentFrame, totalFrames } = this._lottieInstance;
1593
+ this._seeker = Math.round(currentFrame / totalFrames * 100);
1594
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Frame, {
1595
+ detail: {
1596
+ frame: currentFrame,
1597
+ seeker: this._seeker
1598
+ }
1599
+ }));
1567
1600
  }
1568
1601
  /**
1569
- * Freeze animation.
1570
- * This internal state pauses animation and is used to differentiate between
1571
- * user requested pauses and component instigated pauses.
1572
- */ _freeze() {
1573
- if (!this._lottieInstance) {
1574
- return;
1602
+ * Get options from props.
1603
+ *
1604
+ */ _getOptions() {
1605
+ if (!this._container) {
1606
+ throw new Error('Container not rendered');
1575
1607
  }
1576
- if (this.playerState) {
1577
- this._playerState.prev = this.playerState;
1608
+ const preserveAspectRatio = this.preserveAspectRatio ?? aspectRatio(this.objectfit), currentAnimationSettings = this._multiAnimationSettings.length > 0 ? this._multiAnimationSettings[this._currentAnimation] : undefined, currentAnimationManifest = this._manifest?.animations[this._currentAnimation];
1609
+ // Loop
1610
+ let hasLoop = Boolean(this.loop);
1611
+ if (currentAnimationManifest?.loop !== undefined) {
1612
+ hasLoop = Boolean(currentAnimationManifest.loop);
1578
1613
  }
1579
- try {
1580
- this._lottieInstance.pause();
1581
- this.dispatchEvent(new CustomEvent(PlayerEvents.Freeze));
1582
- } finally{
1583
- this.playerState = PlayerState.Frozen;
1614
+ if (currentAnimationSettings?.loop !== undefined) {
1615
+ hasLoop = Boolean(currentAnimationSettings.loop);
1584
1616
  }
1585
- }
1586
- /**
1587
- * Reload animation
1588
- */ async reload() {
1589
- if (!this._lottieInstance || !this.src) {
1590
- return;
1617
+ // Autoplay
1618
+ let hasAutoplay = Boolean(this.autoplay);
1619
+ if (currentAnimationManifest?.autoplay !== undefined) {
1620
+ hasAutoplay = Boolean(currentAnimationManifest.autoplay);
1591
1621
  }
1592
- this._lottieInstance.destroy();
1593
- await this.load(this.src);
1622
+ if (currentAnimationSettings?.autoplay !== undefined) {
1623
+ hasAutoplay = Boolean(currentAnimationSettings.autoplay);
1624
+ }
1625
+ if (this.animateOnScroll) {
1626
+ hasAutoplay = false;
1627
+ }
1628
+ // Segment
1629
+ let initialSegment = this._segment;
1630
+ if (this._segment?.every((val)=>val > 0)) {
1631
+ initialSegment = [
1632
+ this._segment[0] - 1,
1633
+ this._segment[1] - 1
1634
+ ];
1635
+ }
1636
+ if (this._segment?.some((val)=>val < 0)) {
1637
+ initialSegment = undefined;
1638
+ }
1639
+ const options = {
1640
+ autoplay: hasAutoplay,
1641
+ container: this._container,
1642
+ initialSegment,
1643
+ loop: hasLoop,
1644
+ renderer: this.renderer,
1645
+ rendererSettings: {
1646
+ imagePreserveAspectRatio: preserveAspectRatio
1647
+ }
1648
+ };
1649
+ switch(this.renderer){
1650
+ case RendererType.SVG:
1651
+ {
1652
+ options.rendererSettings = {
1653
+ ...options.rendererSettings,
1654
+ hideOnTransparent: true,
1655
+ preserveAspectRatio,
1656
+ progressiveLoad: true
1657
+ };
1658
+ break;
1659
+ }
1660
+ case RendererType.Canvas:
1661
+ {
1662
+ options.rendererSettings = {
1663
+ ...options.rendererSettings,
1664
+ // @ts-expect-error TODO:
1665
+ clearCanvas: true,
1666
+ preserveAspectRatio,
1667
+ progressiveLoad: true
1668
+ };
1669
+ break;
1670
+ }
1671
+ case RendererType.HTML:
1672
+ {
1673
+ options.rendererSettings = {
1674
+ ...options.rendererSettings,
1675
+ hideOnTransparent: true
1676
+ };
1677
+ }
1678
+ }
1679
+ return options;
1594
1680
  }
1595
1681
  /**
1596
- * Set animation playback speed
1597
- * @param { number } value Playback speed
1598
- */ setSpeed(value = 1) {
1599
- if (!this._lottieInstance) {
1682
+ * Handle scroll.
1683
+ */ _handleScroll() {
1684
+ if (!this.animateOnScroll || !this._lottieInstance) {
1600
1685
  return;
1601
1686
  }
1602
- this._lottieInstance.setSpeed(value);
1603
- }
1604
- /**
1605
- * Animation play direction
1606
- * @param { AnimationDirection } value Animation direction
1607
- */ setDirection(value) {
1608
- if (!this._lottieInstance) {
1687
+ if (isServer()) {
1688
+ console.warn('DotLottie: Scroll animations might not work properly in a Server Side Rendering context. Try to wrap this in a client component.');
1609
1689
  return;
1610
1690
  }
1611
- this._lottieInstance.setDirection(value);
1691
+ if (this._playerState.visible) {
1692
+ if (this._playerState.scrollTimeout) {
1693
+ clearTimeout(this._playerState.scrollTimeout);
1694
+ }
1695
+ this._playerState.scrollTimeout = setTimeout(()=>{
1696
+ this.playerState = PlayerState.Paused;
1697
+ }, 400);
1698
+ const adjustedScroll = scrollY > this._playerState.scrollY ? scrollY - this._playerState.scrollY : this._playerState.scrollY - scrollY, clampedScroll = Math.min(Math.max(adjustedScroll / 3, 1), this._lottieInstance.totalFrames * 3), roundedScroll = clampedScroll / 3;
1699
+ requestAnimationFrame(()=>{
1700
+ if (roundedScroll < (this._lottieInstance?.totalFrames ?? 0)) {
1701
+ this.playerState = PlayerState.Playing;
1702
+ this._lottieInstance?.goToAndStop(roundedScroll, true);
1703
+ } else {
1704
+ this.playerState = PlayerState.Paused;
1705
+ }
1706
+ });
1707
+ }
1612
1708
  }
1613
- /**
1614
- * Set loop
1615
- * @param { boolean } value
1616
- */ setLoop(value) {
1617
- if (!this._lottieInstance) {
1618
- return;
1709
+ _handleWindowBlur({ type }) {
1710
+ if (this.playerState === PlayerState.Playing && type === 'blur') {
1711
+ this._freeze();
1712
+ }
1713
+ if (this.playerState === PlayerState.Frozen && type === 'focus') {
1714
+ this.play();
1619
1715
  }
1620
- this._lottieInstance.setLoop(value);
1621
1716
  }
1622
- /**
1623
- * Toggle playing state
1624
- */ togglePlay() {
1717
+ _isLottie(json) {
1718
+ const mandatory = [
1719
+ 'v',
1720
+ 'ip',
1721
+ 'op',
1722
+ 'layers',
1723
+ 'fr',
1724
+ 'w',
1725
+ 'h'
1726
+ ];
1727
+ return mandatory.every((field)=>Object.hasOwn(json, field));
1728
+ }
1729
+ _loopComplete() {
1625
1730
  if (!this._lottieInstance) {
1626
1731
  return;
1627
1732
  }
1628
- const { currentFrame, playDirection, totalFrames } = this._lottieInstance;
1629
- if (this.playerState === PlayerState.Playing) {
1630
- return this.pause();
1631
- }
1632
- if (this.playerState !== PlayerState.Completed) {
1633
- return this.play();
1733
+ const { playDirection, // firstFrame,
1734
+ totalFrames } = this._lottieInstance, inPoint = this._segment ? this._segment[0] : 0, outPoint = this._segment ? this._segment[0] : totalFrames;
1735
+ if (this.count) {
1736
+ if (this._isBounce) {
1737
+ this._playerState.count += 0.5;
1738
+ } else {
1739
+ this._playerState.count += 1;
1740
+ }
1741
+ if (this._playerState.count >= this.count) {
1742
+ this.setLoop(false);
1743
+ this.playerState = PlayerState.Completed;
1744
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Complete));
1745
+ return;
1746
+ }
1634
1747
  }
1635
- this.playerState = PlayerState.Playing;
1748
+ this.dispatchEvent(new CustomEvent(PlayerEvents.Loop));
1636
1749
  if (this._isBounce) {
1637
- this.setDirection(playDirection * -1);
1638
- return this._lottieInstance.goToAndPlay(currentFrame, true);
1639
- }
1640
- if (playDirection === -1) {
1641
- return this._lottieInstance.goToAndPlay(totalFrames, true);
1750
+ this._lottieInstance.goToAndStop(playDirection === -1 ? inPoint : outPoint * 0.99, true);
1751
+ this._lottieInstance.setDirection(playDirection * -1);
1752
+ return setTimeout(()=>{
1753
+ if (!this.animateOnScroll) {
1754
+ this._lottieInstance?.play();
1755
+ }
1756
+ }, this.intermission);
1642
1757
  }
1643
- return this._lottieInstance.goToAndPlay(0, true);
1758
+ this._lottieInstance.goToAndStop(playDirection === -1 ? outPoint * 0.99 : inPoint, true);
1759
+ return setTimeout(()=>{
1760
+ if (!this.animateOnScroll) {
1761
+ this._lottieInstance?.play();
1762
+ }
1763
+ }, this.intermission);
1644
1764
  }
1645
1765
  /**
1646
- * Toggle loop
1647
- */ toggleLoop() {
1648
- const val = !this.loop;
1649
- this.loop = val;
1650
- this.setLoop(val);
1766
+ * Handle MouseEnter.
1767
+ */ _mouseEnter() {
1768
+ if (this.hover && this.playerState !== PlayerState.Playing) {
1769
+ this.play();
1770
+ }
1651
1771
  }
1652
1772
  /**
1653
- * Toggle Boomerang
1654
- */ toggleBoomerang() {
1655
- const curr = this._multiAnimationSettings?.[this._currentAnimation];
1656
- if (curr?.mode !== undefined) {
1657
- if (curr.mode === PlayMode.Normal) {
1658
- curr.mode = PlayMode.Bounce;
1659
- this._isBounce = true;
1660
- return;
1661
- }
1662
- curr.mode = PlayMode.Normal;
1663
- this._isBounce = false;
1664
- return;
1665
- }
1666
- if (this.mode === PlayMode.Normal) {
1667
- this.mode = PlayMode.Bounce;
1668
- this._isBounce = true;
1669
- return;
1773
+ * Handle MouseLeave.
1774
+ */ _mouseLeave() {
1775
+ if (this.hover && this.playerState === PlayerState.Playing) {
1776
+ this.stop();
1670
1777
  }
1671
- this.mode = PlayMode.Normal;
1672
- this._isBounce = false;
1673
1778
  }
1674
1779
  /**
1675
- * Toggle show Settings
1676
- */ _toggleSettings(flag) {
1677
- if (flag === undefined) {
1678
- this._isSettingsOpen = !this._isSettingsOpen;
1780
+ * Handle visibility change events.
1781
+ */ _onVisibilityChange() {
1782
+ if (document.hidden && this.playerState === PlayerState.Playing) {
1783
+ this._freeze();
1679
1784
  return;
1680
1785
  }
1681
- this._isSettingsOpen = flag;
1786
+ if (this.playerState === PlayerState.Frozen) {
1787
+ this.play();
1788
+ }
1682
1789
  }
1683
1790
  /**
1684
- * Handle blur
1685
- */ _handleBlur() {
1686
- setTimeout(()=>this._toggleSettings(false), 200);
1791
+ * Remove event listeners.
1792
+ */ _removeEventListeners() {
1793
+ this._toggleEventListeners('remove');
1687
1794
  }
1688
1795
  _switchInstance(isPrevious = false) {
1689
1796
  // Bail early if there is not animation to play
@@ -1696,82 +1803,86 @@ var css_248z = "* {\n box-sizing: border-box;\n}\n\n:host {\n --lottie-player-
1696
1803
  this._lottieInstance.destroy();
1697
1804
  }
1698
1805
  // Re-initialize lottie player
1699
- this._lottieInstance = Lottie.default.loadAnimation({
1806
+ // @ts-expect-error TODO:
1807
+ this._lottieInstance = Lottie.loadAnimation({
1700
1808
  ...this._getOptions(),
1701
1809
  animationData: this._animations[this._currentAnimation]
1702
1810
  });
1703
1811
  // Check play mode for current animation
1704
- if (this._multiAnimationSettings?.[this._currentAnimation]?.mode) {
1812
+ if (this._multiAnimationSettings[this._currentAnimation]?.mode) {
1705
1813
  this._isBounce = this._multiAnimationSettings[this._currentAnimation].mode === PlayMode.Bounce;
1706
1814
  }
1707
1815
  // Remove event listeners to new Lottie instance, and add new
1708
1816
  this._removeEventListeners();
1709
1817
  this._addEventListeners();
1710
1818
  this.dispatchEvent(new CustomEvent(isPrevious ? PlayerEvents.Previous : PlayerEvents.Next));
1711
- if (this._multiAnimationSettings?.[this._currentAnimation]?.autoplay ?? this.autoplay) {
1819
+ if (this._multiAnimationSettings[this._currentAnimation]?.autoplay ?? this.autoplay) {
1712
1820
  if (this.animateOnScroll) {
1713
- this._lottieInstance?.goToAndStop(0, true);
1821
+ this._lottieInstance.goToAndStop(0, true);
1714
1822
  this.playerState = PlayerState.Paused;
1715
1823
  return;
1716
1824
  }
1717
- this._lottieInstance?.goToAndPlay(0, true);
1825
+ this._lottieInstance.goToAndPlay(0, true);
1718
1826
  this.playerState = PlayerState.Playing;
1719
1827
  return;
1720
1828
  }
1721
- this._lottieInstance?.goToAndStop(0, true);
1829
+ this._lottieInstance.goToAndStop(0, true);
1722
1830
  this.playerState = PlayerState.Stopped;
1723
- } catch (err) {
1724
- this._errorMessage = handleErrors(err).message;
1831
+ } catch (error) {
1832
+ this._errorMessage = handleErrors(error).message;
1725
1833
  this.playerState = PlayerState.Error;
1726
1834
  this.dispatchEvent(new CustomEvent(PlayerEvents.Error));
1727
1835
  }
1728
1836
  }
1729
1837
  /**
1730
- * Skip to next animation
1731
- */ next() {
1732
- this._currentAnimation++;
1733
- this._switchInstance();
1734
- }
1735
- /**
1736
- * Skip to previous animation
1737
- */ prev() {
1738
- this._currentAnimation--;
1739
- this._switchInstance(true);
1740
- }
1741
- async convert({ animations, fileName, manifest, shouldDownload = true, src, typeCheck }) {
1742
- if (typeCheck || this._isDotLottie) {
1743
- return createJSON({
1744
- animation: (await getAnimationData(src || this.src))?.animations?.[0],
1745
- fileName: `${getFilename(fileName || this.src || 'converted')}.json`,
1746
- shouldDownload
1747
- });
1838
+ * Toggle event listeners.
1839
+ */ _toggleEventListeners(action) {
1840
+ const method = action === 'add' ? 'addEventListener' : 'removeEventListener';
1841
+ if (this._lottieInstance) {
1842
+ this._lottieInstance[method]('enterFrame', this._enterFrame);
1843
+ this._lottieInstance[method]('complete', this._complete);
1844
+ this._lottieInstance[method]('loopComplete', this._loopComplete);
1845
+ this._lottieInstance[method]('DOMLoaded', this._DOMLoaded);
1846
+ this._lottieInstance[method]('data_ready', this._dataReady);
1847
+ this._lottieInstance[method]('data_failed', this._dataFailed);
1748
1848
  }
1749
- return createDotLottie({
1750
- animations: animations || (await getAnimationData(this.src))?.animations,
1751
- fileName: `${getFilename(fileName || this.src || 'converted')}.lottie`,
1752
- manifest: {
1753
- ...manifest || this._manifest,
1754
- generator: pkg.name
1755
- },
1756
- shouldDownload
1849
+ if (this._container && this.hover) {
1850
+ this._container[method]('mouseenter', this._mouseEnter);
1851
+ this._container[method]('mouseleave', this._mouseLeave);
1852
+ }
1853
+ window[method]('focus', this._handleWindowBlur, {
1854
+ capture: false,
1855
+ passive: true
1856
+ });
1857
+ window[method]('blur', this._handleWindowBlur, {
1858
+ capture: false,
1859
+ passive: true
1757
1860
  });
1861
+ if (this.animateOnScroll) {
1862
+ window[method]('scroll', this._handleScroll, {
1863
+ capture: true,
1864
+ passive: true
1865
+ });
1866
+ }
1758
1867
  }
1759
1868
  /**
1760
- * Return the styles for the component
1761
- */ static get styles() {
1762
- const styleSheet = new CSSStyleSheet();
1763
- styleSheet.replace(css_248z);
1764
- return styleSheet;
1869
+ * Toggle show Settings.
1870
+ */ _toggleSettings(flag) {
1871
+ if (flag === undefined) {
1872
+ this._isSettingsOpen = !this._isSettingsOpen;
1873
+ return;
1874
+ }
1875
+ this._isSettingsOpen = flag;
1765
1876
  }
1766
1877
  }
1767
1878
 
1768
1879
  /**
1769
- * Expose DotLottiePlayer class as global variable
1770
- * @returns { DotLottiePlayer }
1880
+ * Expose DotLottiePlayer class as global variable.
1881
+ *
1771
1882
  */ globalThis.dotLottiePlayer = ()=>new DotLottiePlayer();
1772
1883
  const tagName = 'dotlottie-player';
1773
1884
  if (!isServer()) {
1774
- customElements.define('dotlottie-player', DotLottiePlayer);
1885
+ customElements.define(tagName, DotLottiePlayer);
1775
1886
  }
1776
1887
 
1777
1888
  export { PlayMode, PlayerEvents, PlayerState, DotLottiePlayer as default, tagName };