@brandocms/jupiter 5.0.0-beta.11 → 5.0.0-beta.13

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.
@@ -11,13 +11,16 @@ import * as Events from '../../events'
11
11
  import prefersReducedMotion from '../../utils/prefersReducedMotion'
12
12
  import imageIsLoaded from '../../utils/imageIsLoaded'
13
13
  import imagesAreLoaded from '../../utils/imagesAreLoaded'
14
- import { set, animateAutoAlpha, delayedCall, convertEasing } from '../../utils/motion-helpers'
14
+ import { set, delayedCall, convertEasing } from '../../utils/motion-helpers'
15
15
  import Dom from '../Dom'
16
16
 
17
17
  /**
18
18
  * Debug logging
19
19
  */
20
20
  const DEBUG = false
21
+ let _idCounter = 0
22
+
23
+ const MOONWALK_ATTRS = ['data-moonwalk', 'data-moonwalk-section', 'data-moonwalk-children']
21
24
 
22
25
  function logMoonwalk(category, message, data = {}) {
23
26
  if (DEBUG) {
@@ -40,30 +43,42 @@ function logComputedStyle(element, props = ['opacity', 'transform']) {
40
43
  * @property {Object} to - Ending properties for the transition
41
44
  */
42
45
 
46
+ /**
47
+ * @typedef {Object} AlphaTweenConfig
48
+ * @property {number} [duration] - Duration of the alpha tween (defaults to walk duration)
49
+ * @property {string} [ease] - Easing function (defaults to 'easeIn')
50
+ * @property {number} [delay] - Additional delay before the alpha tween starts
51
+ */
52
+
43
53
  /**
44
54
  * @typedef {Object} MoonwalkWalk
45
55
  * @property {number} [startDelay=0] - Delay before the animation starts
46
56
  * @property {number} [interval=0.15] - Time between animations in a sequence
47
57
  * @property {number} [duration=0.65] - Duration of the animation
48
- * @property {boolean|Object} [alphaTween=false] - Whether to add a separate opacity tween
49
- * @property {MoonwalkTransition} transition - The transition configuration
50
- * @property {string} [sectionTargets] - CSS selector for targeting elements in named sections
58
+ * @property {boolean|AlphaTweenConfig} [alphaTween=false] - Whether to add a separate opacity tween. Pass `true` for defaults or an AlphaTweenConfig object for control.
59
+ * @property {MoonwalkTransition|null} transition - The transition configuration. Set to `null` for CSS-only mode (uses `data-moonwalked` attribute for CSS transitions).
60
+ * @property {string} [sectionTargets] - CSS selector for targeting elements in named sections (instead of using direct children)
61
+ */
62
+
63
+ /**
64
+ * @typedef {Object} MoonwalkRunMeta
65
+ * @property {string|null} direction - The viewport entry/exit direction ('top', 'bottom', 'left', 'right', or null)
51
66
  */
52
67
 
53
68
  /**
54
69
  * @typedef {Object} MoonwalkRun
55
70
  * @property {number} [threshold=0] - IntersectionObserver threshold
56
- * @property {Function} callback - Function called when element enters viewport
57
- * @property {Function} [onExit] - Function called when element exits viewport
71
+ * @property {(el: HTMLElement, repeated: boolean, meta: MoonwalkRunMeta) => void} callback - Function called when element enters viewport
72
+ * @property {(el: HTMLElement, exited: boolean, meta: MoonwalkRunMeta) => void} [onExit] - Function called when element exits viewport
58
73
  * @property {boolean} [repeated=false] - Whether the run should repeat
59
74
  * @property {string} [rootMargin] - IntersectionObserver rootMargin
60
- * @property {Function} [initialize] - Function called during initialization
61
- * @property {Function} [onReady] - Function called when APPLICATION_REVEALED fires, before viewport observers start
75
+ * @property {(el: HTMLElement) => void} [initialize] - Function called during initialization
76
+ * @property {(el: HTMLElement) => void} [onReady] - Function called when APPLICATION_REVEALED fires, before viewport observers start
62
77
  */
63
78
 
64
79
  /**
65
80
  * @typedef {Object} MoonwalkOptions
66
- * @property {string|Function} [on=Events.APPLICATION_REVEALED] - Event to trigger animations
81
+ * @property {string|null} [on=Events.APPLICATION_REVEALED] - Event name to trigger animations. Set to `null` to trigger manually via `ready()`.
67
82
  * @property {number} [initialDelay=0.1] - Delay before starting animations
68
83
  * @property {boolean} [clearLazyload=false] - Clear data-ll-srcset attributes
69
84
  * @property {boolean} [clearNestedSections=true] - Remove nested data-moonwalk-section attributes
@@ -75,7 +90,7 @@ function logComputedStyle(element, props = ['opacity', 'transform']) {
75
90
  * @property {boolean} [uniqueIds=false] - Generate unique IDs for moonwalk elements
76
91
  * @property {boolean} [addIndexes=false] - Add index attributes to elements
77
92
  * @property {Object.<string, MoonwalkRun>} [runs={}] - Run configurations
78
- * @property {Object.<string, MoonwalkWalk>} walks - Walk configurations
93
+ * @property {Object.<string, MoonwalkWalk>} [walks] - Walk configurations
79
94
  */
80
95
 
81
96
  /** @type {MoonwalkOptions} */
@@ -166,7 +181,50 @@ const DEFAULT_OPTIONS = {
166
181
  }
167
182
 
168
183
  /**
169
- * Moonwalk animation system for scroll-based reveal animations
184
+ * Normalize alphaTween config into a consistent object form.
185
+ * Returns a new object (never mutates the original).
186
+ */
187
+ export function normalizeAlphaTween(alphaTween, duration) {
188
+ if (typeof alphaTween === 'object' && alphaTween !== null) {
189
+ return { ...alphaTween, duration: alphaTween.duration || duration }
190
+ }
191
+ if (alphaTween === true) {
192
+ return { duration, ease: 'easeIn' }
193
+ }
194
+ return alphaTween
195
+ }
196
+
197
+ /**
198
+ * Moonwalk animation system for scroll-based reveal animations.
199
+ *
200
+ * ## HTML attributes
201
+ *
202
+ * - `data-moonwalk` / `data-moonwalk="{walkName}"` — marks an element for scroll-triggered animation
203
+ * - `data-moonwalk-section` / `data-moonwalk-section="{walkName}"` — groups elements; unnamed sections
204
+ * animate children individually, named sections stagger-reveal all children at once
205
+ * - `data-moonwalk-children` / `data-moonwalk-children="{walkName}"` — converts direct children
206
+ * into `data-moonwalk` (or `data-moonwalk="{walkName}"`) elements automatically
207
+ * - `data-moonwalk-stage="{walkName}"` — applies a walk transition to the section element itself
208
+ * before its children animate (e.g. fade in a container, then reveal items)
209
+ * - `data-moonwalk-order="{number}"` — overrides the DOM order of children inside a named section;
210
+ * elements with order are sorted first, unordered elements keep their relative position
211
+ * - `data-moonwalk-run="{runName}"` — standalone observer-based callback (not part of walk system)
212
+ * - `data-placeholder` / `data-ll-placeholder` — skip waiting for image load before tweening
213
+ *
214
+ * ## CSS-only mode
215
+ *
216
+ * Set `transition: null` on a walk to use CSS-only animations. Moonwalk will stagger-add the
217
+ * `data-moonwalked` attribute instead of running JS tweens. Style the reveal via CSS:
218
+ * ```css
219
+ * [data-moonwalk="fade"] { opacity: 0; transition: opacity 0.5s; }
220
+ * [data-moonwalk="fade"][data-moonwalked] { opacity: 1; }
221
+ * ```
222
+ *
223
+ * ## alphaTween
224
+ *
225
+ * Can be `true` (defaults: duration from walk, ease `'easeIn'`) or an object:
226
+ * `{ duration?: number, ease?: string, delay?: number }` for fine-grained control
227
+ * over a separate opacity animation layered on top of the main transition.
170
228
  */
171
229
  export default class Moonwalk {
172
230
  /**
@@ -178,7 +236,7 @@ export default class Moonwalk {
178
236
  this.app = app
179
237
  this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
180
238
  if (container !== document.body) {
181
- this.opts.on = () => {}
239
+ this.opts.on = null
182
240
  }
183
241
  this.initialize(container)
184
242
  }
@@ -214,6 +272,7 @@ export default class Moonwalk {
214
272
  }
215
273
 
216
274
  this.addClass()
275
+ this._observers = []
217
276
  this.sections = this.initializeSections(container)
218
277
  this.runs = this.initializeRuns(container)
219
278
 
@@ -225,8 +284,9 @@ export default class Moonwalk {
225
284
  this.removeAllWalks(container)
226
285
  }
227
286
 
228
- if (this.opts.on) {
229
- window.addEventListener(this.opts.on, this.onReady.bind(this))
287
+ if (this.opts.on && typeof this.opts.on === 'string') {
288
+ this._boundOnReady = this.onReady.bind(this)
289
+ window.addEventListener(this.opts.on, this._boundOnReady)
230
290
  }
231
291
  }
232
292
 
@@ -275,12 +335,7 @@ export default class Moonwalk {
275
335
  * Remove all moonwalks. Useful for clients who prefer reduced motion
276
336
  */
277
337
  removeAllWalks(container = document.body) {
278
- const keys = [
279
- 'data-moonwalk',
280
- 'data-moonwalk-section',
281
- 'data-moonwalk-children',
282
- ]
283
- keys.forEach((key) => {
338
+ MOONWALK_ATTRS.forEach((key) => {
284
339
  const elems = container.querySelectorAll(`[${key}]`)
285
340
  Array.from(elems).forEach((el) => el.removeAttribute(key))
286
341
  container.removeAttribute(key)
@@ -288,12 +343,7 @@ export default class Moonwalk {
288
343
  }
289
344
 
290
345
  removeFor(container = document.body, selector) {
291
- const keys = [
292
- 'data-moonwalk',
293
- 'data-moonwalk-section',
294
- 'data-moonwalk-children',
295
- ]
296
- keys.forEach((key) => {
346
+ MOONWALK_ATTRS.forEach((key) => {
297
347
  const elems = container.querySelectorAll(`${selector}[${key}]`)
298
348
  Array.from(elems).forEach((el) => el.removeAttribute(key))
299
349
  })
@@ -324,10 +374,7 @@ export default class Moonwalk {
324
374
  */
325
375
  addIds(section) {
326
376
  Array.from(section.querySelectorAll('[data-moonwalk]')).forEach((el) => {
327
- el.setAttribute(
328
- 'data-moonwalk-id',
329
- Math.random().toString(36).substring(7)
330
- )
377
+ el.setAttribute('data-moonwalk-id', `mw-${++_idCounter}`)
331
378
  })
332
379
  }
333
380
 
@@ -345,7 +392,7 @@ export default class Moonwalk {
345
392
  Array.from(elements).forEach((element, index) => {
346
393
  element.setAttribute('data-moonwalk-idx', index + 1)
347
394
  })
348
- }, this)
395
+ })
349
396
  }
350
397
 
351
398
  /**
@@ -355,25 +402,27 @@ export default class Moonwalk {
355
402
  initializeRuns(container = document.body) {
356
403
  const runs = container.querySelectorAll('[data-moonwalk-run]')
357
404
  return Array.from(runs).map((run) => {
358
- const foundRun = this.opts.runs[run.getAttribute('data-moonwalk-run')]
359
- if (foundRun) {
360
- if (foundRun.initialize) {
361
- foundRun.initialize(run)
362
- }
363
- return {
364
- el: run,
365
- threshold: foundRun.threshold || 0,
366
- initialize: foundRun.initialize,
367
- onReady: foundRun.onReady,
368
- callback: foundRun.callback,
369
- onExit: foundRun.onExit,
370
- repeated: foundRun.repeated,
371
- rootMargin: foundRun.rootMargin,
372
- }
405
+ const runName = run.getAttribute('data-moonwalk-run')
406
+ const foundRun = this.opts.runs[runName]
407
+ if (!foundRun) {
408
+ console.warn(`==> JUPITER/MOONWALK: Unknown run "${runName}" — not found in opts.runs`)
409
+ return null
373
410
  }
374
411
 
375
- return null
376
- })
412
+ if (foundRun.initialize) {
413
+ foundRun.initialize(run)
414
+ }
415
+ return {
416
+ el: run,
417
+ threshold: foundRun.threshold || 0,
418
+ initialize: foundRun.initialize,
419
+ onReady: foundRun.onReady,
420
+ callback: foundRun.callback,
421
+ onExit: foundRun.onExit,
422
+ repeated: foundRun.repeated,
423
+ rootMargin: foundRun.rootMargin,
424
+ }
425
+ }).filter(Boolean)
377
426
  }
378
427
 
379
428
  /**
@@ -408,7 +457,7 @@ export default class Moonwalk {
408
457
  }
409
458
 
410
459
  return {
411
- id: Math.random().toString(36).substring(7),
460
+ id: `mw-${++_idCounter}`,
412
461
  el: section,
413
462
  name: section.getAttribute('data-moonwalk-section') || null,
414
463
  animation: {
@@ -464,7 +513,7 @@ export default class Moonwalk {
464
513
  setAttrs(element, val) {
465
514
  const affectedElements = []
466
515
 
467
- Array.prototype.forEach.call(element.children, (c) => {
516
+ Array.from(element.children).forEach((c) => {
468
517
  c.setAttribute('data-moonwalk', val)
469
518
  affectedElements.push(c)
470
519
  })
@@ -548,6 +597,8 @@ export default class Moonwalk {
548
597
  }
549
598
 
550
599
  const observer = this.sectionObserver(section)
600
+ section.observer = observer
601
+ this._observers.push(observer)
551
602
  observer.observe(section.el)
552
603
  }
553
604
 
@@ -614,16 +665,7 @@ export default class Moonwalk {
614
665
  })
615
666
  } else {
616
667
  // JS animation mode
617
- if (typeof tween.alphaTween === 'object') {
618
- tween.alphaTween.duration = tween.alphaTween.duration
619
- ? tween.alphaTween.duration
620
- : tween.duration
621
- } else if (tween.alphaTween === true) {
622
- tween.alphaTween = {
623
- duration: tween.duration,
624
- ease: 'easeIn',
625
- }
626
- }
668
+ const resolvedAlpha = normalizeAlphaTween(tween.alphaTween, tween.duration)
627
669
 
628
670
  // Extract ease from to values and convert for Motion.js
629
671
  const { ease: tweenEase, ...toValues } = tween.transition.to
@@ -646,13 +688,13 @@ export default class Moonwalk {
646
688
 
647
689
  animate(section.children, toValues, animationOptions)
648
690
 
649
- if (tween.alphaTween) {
691
+ if (resolvedAlpha) {
650
692
  animate(
651
693
  section.children,
652
694
  { opacity: 1 },
653
695
  {
654
- duration: tween.alphaTween.duration,
655
- ease: convertEasing(tween.alphaTween.ease || 'easeIn'),
696
+ duration: resolvedAlpha.duration,
697
+ ease: convertEasing(resolvedAlpha.ease || 'easeIn'),
656
698
  delay: stagger(tween.interval, {
657
699
  startDelay: tween.startDelay || 0,
658
700
  }),
@@ -666,7 +708,7 @@ export default class Moonwalk {
666
708
  }
667
709
  }
668
710
  },
669
- { rootMargin: opts.rootMargin }
711
+ { rootMargin: opts.rootMargin, threshold: opts.threshold }
670
712
  )
671
713
  }
672
714
 
@@ -680,7 +722,7 @@ export default class Moonwalk {
680
722
  const orderA = a.getAttribute('data-moonwalk-order')
681
723
  ? parseInt(a.getAttribute('data-moonwalk-order'))
682
724
  : null
683
- const orderB = a.getAttribute('data-moonwalk-order')
725
+ const orderB = b.getAttribute('data-moonwalk-order')
684
726
  ? parseInt(b.getAttribute('data-moonwalk-order'))
685
727
  : null
686
728
 
@@ -758,11 +800,41 @@ export default class Moonwalk {
758
800
  })
759
801
  }
760
802
 
803
+ destroy() {
804
+ if (this.opts.on && typeof this.opts.on === 'string' && this._boundOnReady) {
805
+ window.removeEventListener(this.opts.on, this._boundOnReady)
806
+ this._boundOnReady = null
807
+ }
808
+
809
+ if (this._observers) {
810
+ this._observers.forEach(obs => obs.disconnect())
811
+ this._observers = []
812
+ }
813
+
814
+ if (this.sections) {
815
+ this.sections.forEach(section => {
816
+ section.el = null
817
+ section.elements = []
818
+ section.children = null
819
+ section.observer = null
820
+ })
821
+ this.sections = []
822
+ }
823
+
824
+ if (this.runs) {
825
+ this.runs.forEach(run => {
826
+ run.el = null
827
+ run.observer = null
828
+ })
829
+ this.runs = []
830
+ }
831
+ }
832
+
761
833
  onReady() {
762
834
  if (this.opts.initialDelay) {
763
835
  setTimeout(() => {
764
836
  this.ready()
765
- }, this.opts.initialDelay)
837
+ }, this.opts.initialDelay * 1000)
766
838
  } else {
767
839
  this.ready()
768
840
  }
@@ -778,7 +850,7 @@ export default class Moonwalk {
778
850
  // Execute onReady callbacks for all runs
779
851
  for (let idx = 0; idx < this.runs.length; idx += 1) {
780
852
  const run = this.runs[idx]
781
- if (run && run.onReady) {
853
+ if (run.onReady) {
782
854
  run.onReady(run.el)
783
855
  }
784
856
  }
@@ -786,14 +858,10 @@ export default class Moonwalk {
786
858
  for (let idx = 0; idx < this.runs.length; idx += 1) {
787
859
  const run = this.runs[idx]
788
860
 
789
- if (!run) {
790
- return
791
- }
792
-
793
861
  // if this is the last section, set rootMargin to 0
794
862
  let rootMargin
795
863
 
796
- if (idx === this.sections.length - 1) {
864
+ if (idx === this.runs.length - 1) {
797
865
  rootMargin = '0px'
798
866
  } else {
799
867
  if (run.rootMargin) {
@@ -804,6 +872,8 @@ export default class Moonwalk {
804
872
  }
805
873
 
806
874
  const runObserver = this.runObserver(run, rootMargin)
875
+ run.observer = runObserver
876
+ this._observers.push(runObserver)
807
877
  runObserver.observe(run.el)
808
878
  }
809
879
 
@@ -822,6 +892,7 @@ export default class Moonwalk {
822
892
 
823
893
  if (!section.name) {
824
894
  section.observer = this.observer(section, rootMargin)
895
+ this._observers.push(section.observer)
825
896
  }
826
897
 
827
898
  section.elements = section.el.querySelectorAll('[data-moonwalk]')
@@ -851,6 +922,46 @@ export default class Moonwalk {
851
922
  }
852
923
  }
853
924
 
925
+ /**
926
+ * Get the viewport entry direction based on current scroll direction.
927
+ * When entering, elements appear from the opposite side of scroll direction.
928
+ *
929
+ * @param {boolean} isEntry - Whether this is an entry (true) or exit (false)
930
+ * @returns {string|null}
931
+ */
932
+ getScrollDirection(isEntry) {
933
+ if (!this.app.state || !this.app.state.scrollDirection) {
934
+ return null
935
+ }
936
+
937
+ const entryMap = { down: 'bottom', up: 'top', right: 'left', left: 'right' }
938
+ const exitMap = { down: 'top', up: 'bottom', right: 'right', left: 'left' }
939
+ const map = isEntry ? entryMap : exitMap
940
+
941
+ return map[this.app.state.scrollDirection] || null
942
+ }
943
+
944
+ /**
945
+ * Get the exit direction for an element, falling back to position-based
946
+ * detection when scroll direction is unavailable.
947
+ *
948
+ * @param {IntersectionObserverEntry} entry
949
+ * @returns {string|null}
950
+ */
951
+ getExitDirection(entry) {
952
+ const scrollDir = this.getScrollDirection(false)
953
+ if (scrollDir) {
954
+ return scrollDir
955
+ }
956
+
957
+ const { boundingClientRect: rect } = entry
958
+ if (rect.bottom <= 0) return 'top'
959
+ if (rect.top >= window.innerHeight) return 'bottom'
960
+ if (rect.right <= 0) return 'left'
961
+ if (rect.left >= window.innerWidth) return 'right'
962
+ return null
963
+ }
964
+
854
965
  /**
855
966
  * Creates and returns the RUN observer for data-moonwalk-run elements
856
967
  *
@@ -858,52 +969,14 @@ export default class Moonwalk {
858
969
  * @param {*} rootMargin
859
970
  */
860
971
  runObserver(run, rootMargin) {
861
- // Store the previous positions of observed elements to compare for exit direction
862
- const elementPositions = new WeakMap()
863
-
864
972
  return new IntersectionObserver(
865
973
  (entries, self) => {
866
974
  for (let i = 0; i < entries.length; i += 1) {
867
975
  const entry = entries[i]
868
976
 
869
- // Store the element's current position in the viewport
870
- const boundingRect = entry.boundingClientRect
871
- const viewportHeight = window.innerHeight
872
- const viewportWidth = window.innerWidth
873
-
874
977
  if (entry.isIntersecting && run.callback) {
875
- // Calculate entry direction
876
- let meta = { direction: null }
877
-
878
- // Use the app's scroll direction for reliable detection
879
- // If scrollDirection is null, the element was likely revealed on initial load
880
- if (this.app.state && this.app.state.scrollDirection) {
881
- // Map scroll direction to viewport entry direction
882
- switch (this.app.state.scrollDirection) {
883
- case 'down':
884
- meta.direction = 'bottom' // When scrolling down, elements enter from bottom
885
- break
886
- case 'up':
887
- meta.direction = 'top' // When scrolling up, elements enter from top
888
- break
889
- case 'right':
890
- meta.direction = 'left' // When scrolling right, elements enter from left
891
- break
892
- case 'left':
893
- meta.direction = 'right' // When scrolling left, elements enter from right
894
- break
895
- }
896
- }
897
- // If no scroll direction is available, direction remains null
898
-
899
- // Store the element's position when it enters the viewport
900
- elementPositions.set(entry.target, {
901
- top: boundingRect.top,
902
- bottom: boundingRect.bottom,
903
- left: boundingRect.left,
904
- right: boundingRect.right
905
- })
906
-
978
+ const meta = { direction: this.getScrollDirection(true) }
979
+
907
980
  const runRepeated = entry.target.hasAttribute(
908
981
  'data-moonwalk-run-triggered'
909
982
  )
@@ -921,40 +994,8 @@ export default class Moonwalk {
921
994
  'data-moonwalk-run-exit-triggered'
922
995
  )
923
996
  entry.target.setAttribute('data-moonwalk-run-exit-triggered', '')
924
-
925
- // Calculate exit direction
926
- let meta = { direction: null }
927
-
928
- // Use the app's scroll direction for reliable detection
929
- // For exit direction, it's the opposite of the entry direction for the same scroll
930
- if (this.app.state && this.app.state.scrollDirection) {
931
- // Map scroll direction to viewport exit direction
932
- switch (this.app.state.scrollDirection) {
933
- case 'down':
934
- meta.direction = 'top' // When scrolling down, elements exit from top
935
- break
936
- case 'up':
937
- meta.direction = 'bottom' // When scrolling up, elements exit from bottom
938
- break
939
- case 'right':
940
- meta.direction = 'right' // When scrolling right, elements exit from right
941
- break
942
- case 'left':
943
- meta.direction = 'left' // When scrolling left, elements exit from left
944
- break
945
- }
946
- } else {
947
- // If no scroll direction is available, use the simplest position-based check
948
- if (boundingRect.bottom <= 0) {
949
- meta.direction = 'top'
950
- } else if (boundingRect.top >= viewportHeight) {
951
- meta.direction = 'bottom'
952
- } else if (boundingRect.right <= 0) {
953
- meta.direction = 'left'
954
- } else if (boundingRect.left >= viewportWidth) {
955
- meta.direction = 'right'
956
- }
957
- }
997
+
998
+ const meta = { direction: this.getExitDirection(entry) }
958
999
 
959
1000
  run.onExit(entry.target, runExited, meta)
960
1001
  if (!run.repeated) {
@@ -987,8 +1028,6 @@ export default class Moonwalk {
987
1028
  const entry = entries[i]
988
1029
 
989
1030
  if (entry.isIntersecting || entry.intersectionRatio > 0) {
990
- section.running = true
991
-
992
1031
  const walkName = entry.target.getAttribute('data-moonwalk')
993
1032
  const targetId =
994
1033
  entry.target.getAttribute('data-testid') ||
@@ -1009,25 +1048,14 @@ export default class Moonwalk {
1009
1048
  // Default interval to 0.15 if not specified (same as default walk)
1010
1049
  const interval = cfg.interval !== undefined ? cfg.interval : 0.15
1011
1050
 
1012
- let { alphaTween } = cfg
1013
- let overlap = (duration - interval) * -1 // flip it
1051
+ const alphaTween = normalizeAlphaTween(cfg.alphaTween, duration)
1052
+ let overlap = interval - duration
1014
1053
 
1015
1054
  if (section.stage.firstTween) {
1016
1055
  overlap = 0
1017
1056
  section.stage.firstTween = false
1018
1057
  }
1019
1058
 
1020
- if (typeof alphaTween === 'object' && alphaTween !== null) {
1021
- alphaTween.duration = alphaTween.duration
1022
- ? alphaTween.duration
1023
- : duration
1024
- } else if (alphaTween === true) {
1025
- alphaTween = {
1026
- duration,
1027
- ease: 'easeIn',
1028
- }
1029
- }
1030
-
1031
1059
  const tweenFn = () => {
1032
1060
  if (transition) {
1033
1061
  this.tweenJS(
@@ -1104,6 +1132,7 @@ export default class Moonwalk {
1104
1132
  * @param {*} section
1105
1133
  * @param {*} target
1106
1134
  * @param {*} tweenDuration
1135
+ * @param {*} tweenInterval
1107
1136
  * @param {*} tweenTransition
1108
1137
  * @param {*} tweenOverlap
1109
1138
  * @param {*} alphaTween
@@ -1208,9 +1237,10 @@ export default class Moonwalk {
1208
1237
  *
1209
1238
  * @param {*} section
1210
1239
  * @param {*} target
1211
- * @param {*} duration
1212
- * @param {*} transition
1213
- * @param {*} overlap
1240
+ * @param {*} tweenDuration
1241
+ * @param {*} tweenInterval
1242
+ * @param {*} tweenTransition
1243
+ * @param {*} tweenOverlap
1214
1244
  */
1215
1245
  tweenCSS(
1216
1246
  section,
@@ -1,5 +1,5 @@
1
1
  const DEFAULT_FPS = 60
2
- const SCOPES = {}
2
+ const SCOPES = new Map()
3
3
 
4
4
  export default (callback, fps = DEFAULT_FPS) =>
5
5
  (...passedArgs) =>
@@ -7,12 +7,11 @@ export default (callback, fps = DEFAULT_FPS) =>
7
7
  const msCurrent = new Date().getTime()
8
8
  const fpsInterval = 1000 / fps
9
9
 
10
- SCOPES[callback] = SCOPES[callback] || null
11
-
12
- const msDelta = SCOPES[callback] ? msCurrent - SCOPES[callback] : null
10
+ const msLast = SCOPES.get(callback) || null
11
+ const msDelta = msLast ? msCurrent - msLast : null
13
12
 
14
13
  if (msDelta === null || msDelta > fpsInterval) {
15
- SCOPES[callback] = msCurrent - (msDelta % fpsInterval)
14
+ SCOPES.set(callback, msCurrent - (msDelta % fpsInterval))
16
15
  callback(...passedArgs)
17
16
  }
18
17
  })
@@ -3,8 +3,8 @@ export default class Application {
3
3
  debugType: number;
4
4
  debugOverlay: Element;
5
5
  userAgent: string;
6
- _lastWindowHeight: number;
7
6
  breakpoint: any;
7
+ root: HTMLElement;
8
8
  language: string;
9
9
  size: {
10
10
  width: number;
@@ -28,19 +28,19 @@ export default class Application {
28
28
  };
29
29
  opts: any;
30
30
  focusableSelectors: any;
31
+ browser: any;
31
32
  featureTests: any;
32
33
  breakpoints: any;
33
34
  fontLoader: any;
34
- fader: any;
35
35
  callbacks: {};
36
36
  SCROLL_LOCKED: boolean;
37
37
  SCROLLBAR_WIDTH: number;
38
38
  INITIALIZED: boolean;
39
39
  PREFERS_REDUCED_MOTION: boolean;
40
- beforeInitializedEvent: CustomEvent<any>;
41
- initializedEvent: CustomEvent<any>;
42
- readyEvent: CustomEvent<any>;
43
- revealedEvent: CustomEvent<any>;
40
+ beforeInitializedEvent: CustomEvent<this>;
41
+ initializedEvent: CustomEvent<this>;
42
+ readyEvent: CustomEvent<this>;
43
+ revealedEvent: CustomEvent<this>;
44
44
  /**
45
45
  * Main init. Called from client application on DOMReady.
46
46
  */
@@ -110,7 +110,6 @@ export default class Application {
110
110
  * Ugly hacks
111
111
  */
112
112
  hacks(): void;
113
- getIOSCurrentInnerHeight(): number;
114
113
  getIOSInnerHeightMax(): number;
115
114
  /**
116
115
  * Event emitters
@@ -144,7 +143,7 @@ export default class Application {
144
143
  onScroll(e: any): void;
145
144
  onVisibilityChange(e: any): void;
146
145
  pollForElement(selector: any, time?: number, callback?: () => void): void;
147
- pollForVar(variable: any, time?: number, callback?: () => void): void;
146
+ pollForVar(getter: any, time?: number, callback?: () => void): void;
148
147
  setupDebug(): void;
149
148
  toggleDebug(): void;
150
149
  /**