animejs 4.0.2 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/types/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * anime.js - ESM
3
- * @version v4.0.2
3
+ * @version v4.1.1
4
4
  * @author Julian Garnier
5
5
  * @license MIT
6
6
  * @copyright (c) 2025 Julian Garnier
@@ -56,10 +56,13 @@ const K = 1e3;
56
56
  const maxFps = 120;
57
57
  // Strings
58
58
  const emptyString = '';
59
- const shortTransforms = new Map();
60
- shortTransforms.set('x', 'translateX');
61
- shortTransforms.set('y', 'translateY');
62
- shortTransforms.set('z', 'translateZ');
59
+ const shortTransforms = /*#__PURE__*/ (() => {
60
+ const map = new Map();
61
+ map.set('x', 'translateX');
62
+ map.set('y', 'translateY');
63
+ map.set('z', 'translateZ');
64
+ return map;
65
+ })();
63
66
  const validTransforms = [
64
67
  'translateX',
65
68
  'translateY',
@@ -79,7 +82,7 @@ const validTransforms = [
79
82
  'matrix',
80
83
  'matrix3d',
81
84
  ];
82
- const transformsFragmentStrings = validTransforms.reduce((a, v) => ({ ...a, [v]: v + '(' }), {});
85
+ const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({ ...a, [v]: v + '(' }), {});
83
86
  // Functions
84
87
  /** @return {void} */
85
88
  const noop = () => { };
@@ -123,13 +126,15 @@ const defaults = {
123
126
  onComplete: noop,
124
127
  onRender: noop,
125
128
  };
129
+ const scope = {
130
+ /** @type {Scope} */
131
+ current: null,
132
+ /** @type {Document|DOMTarget} */
133
+ root: doc,
134
+ };
126
135
  const globals = {
127
136
  /** @type {DefaultsParams} */
128
137
  defaults,
129
- /** @type {Document|DOMTarget} */
130
- root: doc,
131
- /** @type {Scope} */
132
- scope: null,
133
138
  /** @type {Number} */
134
139
  precision: 4,
135
140
  /** @type {Number} */
@@ -137,7 +142,7 @@ const globals = {
137
142
  /** @type {Number} */
138
143
  tickThreshold: 200,
139
144
  };
140
- const globalVersions = { version: '4.0.2', engine: null };
145
+ const globalVersions = { version: '4.1.1', engine: null };
141
146
  if (isBrowser) {
142
147
  if (!win.AnimeJS)
143
148
  win.AnimeJS = [];
@@ -244,6 +249,28 @@ const snap = (v, increment) => isArr(increment) ? increment.reduce((closest, cv)
244
249
  * @return {Number}
245
250
  */
246
251
  const interpolate = (start, end, progress) => start + (end - start) * progress;
252
+ /**
253
+ * @param {Number} min
254
+ * @param {Number} max
255
+ * @param {Number} [decimalLength]
256
+ * @return {Number}
257
+ */
258
+ const random = (min, max, decimalLength) => { const m = 10 ** (decimalLength || 0); return floor((Math.random() * (max - min + (1 / m)) + min) * m) / m; };
259
+ /**
260
+ * Adapted from https://bost.ocks.org/mike/shuffle/
261
+ * @param {Array} items
262
+ * @return {Array}
263
+ */
264
+ const shuffle = items => {
265
+ let m = items.length, t, i;
266
+ while (m) {
267
+ i = random(0, --m);
268
+ t = items[m];
269
+ items[m] = items[i];
270
+ items[i] = t;
271
+ }
272
+ return items;
273
+ };
247
274
  /**
248
275
  * @param {Number} v
249
276
  * @return {Number}
@@ -332,6 +359,32 @@ const addChild = (parent, child, sortMethod, prevProp = '_prev', nextProp = '_ne
332
359
  child[prevProp] = prev;
333
360
  child[nextProp] = next;
334
361
  };
362
+ /**
363
+ * @param {(...args: any[]) => Tickable | ((...args: any[]) => void)} constructor
364
+ * @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
365
+ */
366
+ const createRefreshable = constructor => {
367
+ /** @type {Tickable} */
368
+ let tracked;
369
+ return (...args) => {
370
+ let currentIteration, currentIterationProgress, reversed, alternate;
371
+ if (tracked) {
372
+ currentIteration = tracked.currentIteration;
373
+ currentIterationProgress = tracked.iterationProgress;
374
+ reversed = tracked.reversed;
375
+ alternate = tracked._alternate;
376
+ tracked.revert();
377
+ }
378
+ const cleanup = constructor(...args);
379
+ if (cleanup && !isFnc(cleanup) && cleanup.revert)
380
+ tracked = cleanup;
381
+ if (!isUnd(currentIterationProgress)) {
382
+ /** @type {Tickable} */ (tracked).currentIteration = currentIteration;
383
+ /** @type {Tickable} */ (tracked).iterationProgress = (alternate ? !(currentIteration % 2) ? reversed : !reversed : reversed) ? 1 - currentIterationProgress : currentIterationProgress;
384
+ }
385
+ return cleanup || noop;
386
+ };
387
+ };
335
388
 
336
389
  /*
337
390
  * Base class to control framerate and playback rate.
@@ -801,8 +854,8 @@ const addAdditiveAnimation = lookups => {
801
854
  return animation;
802
855
  };
803
856
 
804
- const engineTickMethod = isBrowser ? requestAnimationFrame : setImmediate;
805
- const engineCancelMethod = isBrowser ? cancelAnimationFrame : clearImmediate;
857
+ const engineTickMethod = /*#__PURE__*/ (() => isBrowser ? requestAnimationFrame : setImmediate)();
858
+ const engineCancelMethod = /*#__PURE__*/ (() => isBrowser ? cancelAnimationFrame : clearImmediate)();
806
859
  class Engine extends Clock {
807
860
  /** @param {Number} [initTime] */
808
861
  constructor(initTime) {
@@ -958,7 +1011,7 @@ const parseInlineTransforms = (target, propName, animationInlineStyles) => {
958
1011
  * @return {NodeList|HTMLCollection}
959
1012
  */
960
1013
  function getNodeList(v) {
961
- const n = isStr(v) ? globals.root.querySelectorAll(v) : v;
1014
+ const n = isStr(v) ? scope.root.querySelectorAll(v) : v;
962
1015
  if (n instanceof NodeList || n instanceof HTMLCollection)
963
1016
  return n;
964
1017
  }
@@ -1803,8 +1856,8 @@ class Timer extends Clock {
1803
1856
  constructor(parameters = {}, parent = null, parentPosition = 0) {
1804
1857
  super(0);
1805
1858
  const { id, delay, duration, reversed, alternate, loop, loopDelay, autoplay, frameRate, playbackRate, onComplete, onLoop, onPause, onBegin, onBeforeUpdate, onUpdate, } = parameters;
1806
- if (globals.scope)
1807
- globals.scope.revertibles.push(this);
1859
+ if (scope.current)
1860
+ scope.current.register(this);
1808
1861
  const timerInitTime = parent ? 0 : engine._elapsedTime;
1809
1862
  const timerDefaults = parent ? parent.defaults : globals.defaults;
1810
1863
  const timerDelay = /** @type {Number} */ (isFnc(delay) || isUnd(delay) ? timerDefaults.delay : +delay);
@@ -1924,14 +1977,14 @@ class Timer extends Clock {
1924
1977
  this.currentTime = (this.iterationDuration * this._currentIteration) + time;
1925
1978
  }
1926
1979
  get progress() {
1927
- return clamp(round(this._currentTime / this.duration, 5), 0, 1);
1980
+ return clamp(round(this._currentTime / this.duration, 10), 0, 1);
1928
1981
  }
1929
1982
  /** @param {Number} progress */
1930
1983
  set progress(progress) {
1931
1984
  this.currentTime = this.duration * progress;
1932
1985
  }
1933
1986
  get iterationProgress() {
1934
- return clamp(round(this._iterationTime / this.iterationDuration, 5), 0, 1);
1987
+ return clamp(round(this._iterationTime / this.iterationDuration, 10), 0, 1);
1935
1988
  }
1936
1989
  /** @param {Number} progress */
1937
1990
  set iterationProgress(progress) {
@@ -3090,12 +3143,13 @@ class JSAnimation extends Timer {
3090
3143
  */
3091
3144
  refresh() {
3092
3145
  forEachChildren(this, (/** @type {Tween} */ tween) => {
3093
- const ogValue = getOriginalAnimatableValue(tween.target, tween.property, tween._tweenType);
3094
- decomposeRawValue(ogValue, decomposedOriginalValue);
3095
- tween._fromNumbers = cloneArray(decomposedOriginalValue.d);
3096
- tween._fromNumber = decomposedOriginalValue.n;
3097
- if (tween._func) {
3098
- decomposeRawValue(tween._func(), toTargetObject);
3146
+ const tweenFunc = tween._func;
3147
+ if (tweenFunc) {
3148
+ const ogValue = getOriginalAnimatableValue(tween.target, tween.property, tween._tweenType);
3149
+ decomposeRawValue(ogValue, decomposedOriginalValue);
3150
+ decomposeRawValue(tweenFunc(), toTargetObject);
3151
+ tween._fromNumbers = cloneArray(decomposedOriginalValue.d);
3152
+ tween._fromNumber = decomposedOriginalValue.n;
3099
3153
  tween._toNumbers = cloneArray(toTargetObject.d);
3100
3154
  tween._strings = cloneArray(toTargetObject.s);
3101
3155
  tween._toNumber = toTargetObject.n;
@@ -3243,31 +3297,8 @@ const commonDefaultPXProperties = [
3243
3297
  'borderRadius',
3244
3298
  ...transformsShorthands
3245
3299
  ];
3246
- const validIndividualTransforms = [...transformsShorthands, ...validTransforms.filter(t => ['X', 'Y', 'Z'].some(axis => t.endsWith(axis)))];
3247
- // Setting it to true in case CSS.registerProperty is not supported will automatically skip the registration and fallback to no animation
3248
- let transformsPropertiesRegistered = isBrowser && (isUnd(CSS) || !Object.hasOwnProperty.call(CSS, 'registerProperty'));
3249
- const registerTransformsProperties = () => {
3250
- if (transformsPropertiesRegistered)
3251
- return;
3252
- validTransforms.forEach(t => {
3253
- const isSkew = stringStartsWith(t, 'skew');
3254
- const isScale = stringStartsWith(t, 'scale');
3255
- const isRotate = stringStartsWith(t, 'rotate');
3256
- const isTranslate = stringStartsWith(t, 'translate');
3257
- const isAngle = isRotate || isSkew;
3258
- const syntax = isAngle ? '<angle>' : isScale ? "<number>" : isTranslate ? "<length-percentage>" : "*";
3259
- try {
3260
- CSS.registerProperty({
3261
- name: '--' + t,
3262
- syntax,
3263
- inherits: false,
3264
- initialValue: isTranslate ? '0px' : isAngle ? '0deg' : isScale ? '1' : '0',
3265
- });
3266
- }
3267
- catch { }
3268
- });
3269
- transformsPropertiesRegistered = true;
3270
- };
3300
+ const validIndividualTransforms = /*#__PURE__*/ (() => [...transformsShorthands, ...validTransforms.filter(t => ['X', 'Y', 'Z'].some(axis => t.endsWith(axis)))])();
3301
+ let transformsPropertiesRegistered = null;
3271
3302
  const WAAPIAnimationsLookups = {
3272
3303
  _head: null,
3273
3304
  _tail: null,
@@ -3380,9 +3411,34 @@ class WAAPIAnimation {
3380
3411
  * @param {WAAPIAnimationParams} params
3381
3412
  */
3382
3413
  constructor(targets, params) {
3383
- if (globals.scope)
3384
- globals.scope.revertibles.push(this);
3385
- registerTransformsProperties();
3414
+ if (scope.current)
3415
+ scope.current.register(this);
3416
+ // Skip the registration and fallback to no animation in case CSS.registerProperty is not supported
3417
+ if (isNil(transformsPropertiesRegistered)) {
3418
+ if (isBrowser && (isUnd(CSS) || !Object.hasOwnProperty.call(CSS, 'registerProperty'))) {
3419
+ transformsPropertiesRegistered = false;
3420
+ }
3421
+ else {
3422
+ validTransforms.forEach(t => {
3423
+ const isSkew = stringStartsWith(t, 'skew');
3424
+ const isScale = stringStartsWith(t, 'scale');
3425
+ const isRotate = stringStartsWith(t, 'rotate');
3426
+ const isTranslate = stringStartsWith(t, 'translate');
3427
+ const isAngle = isRotate || isSkew;
3428
+ const syntax = isAngle ? '<angle>' : isScale ? "<number>" : isTranslate ? "<length-percentage>" : "*";
3429
+ try {
3430
+ CSS.registerProperty({
3431
+ name: '--' + t,
3432
+ syntax,
3433
+ inherits: false,
3434
+ initialValue: isTranslate ? '0px' : isAngle ? '0deg' : isScale ? '1' : '0',
3435
+ });
3436
+ }
3437
+ catch { }
3438
+ });
3439
+ transformsPropertiesRegistered = true;
3440
+ }
3441
+ }
3386
3442
  const parsedTargets = registerTargets(targets);
3387
3443
  const targetsLength = parsedTargets.length;
3388
3444
  if (!targetsLength) {
@@ -3815,32 +3871,15 @@ const remove = (targets, renderable, propertyName) => {
3815
3871
  return targetsArray;
3816
3872
  };
3817
3873
  /**
3818
- * @param {Number} min
3819
- * @param {Number} max
3820
- * @param {Number} [decimalLength]
3821
- * @return {Number}
3874
+ * @param {(...args: any[]) => Tickable} constructor
3875
+ * @return {(...args: any[]) => Tickable}
3822
3876
  */
3823
- const random = (min, max, decimalLength) => { const m = 10 ** (decimalLength || 0); return floor((Math.random() * (max - min + (1 / m)) + min) * m) / m; };
3877
+ const keepTime = createRefreshable;
3824
3878
  /**
3825
3879
  * @param {String|Array} items
3826
3880
  * @return {any}
3827
3881
  */
3828
3882
  const randomPick = items => items[random(0, items.length - 1)];
3829
- /**
3830
- * Adapted from https://bost.ocks.org/mike/shuffle/
3831
- * @param {Array} items
3832
- * @return {Array}
3833
- */
3834
- const shuffle = items => {
3835
- let m = items.length, t, i;
3836
- while (m) {
3837
- i = random(0, --m);
3838
- t = items[m];
3839
- items[m] = items[i];
3840
- items[i] = t;
3841
- }
3842
- return items;
3843
- };
3844
3883
  /**
3845
3884
  * @param {Number|String} v
3846
3885
  * @param {Number} decimalLength
@@ -4021,6 +4060,7 @@ const utils = {
4021
4060
  shuffle,
4022
4061
  lerp,
4023
4062
  sync,
4063
+ keepTime,
4024
4064
  clamp: /** @type {typeof clamp & ChainedClamp} */ (makeChainable(clamp)),
4025
4065
  round: /** @type {typeof round & ChainedRound} */ (makeChainable(round)),
4026
4066
  snap: /** @type {typeof snap & ChainedSnap} */ (makeChainable(snap)),
@@ -4340,8 +4380,8 @@ class Animatable {
4340
4380
  * @param {AnimatableParams} parameters
4341
4381
  */
4342
4382
  constructor(targets, parameters) {
4343
- if (globals.scope)
4344
- globals.scope.revertibles.push(this);
4383
+ if (scope.current)
4384
+ scope.current.register(this);
4345
4385
  /** @type {AnimationParams} */
4346
4386
  const globalParams = {};
4347
4387
  const properties = {};
@@ -4665,8 +4705,8 @@ class Draggable {
4665
4705
  constructor(target, parameters = {}) {
4666
4706
  if (!target)
4667
4707
  return;
4668
- if (globals.scope)
4669
- globals.scope.revertibles.push(this);
4708
+ if (scope.current)
4709
+ scope.current.register(this);
4670
4710
  const paramX = parameters.x;
4671
4711
  const paramY = parameters.y;
4672
4712
  const trigger = parameters.trigger;
@@ -5696,39 +5736,11 @@ class Draggable {
5696
5736
  const createDraggable = (target, parameters) => new Draggable(target, parameters);
5697
5737
 
5698
5738
 
5699
- /**
5700
- * @typedef {Object} ReactRef
5701
- * @property {HTMLElement|SVGElement|null} [current]
5702
- */
5703
- /**
5704
- * @typedef {Object} AngularRef
5705
- * @property {HTMLElement|SVGElement} [nativeElement]
5706
- */
5707
- /**
5708
- * @typedef {Object} ScopeParams
5709
- * @property {DOMTargetSelector|ReactRef|AngularRef} [root]
5710
- * @property {DefaultsParams} [defaults]
5711
- * @property {Record<String, String>} [mediaQueries]
5712
- */
5713
- /**
5714
- * @callback ScopeCleanup
5715
- * @param {Scope} [scope]
5716
- */
5717
- /**
5718
- * @callback ScopeConstructor
5719
- * @param {Scope} [scope]
5720
- * @return {ScopeCleanup|void}
5721
- */
5722
- /**
5723
- * @callback ScopeMethod
5724
- * @param {...*} args
5725
- * @return {ScopeCleanup|void}
5726
- */
5727
5739
  class Scope {
5728
5740
  /** @param {ScopeParams} [parameters] */
5729
5741
  constructor(parameters = {}) {
5730
- if (globals.scope)
5731
- globals.scope.revertibles.push(this);
5742
+ if (scope.current)
5743
+ scope.current.register(this);
5732
5744
  const rootParam = parameters.root;
5733
5745
  /** @type {Document|DOMTarget} */
5734
5746
  let root = doc;
@@ -5745,13 +5757,23 @@ class Scope {
5745
5757
  this.defaults = scopeDefaults ? mergeObjects(scopeDefaults, globalDefault) : globalDefault;
5746
5758
  /** @type {Document|DOMTarget} */
5747
5759
  this.root = root;
5748
- /** @type {Array<ScopeConstructor>} */
5760
+ /** @type {Array<ScopeConstructorCallback>} */
5749
5761
  this.constructors = [];
5750
- /** @type {Array<Function>} */
5762
+ /** @type {Array<ScopeCleanupCallback>} */
5751
5763
  this.revertConstructors = [];
5752
5764
  /** @type {Array<Revertible>} */
5753
5765
  this.revertibles = [];
5754
- /** @type {Record<String, Function>} */
5766
+ /** @type {Array<ScopeConstructorCallback | ((scope: this) => Tickable)>} */
5767
+ this.constructorsOnce = [];
5768
+ /** @type {Array<ScopeCleanupCallback>} */
5769
+ this.revertConstructorsOnce = [];
5770
+ /** @type {Array<Revertible>} */
5771
+ this.revertiblesOnce = [];
5772
+ /** @type {Boolean} */
5773
+ this.once = false;
5774
+ /** @type {Number} */
5775
+ this.onceIndex = 0;
5776
+ /** @type {Record<String, ScopeMethod>} */
5755
5777
  this.methods = {};
5756
5778
  /** @type {Record<String, Boolean>} */
5757
5779
  this.matches = {};
@@ -5768,26 +5790,30 @@ class Scope {
5768
5790
  }
5769
5791
  }
5770
5792
  /**
5771
- * @callback ScoppedCallback
5772
- * @param {this} scope
5773
- * @return {any}
5774
- *
5775
- * @param {ScoppedCallback} cb
5776
- * @return {this}
5793
+ * @param {Revertible} revertible
5794
+ */
5795
+ register(revertible) {
5796
+ const store = this.once ? this.revertiblesOnce : this.revertibles;
5797
+ store.push(revertible);
5798
+ }
5799
+ /**
5800
+ * @template T
5801
+ * @param {ScopedCallback<T>} cb
5802
+ * @return {T}
5777
5803
  */
5778
5804
  execute(cb) {
5779
- let activeScope = globals.scope;
5780
- let activeRoot = globals.root;
5805
+ let activeScope = scope.current;
5806
+ let activeRoot = scope.root;
5781
5807
  let activeDefaults = globals.defaults;
5782
- globals.scope = this;
5783
- globals.root = this.root;
5808
+ scope.current = this;
5809
+ scope.root = this.root;
5784
5810
  globals.defaults = this.defaults;
5785
5811
  const mqs = this.mediaQueryLists;
5786
5812
  for (let mq in mqs)
5787
5813
  this.matches[mq] = mqs[mq].matches;
5788
5814
  const returned = cb(this);
5789
- globals.scope = activeScope;
5790
- globals.root = activeRoot;
5815
+ scope.current = activeScope;
5816
+ scope.root = activeRoot;
5791
5817
  globals.defaults = activeDefaults;
5792
5818
  return returned;
5793
5819
  }
@@ -5795,6 +5821,7 @@ class Scope {
5795
5821
  * @return {this}
5796
5822
  */
5797
5823
  refresh() {
5824
+ this.onceIndex = 0;
5798
5825
  this.execute(() => {
5799
5826
  let i = this.revertibles.length;
5800
5827
  let y = this.revertConstructors.length;
@@ -5804,9 +5831,9 @@ class Scope {
5804
5831
  this.revertConstructors[y](this);
5805
5832
  this.revertibles.length = 0;
5806
5833
  this.revertConstructors.length = 0;
5807
- this.constructors.forEach(constructor => {
5834
+ this.constructors.forEach((/** @type {ScopeConstructorCallback} */ constructor) => {
5808
5835
  const revertConstructor = constructor(this);
5809
- if (revertConstructor) {
5836
+ if (isFnc(revertConstructor)) {
5810
5837
  this.revertConstructors.push(revertConstructor);
5811
5838
  }
5812
5839
  });
@@ -5814,28 +5841,26 @@ class Scope {
5814
5841
  return this;
5815
5842
  }
5816
5843
  /**
5817
- * @callback contructorCallback
5818
- * @param {this} self
5819
- *
5820
5844
  * @overload
5821
5845
  * @param {String} a1
5822
5846
  * @param {ScopeMethod} a2
5823
5847
  * @return {this}
5824
5848
  *
5825
5849
  * @overload
5826
- * @param {contructorCallback} a1
5850
+ * @param {ScopeConstructorCallback} a1
5827
5851
  * @return {this}
5828
5852
  *
5829
- * @param {String|contructorCallback} a1
5853
+ * @param {String|ScopeConstructorCallback} a1
5830
5854
  * @param {ScopeMethod} [a2]
5831
5855
  */
5832
5856
  add(a1, a2) {
5857
+ this.once = false;
5833
5858
  if (isFnc(a1)) {
5834
- const constructor = /** @type {contructorCallback} */ (a1);
5859
+ const constructor = /** @type {ScopeConstructorCallback} */ (a1);
5835
5860
  this.constructors.push(constructor);
5836
5861
  this.execute(() => {
5837
5862
  const revertConstructor = constructor(this);
5838
- if (revertConstructor) {
5863
+ if (isFnc(revertConstructor)) {
5839
5864
  this.revertConstructors.push(revertConstructor);
5840
5865
  }
5841
5866
  });
@@ -5845,6 +5870,46 @@ class Scope {
5845
5870
  }
5846
5871
  return this;
5847
5872
  }
5873
+ /**
5874
+ * @param {ScopeConstructorCallback} scopeConstructorCallback
5875
+ * @return {this}
5876
+ */
5877
+ addOnce(scopeConstructorCallback) {
5878
+ this.once = true;
5879
+ if (isFnc(scopeConstructorCallback)) {
5880
+ const currentIndex = this.onceIndex++;
5881
+ const tracked = this.constructorsOnce[currentIndex];
5882
+ if (tracked)
5883
+ return this;
5884
+ const constructor = /** @type {ScopeConstructorCallback} */ (scopeConstructorCallback);
5885
+ this.constructorsOnce[currentIndex] = constructor;
5886
+ this.execute(() => {
5887
+ const revertConstructor = constructor(this);
5888
+ if (isFnc(revertConstructor)) {
5889
+ this.revertConstructorsOnce.push(revertConstructor);
5890
+ }
5891
+ });
5892
+ }
5893
+ return this;
5894
+ }
5895
+ /**
5896
+ * @param {(scope: this) => Tickable} cb
5897
+ * @return {Tickable}
5898
+ */
5899
+ keepTime(cb) {
5900
+ this.once = true;
5901
+ const currentIndex = this.onceIndex++;
5902
+ const tracked = /** @type {(scope: this) => Tickable} */ (this.constructorsOnce[currentIndex]);
5903
+ if (isFnc(tracked))
5904
+ return tracked(this);
5905
+ const constructor = /** @type {(scope: this) => Tickable} */ (createRefreshable(cb));
5906
+ this.constructorsOnce[currentIndex] = constructor;
5907
+ let trackedTickable;
5908
+ this.execute(() => {
5909
+ trackedTickable = constructor(this);
5910
+ });
5911
+ return trackedTickable;
5912
+ }
5848
5913
  /**
5849
5914
  * @param {Event} e
5850
5915
  */
@@ -5858,18 +5923,30 @@ class Scope {
5858
5923
  revert() {
5859
5924
  const revertibles = this.revertibles;
5860
5925
  const revertConstructors = this.revertConstructors;
5926
+ const revertiblesOnce = this.revertiblesOnce;
5927
+ const revertConstructorsOnce = this.revertConstructorsOnce;
5861
5928
  const mqs = this.mediaQueryLists;
5862
5929
  let i = revertibles.length;
5863
- let y = revertConstructors.length;
5930
+ let j = revertConstructors.length;
5931
+ let k = revertiblesOnce.length;
5932
+ let l = revertConstructorsOnce.length;
5864
5933
  while (i--)
5865
5934
  revertibles[i].revert();
5866
- while (y--)
5867
- revertConstructors[y](this);
5935
+ while (j--)
5936
+ revertConstructors[j](this);
5937
+ while (k--)
5938
+ revertiblesOnce[k].revert();
5939
+ while (l--)
5940
+ revertConstructorsOnce[l](this);
5868
5941
  for (let mq in mqs)
5869
5942
  mqs[mq].removeEventListener('change', this);
5870
5943
  revertibles.length = 0;
5871
5944
  revertConstructors.length = 0;
5872
5945
  this.constructors.length = 0;
5946
+ revertiblesOnce.length = 0;
5947
+ revertConstructorsOnce.length = 0;
5948
+ this.constructorsOnce.length = 0;
5949
+ this.onceIndex = 0;
5873
5950
  this.matches = {};
5874
5951
  this.methods = {};
5875
5952
  this.mediaQueryLists = {};
@@ -6186,7 +6263,7 @@ const getAnimationDomTarget = linked => {
6186
6263
  return $linkedTarget;
6187
6264
  };
6188
6265
  let scrollerIndex = 0;
6189
- const debugColors = ['#FF4B4B', '#FF971B', '#FFC730', '#F9F640', '#7AFF5A', '#18FF74', '#17E09B', '#3CFFEC', '#05DBE9', '#33B3F1', '#638CF9', '#C563FE', '#FF4FCF', '#F93F8A'];
6266
+ const debugColors$1 = ['#FF4B4B', '#FF971B', '#FFC730', '#F9F640', '#7AFF5A', '#18FF74', '#17E09B', '#3CFFEC', '#05DBE9', '#33B3F1', '#638CF9', '#C563FE', '#FF4FCF', '#F93F8A'];
6190
6267
  /**
6191
6268
  * @typedef {Object} ScrollThresholdParam
6192
6269
  * @property {ScrollThresholdValue} [target]
@@ -6227,8 +6304,8 @@ class ScrollObserver {
6227
6304
  * @param {ScrollObserverParams} parameters
6228
6305
  */
6229
6306
  constructor(parameters = {}) {
6230
- if (globals.scope)
6231
- globals.scope.revertibles.push(this);
6307
+ if (scope.current)
6308
+ scope.current.register(this);
6232
6309
  const syncMode = setValue(parameters.sync, 'play pause');
6233
6310
  const ease = syncMode ? parseEasings(/** @type {EasingParam} */ (syncMode)) : null;
6234
6311
  const isLinear = syncMode && (syncMode === 'linear' || syncMode === none);
@@ -6418,7 +6495,7 @@ class ScrollObserver {
6418
6495
  const $debug = doc.createElement('div');
6419
6496
  const $thresholds = doc.createElement('div');
6420
6497
  const $triggers = doc.createElement('div');
6421
- const color = debugColors[this.index % debugColors.length];
6498
+ const color = debugColors$1[this.index % debugColors$1.length];
6422
6499
  const useWin = container.useWin;
6423
6500
  const containerWidth = useWin ? container.winWidth : container.width;
6424
6501
  const containerHeight = useWin ? container.winHeight : container.height;
@@ -6771,27 +6848,475 @@ class ScrollObserver {
6771
6848
  const onScroll = (parameters = {}) => new ScrollObserver(parameters);
6772
6849
 
6773
6850
 
6851
+ const segmenter = !isUnd(Intl) && Intl.Segmenter;
6852
+ const valueRgx = /\{value\}/g;
6853
+ const indexRgx = /\{i\}/g;
6854
+ const whiteSpaceGroupRgx = /(\s+)/;
6855
+ const whiteSpaceRgx = /^\s+$/;
6856
+ const lineType = 'line';
6857
+ const wordType = 'word';
6858
+ const charType = 'char';
6859
+ const dataLine = `data-line`;
6860
+ /**
6861
+ * @typedef {Object} Segment
6862
+ * @property {String} segment
6863
+ * @property {Boolean} [isWordLike]
6864
+ */
6865
+ /**
6866
+ * @typedef {Object} Segmenter
6867
+ * @property {function(String): Iterable<Segment>} segment
6868
+ */
6869
+ /** @type {Segmenter} */
6870
+ let wordSegmenter = null;
6871
+ /** @type {Segmenter} */
6872
+ let graphemeSegmenter = null;
6873
+ let $splitTemplate = null;
6874
+ /**
6875
+ * @param {Segment} seg
6876
+ * @return {Boolean}
6877
+ */
6878
+ const isSegmentWordLike = seg => {
6879
+ return seg.isWordLike ||
6880
+ seg.segment === ' ' || // Consider spaces as words first, then handle them diffrently later
6881
+ isNum(+seg.segment); // Safari doesn't considers numbers as words
6882
+ };
6774
6883
  /**
6775
- * @typedef {Object} StaggerParameters
6776
- * @property {Number|String} [start]
6777
- * @property {Number|'first'|'center'|'last'} [from]
6778
- * @property {Boolean} [reversed]
6779
- * @property {Array.<Number>} [grid]
6780
- * @property {('x'|'y')} [axis]
6781
- * @property {EasingParam} [ease]
6782
- * @property {TweenModifier} [modifier]
6884
+ * @param {HTMLElement} $el
6783
6885
  */
6886
+ const setAriaHidden = $el => $el.setAttribute('aria-hidden', 'true');
6784
6887
  /**
6785
- * @callback StaggerFunction
6786
- * @param {Target} [target]
6787
- * @param {Number} [index]
6788
- * @param {Number} [length]
6789
- * @param {Timeline} [tl]
6790
- * @return {Number|String}
6888
+ * @param {DOMTarget} $el
6889
+ * @param {String} type
6890
+ * @return {Array<HTMLElement>}
6891
+ */
6892
+ const getAllTopLevelElements = ($el, type) => [... /** @type {*} */($el.querySelectorAll(`[data-${type}]:not([data-${type}] [data-${type}])`))];
6893
+ const debugColors = { line: '#00D672', word: '#FF4B4B', char: '#5A87FF' };
6894
+ /**
6895
+ * @param {HTMLElement} $el
6896
+ */
6897
+ const filterEmptyElements = $el => {
6898
+ if (!$el.childElementCount && !$el.textContent.trim()) {
6899
+ const $parent = $el.parentElement;
6900
+ $el.remove();
6901
+ if ($parent)
6902
+ filterEmptyElements($parent);
6903
+ }
6904
+ };
6905
+ /**
6906
+ * @param {HTMLElement} $el
6907
+ * @param {Number} lineIndex
6908
+ * @param {Set<HTMLElement>} bin
6909
+ * @returns {Set<HTMLElement>}
6910
+ */
6911
+ const filterLineElements = ($el, lineIndex, bin) => {
6912
+ const dataLineAttr = $el.getAttribute(dataLine);
6913
+ if (dataLineAttr !== null && +dataLineAttr !== lineIndex || $el.tagName === 'BR')
6914
+ bin.add($el);
6915
+ let i = $el.childElementCount;
6916
+ while (i--)
6917
+ filterLineElements(/** @type {HTMLElement} */ ($el.children[i]), lineIndex, bin);
6918
+ return bin;
6919
+ };
6920
+ /**
6921
+ * @param {'line'|'word'|'char'} type
6922
+ * @param {splitTemplateParams} params
6923
+ * @return {String}
6924
+ */
6925
+ const generateTemplate = (type, params = {}) => {
6926
+ let template = ``;
6927
+ const classString = isStr(params.class) ? ` class="${params.class}"` : '';
6928
+ const cloneType = setValue(params.clone, false);
6929
+ const wrapType = setValue(params.wrap, false);
6930
+ const overflow = wrapType ? wrapType === true ? 'clip' : wrapType : cloneType ? 'clip' : false;
6931
+ if (wrapType)
6932
+ template += `<span${overflow ? ` style="overflow:${overflow};"` : ''}>`;
6933
+ template += `<span${classString}${cloneType ? ` style="position:relative;"` : ''} data-${type}="{i}">`;
6934
+ if (cloneType) {
6935
+ const left = cloneType === 'left' ? '-100%' : cloneType === 'right' ? '100%' : '0';
6936
+ const top = cloneType === 'top' ? '-100%' : cloneType === 'bottom' ? '100%' : '0';
6937
+ template += `<span>{value}</span>`;
6938
+ template += `<span inert style="position:absolute;top:${top};left:${left};white-space:nowrap;">{value}</span>`;
6939
+ }
6940
+ else {
6941
+ template += `{value}`;
6942
+ }
6943
+ template += `</span>`;
6944
+ if (wrapType)
6945
+ template += `</span>`;
6946
+ return template;
6947
+ };
6948
+ /**
6949
+ * @param {String|SplitFunctionValue} htmlTemplate
6950
+ * @param {Array<HTMLElement>} store
6951
+ * @param {Node|HTMLElement} node
6952
+ * @param {DocumentFragment} $parentFragment
6953
+ * @param {'line'|'word'|'char'} type
6954
+ * @param {Boolean} debug
6955
+ * @param {Number} lineIndex
6956
+ * @param {Number} [wordIndex]
6957
+ * @param {Number} [charIndex]
6958
+ * @return {HTMLElement}
6959
+ */
6960
+ const processHTMLTemplate = (htmlTemplate, store, node, $parentFragment, type, debug, lineIndex, wordIndex, charIndex) => {
6961
+ const isLine = type === lineType;
6962
+ const isChar = type === charType;
6963
+ const className = `_${type}_`;
6964
+ const template = isFnc(htmlTemplate) ? htmlTemplate(node) : htmlTemplate;
6965
+ const displayStyle = isLine ? 'block' : 'inline-block';
6966
+ $splitTemplate.innerHTML = template
6967
+ .replace(valueRgx, `<i class="${className}"></i>`)
6968
+ .replace(indexRgx, `${isChar ? charIndex : isLine ? lineIndex : wordIndex}`);
6969
+ const $content = $splitTemplate.content;
6970
+ const $highestParent = /** @type {HTMLElement} */ ($content.firstElementChild);
6971
+ const $split = /** @type {HTMLElement} */ ($content.querySelector(`[data-${type}]`)) || $highestParent;
6972
+ const $replacables = /** @type {NodeListOf<HTMLElement>} */ ($content.querySelectorAll(`i.${className}`));
6973
+ const replacablesLength = $replacables.length;
6974
+ if (replacablesLength) {
6975
+ $highestParent.style.display = displayStyle;
6976
+ $split.style.display = displayStyle;
6977
+ $split.setAttribute(dataLine, `${lineIndex}`);
6978
+ if (!isLine) {
6979
+ $split.setAttribute('data-word', `${wordIndex}`);
6980
+ if (isChar)
6981
+ $split.setAttribute('data-char', `${charIndex}`);
6982
+ }
6983
+ let i = replacablesLength;
6984
+ while (i--) {
6985
+ const $replace = $replacables[i];
6986
+ const $closestParent = $replace.parentElement;
6987
+ $closestParent.style.display = displayStyle;
6988
+ if (isLine) {
6989
+ $closestParent.innerHTML = /** @type {HTMLElement} */ (node).innerHTML;
6990
+ }
6991
+ else {
6992
+ $closestParent.replaceChild(node.cloneNode(true), $replace);
6993
+ }
6994
+ }
6995
+ store.push($split);
6996
+ $parentFragment.appendChild($content);
6997
+ }
6998
+ else {
6999
+ console.warn(`The expression "{value}" is missing from the provided template.`);
7000
+ }
7001
+ if (debug)
7002
+ $highestParent.style.outline = `1px dotted ${debugColors[type]}`;
7003
+ return $highestParent;
7004
+ };
7005
+ /**
7006
+ * A class that splits text into words and wraps them in span elements while preserving the original HTML structure.
7007
+ * @class
7008
+ */
7009
+ class TextSplitter {
7010
+ /**
7011
+ * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
7012
+ * @param {TextSplitterParams} [parameters]
7013
+ */
7014
+ constructor(target, parameters = {}) {
7015
+ // Only init segmenters when needed
7016
+ if (!wordSegmenter)
7017
+ wordSegmenter = segmenter ? new segmenter([], { granularity: wordType }) : {
7018
+ segment: (text) => {
7019
+ const segments = [];
7020
+ const words = text.split(whiteSpaceGroupRgx);
7021
+ for (let i = 0, l = words.length; i < l; i++) {
7022
+ const segment = words[i];
7023
+ segments.push({
7024
+ segment,
7025
+ isWordLike: !whiteSpaceRgx.test(segment), // Consider non-whitespace as word-like
7026
+ });
7027
+ }
7028
+ return segments;
7029
+ }
7030
+ };
7031
+ if (!graphemeSegmenter)
7032
+ graphemeSegmenter = segmenter ? new segmenter([], { granularity: 'grapheme' }) : {
7033
+ segment: text => [...text].map(char => ({ segment: char }))
7034
+ };
7035
+ if (!$splitTemplate && isBrowser)
7036
+ $splitTemplate = doc.createElement('template');
7037
+ if (scope.current)
7038
+ scope.current.register(this);
7039
+ const { words, chars, lines, accessible, includeSpaces, debug } = parameters;
7040
+ const $target = /** @type {HTMLElement} */ ((target = isArr(target) ? target[0] : target) && /** @type {Node} */ (target).nodeType ? target : (getNodeList(target) || [])[0]);
7041
+ const lineParams = lines === true ? {} : lines;
7042
+ const wordParams = words === true || isUnd(words) ? {} : words;
7043
+ const charParams = chars === true ? {} : chars;
7044
+ this.debug = setValue(debug, false);
7045
+ this.includeSpaces = setValue(includeSpaces, false);
7046
+ this.accessible = setValue(accessible, true);
7047
+ this.linesOnly = lineParams && (!wordParams && !charParams);
7048
+ /** @type {String|false|SplitFunctionValue} */
7049
+ this.lineTemplate = isObj(lineParams) ? generateTemplate(lineType, /** @type {splitTemplateParams} */ (lineParams)) : lineParams;
7050
+ /** @type {String|false|SplitFunctionValue} */
7051
+ this.wordTemplate = isObj(wordParams) || this.linesOnly ? generateTemplate(wordType, /** @type {splitTemplateParams} */ (wordParams)) : wordParams;
7052
+ /** @type {String|false|SplitFunctionValue} */
7053
+ this.charTemplate = isObj(charParams) ? generateTemplate(charType, /** @type {splitTemplateParams} */ (charParams)) : charParams;
7054
+ this.$target = $target;
7055
+ this.html = $target && $target.innerHTML;
7056
+ this.lines = [];
7057
+ this.words = [];
7058
+ this.chars = [];
7059
+ this.effects = [];
7060
+ this.effectsCleanups = [];
7061
+ this.cache = null;
7062
+ this.ready = false;
7063
+ this.width = 0;
7064
+ this.resizeTimeout = null;
7065
+ const handleSplit = () => this.html && (lineParams || wordParams || charParams) && this.split();
7066
+ // Make sure this is declared before calling handleSplit() in case revert() is called inside an effect callback
7067
+ this.resizeObserver = new ResizeObserver(() => {
7068
+ // Use a setTimeout instead of a Timer for better tree shaking
7069
+ clearTimeout(this.resizeTimeout);
7070
+ this.resizeTimeout = setTimeout(() => {
7071
+ const currentWidth = /** @type {HTMLElement} */ ($target).offsetWidth;
7072
+ if (currentWidth === this.width)
7073
+ return;
7074
+ this.width = currentWidth;
7075
+ handleSplit();
7076
+ }, 150);
7077
+ });
7078
+ // Only declare the font ready promise when splitting by lines and not alreay split
7079
+ if (this.lineTemplate && !this.ready) {
7080
+ doc.fonts.ready.then(handleSplit);
7081
+ }
7082
+ else {
7083
+ handleSplit();
7084
+ }
7085
+ $target ? this.resizeObserver.observe($target) : console.warn('No Text Splitter target found.');
7086
+ }
7087
+ /**
7088
+ * @param {(...args: any[]) => Tickable | (() => void)} effect
7089
+ * @return this
7090
+ */
7091
+ addEffect(effect) {
7092
+ if (!isFnc(effect))
7093
+ return console.warn('Effect must return a function.');
7094
+ const refreshableEffect = createRefreshable(effect);
7095
+ this.effects.push(refreshableEffect);
7096
+ if (this.ready)
7097
+ this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
7098
+ return this;
7099
+ }
7100
+ revert() {
7101
+ clearTimeout(this.resizeTimeout);
7102
+ this.lines.length = this.words.length = this.chars.length = 0;
7103
+ this.resizeObserver.disconnect();
7104
+ // Make sure to revert the effects after disconnecting the resizeObserver to avoid triggering it in the process
7105
+ this.effectsCleanups.forEach(cleanup => isFnc(cleanup) ? cleanup(this) : cleanup.revert && cleanup.revert());
7106
+ this.$target.innerHTML = this.html;
7107
+ return this;
7108
+ }
7109
+ /**
7110
+ * Recursively processes a node and its children
7111
+ * @param {Node} node
7112
+ */
7113
+ splitNode(node) {
7114
+ const wordTemplate = this.wordTemplate;
7115
+ const charTemplate = this.charTemplate;
7116
+ const includeSpaces = this.includeSpaces;
7117
+ const debug = this.debug;
7118
+ const nodeType = node.nodeType;
7119
+ if (nodeType === 3) {
7120
+ const nodeText = node.nodeValue;
7121
+ // If the nodeText is only whitespace, leave it as is
7122
+ if (nodeText.trim()) {
7123
+ const tempWords = [];
7124
+ const words = this.words;
7125
+ const chars = this.chars;
7126
+ const wordSegments = wordSegmenter.segment(nodeText);
7127
+ const $wordsFragment = doc.createDocumentFragment();
7128
+ let prevSeg = null;
7129
+ for (const wordSegment of wordSegments) {
7130
+ const segment = wordSegment.segment;
7131
+ const isWordLike = isSegmentWordLike(wordSegment);
7132
+ // Determine if this segment should be a new word, first segment always becomes a new word
7133
+ if (!prevSeg || (isWordLike && (prevSeg && (isSegmentWordLike(prevSeg))))) {
7134
+ tempWords.push(segment);
7135
+ }
7136
+ else {
7137
+ // Only concatenate if both current and previous are non-word-like and don't contain spaces
7138
+ const lastWordIndex = tempWords.length - 1;
7139
+ const lastWord = tempWords[lastWordIndex];
7140
+ if (!lastWord.includes(' ') && !segment.includes(' ')) {
7141
+ tempWords[lastWordIndex] += segment;
7142
+ }
7143
+ else {
7144
+ tempWords.push(segment);
7145
+ }
7146
+ }
7147
+ prevSeg = wordSegment;
7148
+ }
7149
+ for (let i = 0, l = tempWords.length; i < l; i++) {
7150
+ const word = tempWords[i];
7151
+ if (!word.trim()) {
7152
+ // Preserve whitespace only if includeSpaces is false and if the current space is not the first node
7153
+ if (i && includeSpaces)
7154
+ continue;
7155
+ $wordsFragment.appendChild(doc.createTextNode(word));
7156
+ }
7157
+ else {
7158
+ const nextWord = tempWords[i + 1];
7159
+ const hasWordFollowingSpace = includeSpaces && nextWord && !nextWord.trim();
7160
+ const wordToProcess = word;
7161
+ const charSegments = charTemplate ? graphemeSegmenter.segment(wordToProcess) : null;
7162
+ const $charsFragment = charTemplate ? doc.createDocumentFragment() : doc.createTextNode(hasWordFollowingSpace ? word + '\xa0' : word);
7163
+ if (charTemplate) {
7164
+ const charSegmentsArray = [...charSegments];
7165
+ for (let j = 0, jl = charSegmentsArray.length; j < jl; j++) {
7166
+ const charSegment = charSegmentsArray[j];
7167
+ const isLastChar = j === jl - 1;
7168
+ // If this is the last character and includeSpaces is true with a following space, append the space
7169
+ const charText = isLastChar && hasWordFollowingSpace ? charSegment.segment + '\xa0' : charSegment.segment;
7170
+ const $charNode = doc.createTextNode(charText);
7171
+ processHTMLTemplate(charTemplate, chars, $charNode, /** @type {DocumentFragment} */ ($charsFragment), charType, debug, -1, words.length, chars.length);
7172
+ }
7173
+ }
7174
+ if (wordTemplate) {
7175
+ processHTMLTemplate(wordTemplate, words, $charsFragment, $wordsFragment, wordType, debug, -1, words.length, chars.length);
7176
+ // Chars elements must be re-parsed in the split() method if both words and chars are parsed
7177
+ }
7178
+ else if (charTemplate) {
7179
+ $wordsFragment.appendChild($charsFragment);
7180
+ }
7181
+ else {
7182
+ $wordsFragment.appendChild(doc.createTextNode(word));
7183
+ }
7184
+ // Skip the next iteration if we included a space
7185
+ if (hasWordFollowingSpace)
7186
+ i++;
7187
+ }
7188
+ }
7189
+ node.parentNode.replaceChild($wordsFragment, node);
7190
+ }
7191
+ }
7192
+ else if (nodeType === 1) {
7193
+ // Converting to an array is necessary to work around childNodes pottential mutation
7194
+ const childNodes = /** @type {Array<Node>} */ ([... /** @type {*} */(node.childNodes)]);
7195
+ for (let i = 0, l = childNodes.length; i < l; i++)
7196
+ this.splitNode(childNodes[i]);
7197
+ }
7198
+ }
7199
+ /**
7200
+ * @param {Boolean} clearCache
7201
+ * @return {this}
7202
+ */
7203
+ split(clearCache = false) {
7204
+ const $el = this.$target;
7205
+ const isCached = !!this.cache && !clearCache;
7206
+ const lineTemplate = this.lineTemplate;
7207
+ const wordTemplate = this.wordTemplate;
7208
+ const charTemplate = this.charTemplate;
7209
+ const fontsReady = doc.fonts.status !== 'loading';
7210
+ const canSplitLines = lineTemplate && fontsReady;
7211
+ this.ready = !lineTemplate || fontsReady;
7212
+ if (canSplitLines || clearCache) {
7213
+ // No need to revert effects animations here since it's already taken care by the refreshable
7214
+ this.effectsCleanups.forEach(cleanup => isFnc(cleanup) && cleanup(this));
7215
+ }
7216
+ if (!isCached) {
7217
+ if (clearCache) {
7218
+ $el.innerHTML = this.html;
7219
+ this.words.length = this.chars.length = 0;
7220
+ }
7221
+ this.splitNode($el);
7222
+ this.cache = $el.innerHTML;
7223
+ }
7224
+ if (canSplitLines) {
7225
+ if (isCached)
7226
+ $el.innerHTML = this.cache;
7227
+ this.lines.length = 0;
7228
+ if (wordTemplate)
7229
+ this.words = getAllTopLevelElements($el, wordType);
7230
+ }
7231
+ // Always reparse characters after a line reset or if both words and chars are activated
7232
+ if (charTemplate && (canSplitLines || wordTemplate)) {
7233
+ this.chars = getAllTopLevelElements($el, charType);
7234
+ }
7235
+ // Words are used when lines only and prioritized over chars
7236
+ const elementsArray = this.words.length ? this.words : this.chars;
7237
+ let y, linesCount = 0;
7238
+ for (let i = 0, l = elementsArray.length; i < l; i++) {
7239
+ const $el = elementsArray[i];
7240
+ const { top, height } = $el.getBoundingClientRect();
7241
+ if (y && top - y > height * .5)
7242
+ linesCount++;
7243
+ $el.setAttribute(dataLine, `${linesCount}`);
7244
+ const nested = $el.querySelectorAll(`[${dataLine}]`);
7245
+ let c = nested.length;
7246
+ while (c--)
7247
+ nested[c].setAttribute(dataLine, `${linesCount}`);
7248
+ y = top;
7249
+ }
7250
+ if (canSplitLines) {
7251
+ const linesFragment = doc.createDocumentFragment();
7252
+ const parents = new Set();
7253
+ const clones = [];
7254
+ for (let lineIndex = 0; lineIndex < linesCount + 1; lineIndex++) {
7255
+ const $clone = /** @type {HTMLElement} */ ($el.cloneNode(true));
7256
+ filterLineElements($clone, lineIndex, new Set()).forEach($el => {
7257
+ const $parent = $el.parentElement;
7258
+ if ($parent)
7259
+ parents.add($parent);
7260
+ $el.remove();
7261
+ });
7262
+ clones.push($clone);
7263
+ }
7264
+ parents.forEach(filterEmptyElements);
7265
+ for (let cloneIndex = 0, clonesLength = clones.length; cloneIndex < clonesLength; cloneIndex++) {
7266
+ processHTMLTemplate(lineTemplate, this.lines, clones[cloneIndex], linesFragment, lineType, this.debug, cloneIndex);
7267
+ }
7268
+ $el.innerHTML = '';
7269
+ $el.appendChild(linesFragment);
7270
+ if (wordTemplate)
7271
+ this.words = getAllTopLevelElements($el, wordType);
7272
+ if (charTemplate)
7273
+ this.chars = getAllTopLevelElements($el, charType);
7274
+ }
7275
+ // Remove the word wrappers and clear the words array if lines split only
7276
+ if (this.linesOnly) {
7277
+ const words = this.words;
7278
+ let w = words.length;
7279
+ while (w--) {
7280
+ const $word = words[w];
7281
+ $word.replaceWith($word.textContent);
7282
+ }
7283
+ words.length = 0;
7284
+ }
7285
+ if (this.accessible && (canSplitLines || !isCached)) {
7286
+ const $accessible = doc.createElement('span');
7287
+ // Make the accessible element visually-hidden (https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html)
7288
+ $accessible.style.cssText = `position:absolute;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);width:1px;height:1px;white-space:nowrap;`;
7289
+ // $accessible.setAttribute('tabindex', '-1');
7290
+ $accessible.innerHTML = this.html;
7291
+ $el.insertBefore($accessible, $el.firstChild);
7292
+ this.lines.forEach(setAriaHidden);
7293
+ this.words.forEach(setAriaHidden);
7294
+ this.chars.forEach(setAriaHidden);
7295
+ }
7296
+ this.width = /** @type {HTMLElement} */ ($el).offsetWidth;
7297
+ if (canSplitLines || clearCache) {
7298
+ this.effects.forEach((effect, i) => this.effectsCleanups[i] = effect(this));
7299
+ }
7300
+ return this;
7301
+ }
7302
+ refresh() {
7303
+ this.split(true);
7304
+ }
7305
+ }
7306
+ /**
7307
+ * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
7308
+ * @param {TextSplitterParams} [parameters]
7309
+ * @return {TextSplitter}
6791
7310
  */
7311
+ const split = (target, parameters) => new TextSplitter(target, parameters);
7312
+ const text = {
7313
+ split,
7314
+ };
7315
+
7316
+
6792
7317
  /**
6793
7318
  * @param {Number|String|[Number|String,Number|String]} val
6794
- * @param {StaggerParameters} params
7319
+ * @param {StaggerParams} params
6795
7320
  * @return {StaggerFunction}
6796
7321
  */
6797
7322
  const stagger = (val, params = {}) => {
@@ -6805,22 +7330,29 @@ const stagger = (val, params = {}) => {
6805
7330
  const staggerEase = hasSpring ? /** @type {Spring} */ (ease).ease : hasEasing ? parseEasings(ease) : null;
6806
7331
  const grid = params.grid;
6807
7332
  const axis = params.axis;
7333
+ const customTotal = params.total;
6808
7334
  const fromFirst = isUnd(from) || from === 0 || from === 'first';
6809
7335
  const fromCenter = from === 'center';
6810
7336
  const fromLast = from === 'last';
7337
+ const fromRandom = from === 'random';
6811
7338
  const isRange = isArr(val);
7339
+ const useProp = params.use;
6812
7340
  const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
6813
7341
  const val2 = isRange ? parseNumber(val[1]) : 0;
6814
7342
  const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
6815
7343
  const start = params.start || 0 + (isRange ? val1 : 0);
6816
7344
  let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
6817
- return (_, i, t, tl) => {
7345
+ return (target, i, t, tl) => {
7346
+ const [registeredTarget] = registerTargets(target);
7347
+ const total = isUnd(customTotal) ? t : customTotal;
7348
+ const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
7349
+ const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
6818
7350
  if (fromCenter)
6819
- fromIndex = (t - 1) / 2;
7351
+ fromIndex = (total - 1) / 2;
6820
7352
  if (fromLast)
6821
- fromIndex = t - 1;
7353
+ fromIndex = total - 1;
6822
7354
  if (!values.length) {
6823
- for (let index = 0; index < t; index++) {
7355
+ for (let index = 0; index < total; index++) {
6824
7356
  if (!grid) {
6825
7357
  values.push(abs(fromIndex - index));
6826
7358
  }
@@ -6844,11 +7376,13 @@ const stagger = (val, params = {}) => {
6844
7376
  values = values.map(val => staggerEase(val / maxValue) * maxValue);
6845
7377
  if (reversed)
6846
7378
  values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
7379
+ if (fromRandom)
7380
+ values = shuffle(values);
6847
7381
  }
6848
7382
  const spacing = isRange ? (val2 - val1) / maxValue : val1;
6849
7383
  const offset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */ (start);
6850
7384
  /** @type {String|Number} */
6851
- let output = offset + ((spacing * round(values[i], 2)) || 0);
7385
+ let output = offset + ((spacing * round(values[staggerIndex], 2)) || 0);
6852
7386
  if (params.modifier)
6853
7387
  output = params.modifier(output);
6854
7388
  if (unitMatch)
@@ -6857,4 +7391,4 @@ const stagger = (val, params = {}) => {
6857
7391
  };
6858
7392
  };
6859
7393
 
6860
- export { Animatable, Draggable, JSAnimation, Scope, ScrollObserver, Spring, Timeline, Timer, WAAPIAnimation, animate, createAnimatable, createDraggable, createScope, createSpring, createTimeline, createTimer, eases, engine, onScroll, scrollContainers, stagger, svg, utils, waapi };
7394
+ export { Animatable, Draggable, JSAnimation, Scope, ScrollObserver, Spring, TextSplitter, Timeline, Timer, WAAPIAnimation, animate, createAnimatable, createDraggable, createScope, createSpring, createTimeline, createTimer, eases, engine, onScroll, scrollContainers, stagger, svg, text, utils, waapi };