@brandocms/jupiter 3.47.0 → 3.48.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.
Files changed (37) hide show
  1. package/package.json +9 -9
  2. package/src/events/index.js +1 -0
  3. package/src/index.js +5 -7
  4. package/src/modules/Application/index.js +114 -77
  5. package/src/modules/Breakpoints/index.js +14 -17
  6. package/src/modules/Cookies/index.js +86 -43
  7. package/src/modules/CoverOverlay/index.js +6 -3
  8. package/src/modules/Dataloader/index.js +173 -7
  9. package/src/modules/Dom/index.js +26 -26
  10. package/src/modules/Dropdown/index.js +14 -14
  11. package/src/modules/EqualHeightElements/index.js +70 -0
  12. package/src/modules/EqualHeightImages/index.js +17 -18
  13. package/src/modules/FeatureTests/index.js +22 -15
  14. package/src/modules/FixedHeader/index.js +79 -75
  15. package/src/modules/Fontloader/index.js +5 -3
  16. package/src/modules/FooterReveal/index.js +1 -1
  17. package/src/modules/HeroSlider/index.js +33 -23
  18. package/src/modules/HeroVideo/index.js +15 -13
  19. package/src/modules/Lazyload/index.js +119 -65
  20. package/src/modules/Lightbox/index.js +40 -43
  21. package/src/modules/Links/index.js +8 -7
  22. package/src/modules/Marquee/index.js +40 -34
  23. package/src/modules/MobileMenu/index.js +112 -65
  24. package/src/modules/Moonwalk/index.js +256 -203
  25. package/src/modules/Parallax/index.js +24 -14
  26. package/src/modules/Popup/index.js +24 -21
  27. package/src/modules/ScrollSpy/index.js +5 -5
  28. package/src/modules/StackedBoxes/index.js +5 -5
  29. package/src/modules/StickyHeader/index.js +73 -70
  30. package/src/modules/Toggler/index.js +2 -2
  31. package/src/modules/Typography/index.js +13 -10
  32. package/src/utils/dispatchElementEvent.js +2 -2
  33. package/src/utils/imageIsLoaded.js +1 -1
  34. package/src/utils/imagesAreLoaded.js +3 -3
  35. package/src/utils/loadScript.js +9 -8
  36. package/src/utils/rafCallback.js +12 -10
  37. package/src/utils/zoom.js +11 -8
@@ -52,7 +52,9 @@ const DEFAULT_OPTIONS = {
52
52
  gsap.to(hs.el, {
53
53
  duration: 0.25,
54
54
  opacity: 1,
55
- onComplete: () => { callback() }
55
+ onComplete: () => {
56
+ callback()
57
+ }
56
58
  })
57
59
  } else {
58
60
  gsap.to(hs.el, {
@@ -64,7 +66,7 @@ const DEFAULT_OPTIONS = {
64
66
  }
65
67
 
66
68
  export default class HeroSlider {
67
- constructor (app, opts = {}) {
69
+ constructor(app, opts = {}) {
68
70
  this.app = app
69
71
  this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
70
72
 
@@ -81,7 +83,7 @@ export default class HeroSlider {
81
83
  this.initialize()
82
84
  }
83
85
 
84
- initialize () {
86
+ initialize() {
85
87
  this._addResizeHandler()
86
88
  // style the container
87
89
  gsap.set(this.el, {
@@ -132,9 +134,9 @@ export default class HeroSlider {
132
134
 
133
135
  this.opts.onInitialize(this)
134
136
 
135
- const callback = (this.slides.length > 1) ? this.next.bind(this) : () => {}
137
+ const callback = this.slides.length > 1 ? this.next.bind(this) : () => {}
136
138
 
137
- window.addEventListener(Events.APPLICATION_REVEALED, () => {
139
+ this.app.registerCallback(Events.APPLICATION_REVEALED, () => {
138
140
  /* Wait for the first image to load, then fade in container element */
139
141
  const firstImg = this.slides[this._currentSlideIdx].querySelector('img')
140
142
 
@@ -149,7 +151,7 @@ export default class HeroSlider {
149
151
  /**
150
152
  * Calculate which slide is next, and call the slide function
151
153
  */
152
- next () {
154
+ next() {
153
155
  if (prefersReducedMotion() && this.app.opts.respectReducedMotion) {
154
156
  return
155
157
  }
@@ -163,7 +165,7 @@ export default class HeroSlider {
163
165
  this._previousSlide = this.slides[this._currentSlideIdx]
164
166
  this._currentSlideIdx += 1
165
167
  if (this._currentSlideIdx === this.slideCount) {
166
- [this._nextSlide] = this.slides
168
+ ;[this._nextSlide] = this.slides
167
169
  } else {
168
170
  this._nextSlide = this.slides[this._currentSlideIdx + 1]
169
171
  }
@@ -177,7 +179,7 @@ export default class HeroSlider {
177
179
  /**
178
180
  * Switches between slides
179
181
  */
180
- slide (type) {
182
+ slide(type) {
181
183
  const timeline = gsap.timeline()
182
184
 
183
185
  switch (type) {
@@ -205,12 +207,16 @@ export default class HeroSlider {
205
207
  .set(this._previousSlide, {
206
208
  opacity: 0
207
209
  })
208
- .call(() => {
209
- this._nextSlide.style.zIndex = this.opts.zIndex.visible
210
- this._currentSlide.style.zIndex = this.opts.zIndex.regular
211
- this._previousSlide.style.zIndex = this.opts.zIndex.regular
212
- this.next()
213
- }, null, this)
210
+ .call(
211
+ () => {
212
+ this._nextSlide.style.zIndex = this.opts.zIndex.visible
213
+ this._currentSlide.style.zIndex = this.opts.zIndex.regular
214
+ this._previousSlide.style.zIndex = this.opts.zIndex.regular
215
+ this.next()
216
+ },
217
+ null,
218
+ this
219
+ )
214
220
 
215
221
  break
216
222
 
@@ -221,13 +227,17 @@ export default class HeroSlider {
221
227
  scale: 1.0,
222
228
  width: '100%'
223
229
  })
224
- .fromTo(this._previousSlide, {
225
- duration: this.opts.interval,
226
- overflow: 'hidden'
227
- }, {
228
- duration: this.opts.interval,
229
- scale: this.opts.transition.scale
230
- })
230
+ .fromTo(
231
+ this._previousSlide,
232
+ {
233
+ duration: this.opts.interval,
234
+ overflow: 'hidden'
235
+ },
236
+ {
237
+ duration: this.opts.interval,
238
+ scale: this.opts.transition.scale
239
+ }
240
+ )
231
241
  .to(this._previousSlide, {
232
242
  duration: this.opts.transition.duration,
233
243
  width: 0,
@@ -261,7 +271,7 @@ export default class HeroSlider {
261
271
  /**
262
272
  * Add a window resize handler that resizes slide widths
263
273
  */
264
- _addResizeHandler () {
274
+ _addResizeHandler() {
265
275
  this.observer = new IntersectionObserver(entries => {
266
276
  const [{ isIntersecting }] = entries
267
277
  if (isIntersecting) {
@@ -275,7 +285,7 @@ export default class HeroSlider {
275
285
  this.observer.observe(this.el)
276
286
  }
277
287
 
278
- _resizeSlides () {
288
+ _resizeSlides() {
279
289
  gsap.to(this.images, {
280
290
  duration: 0.15,
281
291
  width: document.body.clientWidth,
@@ -62,7 +62,7 @@ const DEFAULT_OPTIONS = {
62
62
  }
63
63
 
64
64
  export default class HeroVideo {
65
- constructor (app, opts = {}) {
65
+ constructor(app, opts = {}) {
66
66
  this.app = app
67
67
  this.booting = true
68
68
  this.playing = false
@@ -83,7 +83,7 @@ export default class HeroVideo {
83
83
  this.initialize()
84
84
  }
85
85
 
86
- initialize () {
86
+ initialize() {
87
87
  this._addResizeHandler()
88
88
  // style the container
89
89
  gsap.set(this.el, {
@@ -163,21 +163,23 @@ export default class HeroVideo {
163
163
  })
164
164
  }
165
165
 
166
- setSrc () {
166
+ setSrc() {
167
167
  const dataSrc = this.video.getAttribute('data-src')
168
168
  if (!dataSrc) {
169
169
  return
170
170
  }
171
171
  const dataObj = JSON.parse(dataSrc)
172
- if (this.app.breakpoints.mediaQueries.iphone.matches
173
- || this.app.breakpoints.mediaQueries.mobile.matches) {
172
+ if (
173
+ this.app.breakpoints.mediaQueries.iphone.matches ||
174
+ this.app.breakpoints.mediaQueries.mobile.matches
175
+ ) {
174
176
  this.video.setAttribute('src', dataObj.phone)
175
177
  } else {
176
178
  this.video.setAttribute('src', dataObj.desktop)
177
179
  }
178
180
  }
179
181
 
180
- addEvents () {
182
+ addEvents() {
181
183
  this.video.addEventListener('canplay', () => {
182
184
  if (!this.playing) {
183
185
  if (!prefersReducedMotion()) {
@@ -209,7 +211,7 @@ export default class HeroVideo {
209
211
  }
210
212
  }
211
213
 
212
- play () {
214
+ play() {
213
215
  if (this.cover) {
214
216
  this.opts.onFadeOutCover(this)
215
217
  }
@@ -217,20 +219,20 @@ export default class HeroVideo {
217
219
  this.playing = true
218
220
  }
219
221
 
220
- pause () {
222
+ pause() {
221
223
  this.video.pause()
222
224
  this.playing = false
223
225
  }
224
226
 
225
- fadeIn () {
227
+ fadeIn() {
226
228
  this.opts.onFadeIn(this)
227
229
  }
228
230
 
229
- fadeInCover () {
231
+ fadeInCover() {
230
232
  this.opts.onFadeInCover(this)
231
233
  }
232
234
 
233
- addObserver () {
235
+ addObserver() {
234
236
  const observer = new IntersectionObserver(entries => {
235
237
  const [{ isIntersecting }] = entries
236
238
  if (isIntersecting) {
@@ -252,7 +254,7 @@ export default class HeroVideo {
252
254
  /**
253
255
  * Add a window resize handler that resizes video width
254
256
  */
255
- _addResizeHandler () {
257
+ _addResizeHandler() {
256
258
  this.observer = new IntersectionObserver(entries => {
257
259
  const [{ isIntersecting }] = entries
258
260
  if (isIntersecting) {
@@ -266,7 +268,7 @@ export default class HeroVideo {
266
268
  this.observer.observe(this.el)
267
269
  }
268
270
 
269
- _resize () {
271
+ _resize() {
270
272
  gsap.to(this.video, {
271
273
  duration: 0.15,
272
274
  width: document.body.clientWidth,
@@ -1,29 +1,43 @@
1
1
  import _defaultsDeep from 'lodash.defaultsdeep'
2
- import { IMAGE_LAZYLOADED, SECTION_LAZYLOADED } from '../../events'
3
2
  import dispatchElementEvent from '../../utils/dispatchElementEvent'
4
3
  import imagesAreLoaded from '../../utils/imagesAreLoaded'
5
4
  import Dom from '../Dom'
6
5
  import * as Events from '../../events'
7
6
 
8
7
  const DEFAULT_OPTIONS = {
9
- intersectionObserverConfig: {
10
- rootMargin: '350px 0px',
8
+ revealIntersectionObserverConfig: {
9
+ rootMargin: '0px 100px 0px 100px',
10
+ threshold: 0.0
11
+ },
12
+ loadIntersectionObserverConfig: {
13
+ rootMargin: '850px 500px 850px 500px',
11
14
  threshold: 0.0
12
15
  },
13
16
  useNativeLazyloadIfAvailable: true,
14
17
  mode: 'default',
15
18
  minSize: 40,
16
- updateSizes: true
19
+ updateSizes: true,
20
+ registerCallback: true
17
21
  }
18
22
 
19
23
  export default class Lazyload {
20
- constructor (app, opts = {}) {
24
+ constructor(app, opts = {}) {
21
25
  this.app = app
22
26
  this.opts = _defaultsDeep(opts, DEFAULT_OPTIONS)
23
27
  this.initialize()
28
+
29
+ if (this.opts.registerCallback) {
30
+ this.app.registerCallback(Events.APPLICATION_REVEALED, () => {
31
+ this.watch()
32
+ })
33
+ }
34
+ }
35
+
36
+ watch() {
37
+ this.initObserver(this.revealObserver, false)
24
38
  }
25
39
 
26
- initialize () {
40
+ initialize() {
27
41
  // initialize all images that have data-sizes="auto" and set sizes="<actual width>px"
28
42
  this.initializeAutoSizes()
29
43
  // look for lazyload sections. if we find, add an observer that triggers
@@ -40,43 +54,57 @@ export default class Lazyload {
40
54
 
41
55
  const lazyPictures = document.querySelectorAll('[data-ll-srcset]')
42
56
  lazyPictures.forEach(picture => {
43
- picture.querySelectorAll('img').forEach(img => {
44
- img.setAttribute('loading', 'lazy')
45
- })
57
+ picture.querySelectorAll('img').forEach(img => img.setAttribute('loading', 'lazy'))
46
58
  this.swapPicture(picture)
47
59
  })
48
- } else {
49
- this.imgObserver = new IntersectionObserver(
50
- this.lazyloadImages.bind(this),
51
- this.opts.intersectionObserverConfig
52
- )
53
-
54
- this.lazyImages = document.querySelectorAll('[data-ll-image]')
55
- this.lazyImages.forEach((img, idx) => {
56
- img.setAttribute('data-ll-blurred', '')
57
- img.setAttribute('data-ll-idx', idx)
58
- img.style.setProperty('--ll-idx', idx)
59
- this.imgObserver.observe(img)
60
- })
61
60
 
62
- this.pictureObserver = new IntersectionObserver(
63
- this.lazyloadPictures.bind(this),
64
- this.opts.intersectionObserverConfig
65
- )
61
+ return
62
+ }
63
+
64
+ this.lazyPictures = document.querySelectorAll('[data-ll-srcset]')
65
+
66
+ this.loadObserver = new IntersectionObserver(
67
+ this.handleLoadEntries.bind(this),
68
+ this.opts.loadIntersectionObserverConfig
69
+ )
66
70
 
67
- this.lazyPictures = document.querySelectorAll('[data-ll-srcset]')
68
- this.lazyPictures.forEach((picture, idx) => {
71
+ this.revealObserver = new IntersectionObserver(
72
+ this.handleRevealEntries.bind(this),
73
+ this.opts.revealIntersectionObserverConfig
74
+ )
75
+
76
+ this.lazyPictures = document.querySelectorAll('[data-ll-srcset]')
77
+ this.initObserver(this.loadObserver)
78
+
79
+ // Deprecate data-ll-image sometime
80
+ this.imageObserver = new IntersectionObserver(
81
+ this.lazyloadImages.bind(this),
82
+ this.opts.intersectionObserverConfig
83
+ )
84
+
85
+ this.lazyImages = document.querySelectorAll('[data-ll-image]')
86
+ this.lazyImages.forEach((img, idx) => {
87
+ img.setAttribute('data-ll-blurred', '')
88
+ img.setAttribute('data-ll-idx', idx)
89
+ img.style.setProperty('--ll-idx', idx)
90
+ this.imageObserver.observe(img)
91
+ })
92
+ }
93
+
94
+ initObserver(observer, setAttrs = true) {
95
+ this.lazyPictures.forEach((picture, idx) => {
96
+ if (setAttrs) {
69
97
  picture.querySelectorAll('img:not([data-ll-loaded])').forEach(img => {
70
98
  img.setAttribute('data-ll-blurred', '')
71
99
  img.setAttribute('data-ll-idx', idx)
72
100
  img.style.setProperty('--ll-idx', idx)
73
101
  })
74
- this.pictureObserver.observe(picture)
75
- })
76
- }
102
+ }
103
+ observer.observe(picture)
104
+ })
77
105
  }
78
106
 
79
- initializeAutoSizes () {
107
+ initializeAutoSizes() {
80
108
  if (this.opts.updateSizes) {
81
109
  this.$autoSizesImages = Dom.all('[data-sizes="auto"]')
82
110
  this.autoSizes()
@@ -87,17 +115,19 @@ export default class Lazyload {
87
115
  /**
88
116
  * Set sizes attribute for all imgs with `data-sizes="auto"` and source within the <picture>
89
117
  */
90
- autoSizes () {
118
+ autoSizes() {
91
119
  Array.from(this.$autoSizesImages).forEach(img => {
92
120
  const width = this.getWidth(img)
93
121
  img.setAttribute('sizes', `${width}px`)
94
122
  if (img.parentNode) {
95
- Array.from(Dom.all(img.parentNode, 'source')).forEach(source => source.setAttribute('sizes', `${width}px`))
123
+ Array.from(Dom.all(img.parentNode, 'source')).forEach(source =>
124
+ source.setAttribute('sizes', `${width}px`)
125
+ )
96
126
  }
97
127
  })
98
128
  }
99
129
 
100
- getWidth (img) {
130
+ getWidth(img) {
101
131
  let width = img.offsetWidth
102
132
  let parent = img.parentNode
103
133
 
@@ -109,7 +139,7 @@ export default class Lazyload {
109
139
  return width
110
140
  }
111
141
 
112
- initializeSections () {
142
+ initializeSections() {
113
143
  const sections = document.querySelectorAll('[data-lazyload-section]')
114
144
  if (sections) {
115
145
  const sectionObserver = (section, children) => {
@@ -118,17 +148,16 @@ export default class Lazyload {
118
148
  entries.forEach(entry => {
119
149
  if (entry.isIntersecting || entry.intersectionRatio > 0) {
120
150
  imagesAreLoaded(imagesInSection, true).then(() => {
121
- dispatchElementEvent(section, SECTION_LAZYLOADED)
151
+ dispatchElementEvent(section, Events.SECTION_LAZYLOADED)
122
152
  })
123
153
  children.forEach(picture => {
124
- this.swapPicture(picture)
125
- this.pictureObserver.unobserve(picture)
154
+ this.loadPicture(picture)
155
+ this.loadObserver.unobserve(picture)
126
156
  })
127
157
  self.unobserve(section)
128
158
  }
129
159
  })
130
- },
131
- this.opts.intersectionObserverConfig)
160
+ }, this.opts.intersectionObserverConfig)
132
161
  }
133
162
 
134
163
  sections.forEach(section => {
@@ -139,44 +168,41 @@ export default class Lazyload {
139
168
  }
140
169
  }
141
170
 
142
- lazyloadImages (elements) {
171
+ // we load the picture a ways before it enters the viewport
172
+ handleLoadEntries(elements) {
143
173
  elements.forEach(item => {
144
174
  if (item.isIntersecting || item.intersectionRatio > 0) {
145
- const image = item.target
146
- this.swapImage(image)
147
- this.imgObserver.unobserve(image)
175
+ const picture = item.target
176
+ this.loadPicture(picture)
177
+ this.loadObserver.unobserve(item.target)
148
178
  }
149
179
  })
150
180
  }
151
181
 
152
- lazyloadPictures (elements) {
182
+ // we reveal the picture when it enters the viewport
183
+ handleRevealEntries(elements) {
153
184
  elements.forEach(item => {
154
185
  if (item.isIntersecting || item.intersectionRatio > 0) {
155
186
  const picture = item.target
156
- this.swapPicture(picture)
157
- this.pictureObserver.unobserve(picture)
187
+ this.revealPicture(picture)
188
+ this.revealObserver.unobserve(item.target)
158
189
  }
159
190
  })
160
191
  }
161
192
 
162
- swapImage (image) {
163
- image.src = image.dataset.src
164
- image.setAttribute('data-ll-loaded', '')
165
- }
166
-
167
- swapPicture (picture) {
193
+ loadPicture(picture) {
168
194
  // gather all the source elements in picture
169
195
  const sources = picture.querySelectorAll('source')
170
196
  let loadedSomething = false
171
197
 
172
198
  for (let s = 0; s < sources.length; s += 1) {
173
199
  const source = sources[s]
174
- if (!source.hasAttribute('data-ll-loaded')) {
200
+ if (!source.hasAttribute('data-ll-ready')) {
175
201
  loadedSomething = true
176
202
  }
177
203
  if (source.hasAttribute('data-srcset')) {
178
204
  source.setAttribute('srcset', source.dataset.srcset)
179
- source.setAttribute('data-ll-loaded', '')
205
+ source.setAttribute('data-ll-ready', '')
180
206
  }
181
207
  }
182
208
 
@@ -187,24 +213,25 @@ export default class Lazyload {
187
213
  const img = picture.querySelector('img')
188
214
 
189
215
  const onload = () => {
190
- if (!img.getAttribute('data-ll-loaded') && this.app.browser === 'firefox') {
191
- // set sizes attribute on load again,
192
- // since firefox sometimes is a bit slow to
216
+ if (!img.getAttribute('data-ll-ready') && this.app.browser === 'firefox') {
217
+ // set sizes attribute on load again,
218
+ // since firefox sometimes is a bit slow to
193
219
  // get the actual image width
194
220
  const width = this.getWidth(img)
195
221
 
196
222
  img.setAttribute('sizes', `${width}px`)
197
223
  if (img.parentNode) {
198
- Array.from(Dom.all(img.parentNode, 'source'))
199
- .forEach(source => source.setAttribute('sizes', `${width}px`))
224
+ Array.from(Dom.all(img.parentNode, 'source')).forEach(source =>
225
+ source.setAttribute('sizes', `${width}px`)
226
+ )
200
227
  }
201
228
  }
202
229
 
203
230
  img.removeAttribute('data-ll-placeholder')
204
231
  img.removeAttribute('data-ll-blurred')
205
232
  img.removeAttribute('data-ll-loading')
206
- img.setAttribute('data-ll-loaded', '')
207
- picture.setAttribute('data-ll-srcset-loaded', '')
233
+ img.setAttribute('data-ll-ready', '')
234
+ picture.setAttribute('data-ll-srcset-ready', '')
208
235
  }
209
236
 
210
237
  img.addEventListener('load', onload, false)
@@ -225,8 +252,35 @@ export default class Lazyload {
225
252
  }
226
253
 
227
254
  // safari sometimes caches, so force load
228
- if (img.complete) { onload() }
255
+ if (img.complete) {
256
+ onload()
257
+ }
258
+
259
+ dispatchElementEvent(img, Events.IMAGE_LAZYLOADED)
260
+ }
261
+
262
+ /* reveal by just setting `data-ll-loaded` */
263
+ revealPicture(picture) {
264
+ const img = picture.querySelector('img')
265
+ if (img.hasAttribute('data-ll-loaded')) {
266
+ return
267
+ }
268
+ img.setAttribute('data-ll-loaded', '')
269
+ dispatchElementEvent(img, Events.IMAGE_REVEALED)
270
+ }
271
+
272
+ lazyloadImages(elements) {
273
+ elements.forEach(item => {
274
+ if (item.isIntersecting || item.intersectionRatio > 0) {
275
+ const image = item.target
276
+ this.swapImage(image)
277
+ this.imageObserver.unobserve(image)
278
+ }
279
+ })
280
+ }
229
281
 
230
- dispatchElementEvent(img, IMAGE_LAZYLOADED)
282
+ swapImage(image) {
283
+ image.src = image.dataset.src
284
+ image.setAttribute('data-ll-loaded', '')
231
285
  }
232
286
  }