PageSourceSearch

https://www.blankfeldgroup.com/wp-content/themes/universal/lib/js/jarallax/jarallax.js?ver=24.4.08

js blankfeldgroup.com collected 2026-10-04 21:25:40 UTC 23,587 bytes, 759 lines download raw bytes

1/*!
2 * Jarallax v2.0.4 (https://github.com/nk-o/jarallax)
3 * Copyright 2022 nK <https://nkdev.info>
4 * Licensed under MIT (https://github.com/nk-o/jarallax/blob/master/LICENSE)
5 */
6(function (global, factory) {
7  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8  typeof define === 'function' && define.amd ? define(factory) :
9  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.jarallax = factory());
10})(this, (function () { 'use strict';
11
12  function ready(callback) {
13    if ('complete' === document.readyState || 'interactive' === document.readyState) {
14      // Already ready or interactive, execute callback
15      callback();
16    } else {
17      document.addEventListener('DOMContentLoaded', callback, {
18        capture: true,
19        once: true,
20        passive: true
21      });
22    }
23  }
24
25  /* eslint-disable import/no-mutable-exports */
26
27  /* eslint-disable no-restricted-globals */
28  let win;
29
30  if ('undefined' !== typeof window) {
31    win = window;
32  } else if ('undefined' !== typeof global) {
33    win = global;
34  } else if ('undefined' !== typeof self) {
35    win = self;
36  } else {
37    win = {};
38  }
39
40  var global$1 = win;
41
42  const {
43    navigator
44  } = global$1;
45  const isMobile = /*#__PURE__*/ /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
46  let $deviceHelper;
47  /**
48   * The most popular mobile browsers changes height after page scroll and this generates image jumping.
49   * We can fix it using this workaround with vh units.
50   */
51
52  function getDeviceHeight() {
53    if (!$deviceHelper && document.body) {
54      $deviceHelper = document.createElement('div');
55      $deviceHelper.style.cssText = 'position: fixed; top: -9999px; left: 0; height: 100vh; width: 0;';
56      document.body.appendChild($deviceHelper);
57    }
58
59    return ($deviceHelper ? $deviceHelper.clientHeight : 0) || global$1.innerHeight || document.documentElement.clientHeight;
60  } // Window height data
61
62
63  let wndH;
64
65  function updateWndVars() {
66    if (isMobile) {
67      wndH = getDeviceHeight();
68    } else {
69      wndH = global$1.innerHeight || document.documentElement.clientHeight;
70    }
71  }
72
73  updateWndVars();
74  global$1.addEventListener('resize', updateWndVars);
75  global$1.addEventListener('orientationchange', updateWndVars);
76  global$1.addEventListener('load', updateWndVars);
77  ready(() => {
78    updateWndVars();
79  }); // list with all jarallax instances
80  // need to render all in one scroll/resize event
81
82  const jarallaxList = []; // get all parents of the element.
83
84  function getParents(elem) {
85    const parents = [];
86
87    while (null !== elem.parentElement) {
88      elem = elem.parentElement;
89
90      if (1 === elem.nodeType) {
91        parents.push(elem);
92      }
93    }
94
95    return parents;
96  }
97
98  function updateParallax() {
99    if (!jarallaxList.length) {
100      return;
101    }
102
103    jarallaxList.forEach((data, k) => {
104      const {
105        instance,
106        oldData
107      } = data;
108      const clientRect = instance.$item.getBoundingClientRect();
109      const newData = {
110        width: clientRect.width,
111        height: clientRect.height,
112        top: clientRect.top,
113        bottom: clientRect.bottom,
114        wndW: global$1.innerWidth,
115        wndH
116      };
117      const isResized = !oldData || oldData.wndW !== newData.wndW || oldData.wndH !== newData.wndH || oldData.width !== newData.width || oldData.height !== newData.height;
118      const isScrolled = isResized || !oldData || oldData.top !== newData.top || oldData.bottom !== newData.bottom;
119      jarallaxList[k].oldData = newData;
120
121      if (isResized) {
122        instance.onResize();
123      }
124
125      if (isScrolled) {
126        instance.onScroll();
127      }
128    });
129    global$1.requestAnimationFrame(updateParallax);
130  }
131
132  let instanceID = 0; // Jarallax class
133
134  class Jarallax {
135    constructor(item, userOptions) {
136      const self = this;
137      self.instanceID = instanceID;
138      instanceID += 1;
139      self.$item = item;
140      self.defaults = {
141        type: 'scroll',
142        // type of parallax: scroll, scale, opacity, scale-opacity, scroll-opacity
143        speed: 0.5,
144        // supported value from -1 to 2
145        imgSrc: null,
146        imgElement: '.jarallax-img',
147        imgSize: 'cover',
148        imgPosition: '50% 50%',
149        imgRepeat: 'no-repeat',
150        // supported only for background, not for <img> tag
151        keepImg: false,
152        // keep <img> tag in it's default place
153        elementInViewport: null,
154        zIndex: -100,
155        disableParallax: false,
156        disableVideo: false,
157        // video
158        videoSrc: null,
159        videoStartTime: 0,
160        videoEndTime: 0,
161        videoVolume: 0,
162        videoLoop: true,
163        videoPlayOnlyVisible: true,
164        videoLazyLoading: true,
165        // events
166        onScroll: null,
167        // function(calculations) {}
168        onInit: null,
169        // function() {}
170        onDestroy: null,
171        // function() {}
172        onCoverImage: null // function() {}
173
174      }; // prepare data-options
175
176      const dataOptions = self.$item.dataset || {};
177      const pureDataOptions = {};
178      Object.keys(dataOptions).forEach(key => {
179        const loweCaseOption = key.substr(0, 1).toLowerCase() + key.substr(1);
180
181        if (loweCaseOption && 'undefined' !== typeof self.defaults[loweCaseOption]) {
182          pureDataOptions[loweCaseOption] = dataOptions[key];
183        }
184      });
185      self.options = self.extend({}, self.defaults, pureDataOptions, userOptions);
186      self.pureOptions = self.extend({}, self.options); // prepare 'true' and 'false' strings to boolean
187
188      Object.keys(self.options).forEach(key => {
189        if ('true' === self.options[key]) {
190          self.options[key] = true;
191        } else if ('false' === self.options[key]) {
192          self.options[key] = false;
193        }
194      }); // fix speed option [-1.0, 2.0]
195
196      self.options.speed = Math.min(2, Math.max(-1, parseFloat(self.options.speed))); // prepare disableParallax callback
197
198      if ('string' === typeof self.options.disableParallax) {
199        self.options.disableParallax = new RegExp(self.options.disableParallax);
200      }
201
202      if (self.options.disableParallax instanceof RegExp) {
203        const disableParallaxRegexp = self.options.disableParallax;
204
205        self.options.disableParallax = () => disableParallaxRegexp.test(navigator.userAgent);
206      }
207
208      if ('function' !== typeof self.options.disableParallax) {
209        self.options.disableParallax = () => false;
210      } // prepare disableVideo callback
211
212
213      if ('string' === typeof self.options.disableVideo) {
214        self.options.disableVideo = new RegExp(self.options.disableVideo);
215      }
216
217      if (self.options.disableVideo instanceof RegExp) {
218        const disableVideoRegexp = self.options.disableVideo;
219
220        self.options.disableVideo = () => disableVideoRegexp.test(navigator.userAgent);
221      }
222
223      if ('function' !== typeof self.options.disableVideo) {
224        self.options.disableVideo = () => false;
225      } // custom element to check if parallax in viewport
226
227
228      let elementInVP = self.options.elementInViewport; // get first item from array
229
230      if (elementInVP && 'object' === typeof elementInVP && 'undefined' !== typeof elementInVP.length) {
231        [elementInVP] = elementInVP;
232      } // check if dom element
233
234
235      if (!(elementInVP instanceof Element)) {
236        elementInVP = null;
237      }
238
239      self.options.elementInViewport = elementInVP;
240      self.image = {
241        src: self.options.imgSrc || null,
242        $container: null,
243        useImgTag: false,
244        // 1. Position fixed is needed for the most of browsers because absolute position have glitches
245        // 2. On MacOS with smooth scroll there is a huge lags with absolute position - https://github.com/nk-o/jarallax/issues/75
246        // 3. Previously used 'absolute' for mobile devices. But we re-tested on iPhone 12 and 'fixed' position is working better, then 'absolute', so for now position is always 'fixed'
247        position: 'fixed'
248      };
249
250      if (self.initImg() && self.canInitParallax()) {
251        self.init();
252      }
253    } // add styles to element
254    // eslint-disable-next-line class-methods-use-this
255
256
257    css(el, styles) {
258      if ('string' === typeof styles) {
259        return global$1.getComputedStyle(el).getPropertyValue(styles);
260      }
261
262      Object.keys(styles).forEach(key => {
263        el.style[key] = styles[key];
264      });
265      return el;
266    } // Extend like jQuery.extend
267    // eslint-disable-next-line class-methods-use-this
268
269
270    extend(out, ...args) {
271      out = out || {};
272      Object.keys(args).forEach(i => {
273        if (!args[i]) {
274          return;
275        }
276
277        Object.keys(args[i]).forEach(key => {
278          out[key] = args[i][key];
279        });
280      });
281      return out;
282    } // get window size and scroll position. Useful for extensions
283    // eslint-disable-next-line class-methods-use-this
284
285
286    getWindowData() {
287      return {
288        width: global$1.innerWidth || document.documentElement.clientWidth,
289        height: wndH,
290        y: document.documentElement.scrollTop
291      };
292    } // Jarallax functions
293
294
295    initImg() {
296      const self = this; // find image element
297
298      let $imgElement = self.options.imgElement;
299
300      if ($imgElement && 'string' === typeof $imgElement) {
301        $imgElement = self.$item.querySelector($imgElement);
302      } // check if dom element
303
304
305      if (!($imgElement instanceof Element)) {
306        if (self.options.imgSrc) {
307          $imgElement = new Image();
308          $imgElement.src = self.options.imgSrc;
309        } else {
310          $imgElement = null;
311        }
312      }
313
314      if ($imgElement) {
315        if (self.options.keepImg) {
316          self.image.$item = $imgElement.cloneNode(true);
317        } else {
318          self.image.$item = $imgElement;
319          self.image.$itemParent = $imgElement.parentNode;
320        }
321
322        self.image.useImgTag = true;
323      } // true if there is img tag
324
325
326      if (self.image.$item) {
327        return true;
328      } // get image src
329
330
331      if (null === self.image.src) {
332        self.image.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
333        self.image.bgImage = self.css(self.$item, 'background-image');
334      }
335
336      return !(!self.image.bgImage || 'none' === self.image.bgImage);
337    }
338
339    canInitParallax() {
340      return !this.options.disableParallax();
341    }
342
343    init() {
344      const self = this;
345      const containerStyles = {
346        position: 'absolute',
347        top: 0,
348        left: 0,
349        width: '100%',
350        height: '100%',
351        overflow: 'hidden'
352      };
353      let imageStyles = {
354        pointerEvents: 'none',
355        transformStyle: 'preserve-3d',
356        backfaceVisibility: 'hidden'
357      };
358
359      if (!self.options.keepImg) {
360        // save default user styles
361        const curStyle = self.$item.getAttribute('style');
362
363        if (curStyle) {
364          self.$item.setAttribute('data-jarallax-original-styles', curStyle);
365        }
366
367        if (self.image.useImgTag) {
368          const curImgStyle = self.image.$item.getAttribute('style');
369
370          if (curImgStyle) {
371            self.image.$item.setAttribute('data-jarallax-original-styles', curImgStyle);
372          }
373        }
374      } // set relative position and z-index to the parent
375
376
377      if ('static' === self.css(self.$item, 'position')) {
378        self.css(self.$item, {
379          position: 'relative'
380        });
381      }
382
383      if ('auto' === self.css(self.$item, 'z-index')) {
384        self.css(self.$item, {
385          zIndex: 0
386        });
387      } // container for parallax image
388
389
390      self.image.$container = document.createElement('div');
391      self.css(self.image.$container, containerStyles);
392      self.css(self.image.$container, {
393        'z-index': self.options.zIndex
394      }); // it will remove some image overlapping
395      // overlapping occur due to an image position fixed inside absolute position element
396      // needed only when background in fixed position
397
398      if ('fixed' === this.image.position) {
399        self.css(self.image.$container, {
400          '-webkit-clip-path': 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',
401          'clip-path': 'polygon(0 0, 100% 0, 100% 100%, 0 100%)'
402        });
403      }
404
405      self.image.$container.setAttribute('id', `jarallax-container-${self.instanceID}`);
406      self.$item.appendChild(self.image.$container); // use img tag
407
408      if (self.image.useImgTag) {
409        imageStyles = self.extend({
410          'object-fit': self.options.imgSize,
411          'object-position': self.options.imgPosition,
412          'max-width': 'none'
413        }, containerStyles, imageStyles); // use div with background image
414      } else {
415        self.image.$item = document.createElement('div');
416
417        if (self.image.src) {
418          imageStyles = self.extend({
419            'background-position': self.options.imgPosition,
420            'background-size': self.options.imgSize,
421            'background-repeat': self.options.imgRepeat,
422            'background-image': self.image.bgImage || `url("${self.image.src}")`
423          }, containerStyles, imageStyles);
424        }
425      }
426
427      if ('opacity' === self.options.type || 'scale' === self.options.type || 'scale-opacity' === self.options.type || 1 === self.options.speed) {
428        self.image.position = 'absolute';
429      } // 1. Check if one of parents have transform style (without this check, scroll transform will be inverted if used parallax with position fixed)
430      //    discussion - https://github.com/nk-o/jarallax/issues/9
431      // 2. Check if parents have overflow scroll
432
433
434      if ('fixed' === self.image.position) {
435        const $parents = getParents(self.$item).filter(el => {
436          const styles = global$1.getComputedStyle(el);
437          const parentTransform = styles['-webkit-transform'] || styles['-moz-transform'] || styles.transform;
438          const overflowRegex = /(auto|scroll)/;
439          return parentTransform && 'none' !== parentTransform || overflowRegex.test(styles.overflow + styles['overflow-y'] + styles['overflow-x']);
440        });
441        self.image.position = $parents.length ? 'absolute' : 'fixed';
442      } // add position to parallax block
443
444
445      imageStyles.position = self.image.position; // insert parallax image
446
447      self.css(self.image.$item, imageStyles);
448      self.image.$container.appendChild(self.image.$item); // set initial position and size
449
450      self.onResize();
451      self.onScroll(true); // call onInit event
452
453      if (self.options.onInit) {
454        self.options.onInit.call(self);
455      } // remove default user background
456
457
458      if ('none' !== self.css(self.$item, 'background-image')) {
459        self.css(self.$item, {
460          'background-image': 'none'
461        });
462      }
463
464      self.addToParallaxList();
465    } // add to parallax instances list
466
467
468    addToParallaxList() {
469      jarallaxList.push({
470        instance: this
471      });
472
473      if (1 === jarallaxList.length) {
474        global$1.requestAnimationFrame(updateParallax);
475      }
476    } // remove from parallax instances list
477
478
479    removeFromParallaxList() {
480      const self = this;
481      jarallaxList.forEach((data, key) => {
482        if (data.instance.instanceID === self.instanceID) {
483          jarallaxList.splice(key, 1);
484        }
485      });
486    }
487
488    destroy() {
489      const self = this;
490      self.removeFromParallaxList(); // return styles on container as before jarallax init
491
492      const originalStylesTag = self.$item.getAttribute('data-jarallax-original-styles');
493      self.$item.removeAttribute('data-jarallax-original-styles'); // null occurs if there is no style tag before jarallax init
494
495      if (!originalStylesTag) {
496        self.$item.removeAttribute('style');
497      } else {
498        self.$item.setAttribute('style', originalStylesTag);
499      }
500
501      if (self.image.useImgTag) {
502        // return styles on img tag as before jarallax init
503        const originalStylesImgTag = self.image.$item.getAttribute('data-jarallax-original-styles');
504        self.image.$item.removeAttribute('data-jarallax-original-styles'); // null occurs if there is no style tag before jarallax init
505
506        if (!originalStylesImgTag) {
507          self.image.$item.removeAttribute('style');
508        } else {
509          self.image.$item.setAttribute('style', originalStylesTag);
510        } // move img tag to its default position
511
512
513        if (self.image.$itemParent) {
514          self.image.$itemParent.appendChild(self.image.$item);
515        }
516      } // remove additional dom elements
517
518
519      if (self.image.$container) {
520        self.image.$container.parentNode.removeChild(self.image.$container);
521      } // call onDestroy event
522
523
524      if (self.options.onDestroy) {
525        self.options.onDestroy.call(self);
526      } // delete jarallax from item
527
528
529      delete self.$item.jarallax;
530    } // Fallback for removed function.
531    // Does nothing now.
532    // eslint-disable-next-line class-methods-use-this
533
534
535    clipContainer() {}
536
537    coverImage() {
538      const self = this;
539      const rect = self.image.$container.getBoundingClientRect();
540      const contH = rect.height;
541      const {
542        speed
543      } = self.options;
544      const isScroll = 'scroll' === self.options.type || 'scroll-opacity' === self.options.type;
545      let scrollDist = 0;
546      let resultH = contH;
547      let resultMT = 0; // scroll parallax
548
549      if (isScroll) {
550        // scroll distance and height for image
551        if (0 > speed) {
552          scrollDist = speed * Math.max(contH, wndH);
553
554          if (wndH < contH) {
555            scrollDist -= speed * (contH - wndH);
556          }
557        } else {
558          scrollDist = speed * (contH + wndH);
559        } // size for scroll parallax
560
561
562        if (1 < speed) {
563          resultH = Math.abs(scrollDist - wndH);
564        } else if (0 > speed) {
565          resultH = scrollDist / speed + Math.abs(scrollDist);
566        } else {
567          resultH += (wndH - contH) * (1 - speed);
568        }
569
570        scrollDist /= 2;
571      } // store scroll distance
572
573
574      self.parallaxScrollDistance = scrollDist; // vertical center
575
576      if (isScroll) {
577        resultMT = (wndH - resultH) / 2;
578      } else {
579        resultMT = (contH - resultH) / 2;
580      } // apply result to item
581
582
583      self.css(self.image.$item, {
584        height: `${resultH}px`,
585        marginTop: `${resultMT}px`,
586        left: 'fixed' === self.image.position ? `${rect.left}px` : '0',
587        width: `${rect.width}px`
588      }); // call onCoverImage event
589
590      if (self.options.onCoverImage) {
591        self.options.onCoverImage.call(self);
592      } // return some useful data. Used in the video cover function
593
594
595      return {
596        image: {
597          height: resultH,
598          marginTop: resultMT
599        },
600        container: rect
601      };
602    }
603
604    isVisible() {
605      return this.isElementInViewport || false;
606    }
607
608    onScroll(force) {
609      const self = this;
610      const rect = self.$item.getBoundingClientRect();
611      const contT = rect.top;
612      const contH = rect.height;
613      const styles = {}; // check if in viewport
614
615      let viewportRect = rect;
616
617      if (self.options.elementInViewport) {
618        viewportRect = self.options.elementInViewport.getBoundingClientRect();
619      }
620
621      self.isElementInViewport = 0 <= viewportRect.bottom && 0 <= viewportRect.right && viewportRect.top <= wndH && viewportRect.left <= global$1.innerWidth; // stop calculations if item is not in viewport
622
623      if (force ? false : !self.isElementInViewport) {
624        return;
625      } // calculate parallax helping variables
626
627
628      const beforeTop = Math.max(0, contT);
629      const beforeTopEnd = Math.max(0, contH + contT);
630      const afterTop = Math.max(0, -contT);
631      const beforeBottom = Math.max(0, contT + contH - wndH);
632      const beforeBottomEnd = Math.max(0, contH - (contT + contH - wndH));
633      const afterBottom = Math.max(0, -contT + wndH - contH);
634      const fromViewportCenter = 1 - 2 * ((wndH - contT) / (wndH + contH)); // calculate on how percent of section is visible
635
636      let visiblePercent = 1;
637
638      if (contH < wndH) {
639        visiblePercent = 1 - (afterTop || beforeBottom) / contH;
640      } else if (beforeTopEnd <= wndH) {
641        visiblePercent = beforeTopEnd / wndH;
642      } else if (beforeBottomEnd <= wndH) {
643        visiblePercent = beforeBottomEnd / wndH;
644      } // opacity
645
646
647      if ('opacity' === self.options.type || 'scale-opacity' === self.options.type || 'scroll-opacity' === self.options.type) {
648        styles.transform = 'translate3d(0,0,0)';
649        styles.opacity = visiblePercent;
650      } // scale
651
652
653      if ('scale' === self.options.type || 'scale-opacity' === self.options.type) {
654        let scale = 1;
655
656        if (0 > self.options.speed) {
657          scale -= self.options.speed * visiblePercent;
658        } else {
659          scale += self.options.speed * (1 - visiblePercent);
660        }
661
662        styles.transform = `scale(${scale}) translate3d(0,0,0)`;
663      } // scroll
664
665
666      if ('scroll' === self.options.type || 'scroll-opacity' === self.options.type) {
667        let positionY = self.parallaxScrollDistance * fromViewportCenter; // fix if parallax block in absolute position
668
669        if ('absolute' === self.image.position) {
670          positionY -= contT;
671        }
672
673        styles.transform = `translate3d(0,${positionY}px,0)`;
674      }
675
676      self.css(self.image.$item, styles); // call onScroll event
677
678      if (self.options.onScroll) {
679        self.options.onScroll.call(self, {
680          section: rect,
681          beforeTop,
682          beforeTopEnd,
683          afterTop,
684          beforeBottom,
685          beforeBottomEnd,
686          afterBottom,
687          visiblePercent,
688          fromViewportCenter
689        });
690      }
691    }
692
693    onResize() {
694      this.coverImage();
695    }
696
697  } // global definition
698
699
700  const jarallax = function (items, options, ...args) {
701    // check for dom element
702    // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
703    if ('object' === typeof HTMLElement ? items instanceof HTMLElement : items && 'object' === typeof items && null !== items && 1 === items.nodeType && 'string' === typeof items.nodeName) {
704      items = [items];
705    }
706
707    const len = items.length;
708    let k = 0;
709    let ret;
710
711    for (k; k < len; k += 1) {
712      if ('object' === typeof options || 'undefined' === typeof options) {
713        if (!items[k].jarallax) {
714          items[k].jarallax = new Jarallax(items[k], options);
715        }
716      } else if (items[k].jarallax) {
717        // eslint-disable-next-line prefer-spread
718        ret = items[k].jarallax[options].apply(items[k].jarallax, args);
719      }
720
721      if ('undefined' !== typeof ret) {
722        return ret;
723      }
724    }
725
726    return items;
727  };
728
729  jarallax.constructor = Jarallax;
730
731  const $ = global$1.jQuery; // jQuery support
732
733  if ('undefined' !== typeof $) {
734    const $Plugin = function (...args) {
735      Array.prototype.unshift.call(args, this);
736      const res = jarallax.apply(global$1, args);
737      return 'object' !== typeof res ? res : this;
738    };
739
740    $Plugin.constructor = jarallax.constructor; // no conflict
741
742    const old$Plugin = $.fn.jarallax;
743    $.fn.jarallax = $Plugin;
744
745    $.fn.jarallax.noConflict = function () {
746      $.fn.jarallax = old$Plugin;
747      return this;
748    };
749  } // data-jarallax initialization
750
751
752  ready(() => {
753    jarallax(document.querySelectorAll('[data-jarallax]'));
754  });
755
756  return jarallax;
757
758}));
759//# sourceMappingURL=jarallax.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.