PageSourceSearch

https://whitehartwinchcombe.co.uk/wp-content/plugins/hoteller-elementor/assets/js/jarallax.js?ver=7.1.2

js whitehartwinchcombe.co.uk collected 2026-10-04 20:03:27 UTC 23,627 bytes, 760 lines download raw bytes

1/*!
2 * Jarallax v2.0.3 (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        willChange: 'transform,opacity'
358      };
359
360      if (!self.options.keepImg) {
361        // save default user styles
362        const curStyle = self.$item.getAttribute('style');
363
364        if (curStyle) {
365          self.$item.setAttribute('data-jarallax-original-styles', curStyle);
366        }
367
368        if (self.image.useImgTag) {
369          const curImgStyle = self.image.$item.getAttribute('style');
370
371          if (curImgStyle) {
372            self.image.$item.setAttribute('data-jarallax-original-styles', curImgStyle);
373          }
374        }
375      } // set relative position and z-index to the parent
376
377
378      if ('static' === self.css(self.$item, 'position')) {
379        self.css(self.$item, {
380          position: 'relative'
381        });
382      }
383
384      if ('auto' === self.css(self.$item, 'z-index')) {
385        self.css(self.$item, {
386          zIndex: 0
387        });
388      } // container for parallax image
389
390
391      self.image.$container = document.createElement('div');
392      self.css(self.image.$container, containerStyles);
393      self.css(self.image.$container, {
394        'z-index': self.options.zIndex
395      }); // it will remove some image overlapping
396      // overlapping occur due to an image position fixed inside absolute position element
397      // needed only when background in fixed position
398
399      if ('fixed' === this.image.position) {
400        self.css(self.image.$container, {
401          '-webkit-clip-path': 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',
402          'clip-path': 'polygon(0 0, 100% 0, 100% 100%, 0 100%)'
403        });
404      }
405
406      self.image.$container.setAttribute('id', `jarallax-container-${self.instanceID}`);
407      self.$item.appendChild(self.image.$container); // use img tag
408
409      if (self.image.useImgTag) {
410        imageStyles = self.extend({
411          'object-fit': self.options.imgSize,
412          'object-position': self.options.imgPosition,
413          'max-width': 'none'
414        }, containerStyles, imageStyles); // use div with background image
415      } else {
416        self.image.$item = document.createElement('div');
417
418        if (self.image.src) {
419          imageStyles = self.extend({
420            'background-position': self.options.imgPosition,
421            'background-size': self.options.imgSize,
422            'background-repeat': self.options.imgRepeat,
423            'background-image': self.image.bgImage || `url("${self.image.src}")`
424          }, containerStyles, imageStyles);
425        }
426      }
427
428      if ('opacity' === self.options.type || 'scale' === self.options.type || 'scale-opacity' === self.options.type || 1 === self.options.speed) {
429        self.image.position = 'absolute';
430      } // 1. Check if one of parents have transform style (without this check, scroll transform will be inverted if used parallax with position fixed)
431      //    discussion - https://github.com/nk-o/jarallax/issues/9
432      // 2. Check if parents have overflow scroll
433
434
435      if ('fixed' === self.image.position) {
436        const $parents = getParents(self.$item).filter(el => {
437          const styles = global$1.getComputedStyle(el);
438          const parentTransform = styles['-webkit-transform'] || styles['-moz-transform'] || styles.transform;
439          const overflowRegex = /(auto|scroll)/;
440          return parentTransform && 'none' !== parentTransform || overflowRegex.test(styles.overflow + styles['overflow-y'] + styles['overflow-x']);
441        });
442        self.image.position = $parents.length ? 'absolute' : 'fixed';
443      } // add position to parallax block
444
445
446      imageStyles.position = self.image.position; // insert parallax image
447
448      self.css(self.image.$item, imageStyles);
449      self.image.$container.appendChild(self.image.$item); // set initial position and size
450
451      self.onResize();
452      self.onScroll(true); // call onInit event
453
454      if (self.options.onInit) {
455        self.options.onInit.call(self);
456      } // remove default user background
457
458
459      if ('none' !== self.css(self.$item, 'background-image')) {
460        self.css(self.$item, {
461          'background-image': 'none'
462        });
463      }
464
465      self.addToParallaxList();
466    } // add to parallax instances list
467
468
469    addToParallaxList() {
470      jarallaxList.push({
471        instance: this
472      });
473
474      if (1 === jarallaxList.length) {
475        global$1.requestAnimationFrame(updateParallax);
476      }
477    } // remove from parallax instances list
478
479
480    removeFromParallaxList() {
481      const self = this;
482      jarallaxList.forEach((data, key) => {
483        if (data.instance.instanceID === self.instanceID) {
484          jarallaxList.splice(key, 1);
485        }
486      });
487    }
488
489    destroy() {
490      const self = this;
491      self.removeFromParallaxList(); // return styles on container as before jarallax init
492
493      const originalStylesTag = self.$item.getAttribute('data-jarallax-original-styles');
494      self.$item.removeAttribute('data-jarallax-original-styles'); // null occurs if there is no style tag before jarallax init
495
496      if (!originalStylesTag) {
497        self.$item.removeAttribute('style');
498      } else {
499        self.$item.setAttribute('style', originalStylesTag);
500      }
501
502      if (self.image.useImgTag) {
503        // return styles on img tag as before jarallax init
504        const originalStylesImgTag = self.image.$item.getAttribute('data-jarallax-original-styles');
505        self.image.$item.removeAttribute('data-jarallax-original-styles'); // null occurs if there is no style tag before jarallax init
506
507        if (!originalStylesImgTag) {
508          self.image.$item.removeAttribute('style');
509        } else {
510          self.image.$item.setAttribute('style', originalStylesTag);
511        } // move img tag to its default position
512
513
514        if (self.image.$itemParent) {
515          self.image.$itemParent.appendChild(self.image.$item);
516        }
517      } // remove additional dom elements
518
519
520      if (self.image.$container) {
521        self.image.$container.parentNode.removeChild(self.image.$container);
522      } // call onDestroy event
523
524
525      if (self.options.onDestroy) {
526        self.options.onDestroy.call(self);
527      } // delete jarallax from item
528
529
530      delete self.$item.jarallax;
531    } // Fallback for removed function.
532    // Does nothing now.
533    // eslint-disable-next-line class-methods-use-this
534
535
536    clipContainer() {}
537
538    coverImage() {
539      const self = this;
540      const rect = self.image.$container.getBoundingClientRect();
541      const contH = rect.height;
542      const {
543        speed
544      } = self.options;
545      const isScroll = 'scroll' === self.options.type || 'scroll-opacity' === self.options.type;
546      let scrollDist = 0;
547      let resultH = contH;
548      let resultMT = 0; // scroll parallax
549
550      if (isScroll) {
551        // scroll distance and height for image
552        if (0 > speed) {
553          scrollDist = speed * Math.max(contH, wndH);
554
555          if (wndH < contH) {
556            scrollDist -= speed * (contH - wndH);
557          }
558        } else {
559          scrollDist = speed * (contH + wndH);
560        } // size for scroll parallax
561
562
563        if (1 < speed) {
564          resultH = Math.abs(scrollDist - wndH);
565        } else if (0 > speed) {
566          resultH = scrollDist / speed + Math.abs(scrollDist);
567        } else {
568          resultH += (wndH - contH) * (1 - speed);
569        }
570
571        scrollDist /= 2;
572      } // store scroll distance
573
574
575      self.parallaxScrollDistance = scrollDist; // vertical center
576
577      if (isScroll) {
578        resultMT = (wndH - resultH) / 2;
579      } else {
580        resultMT = (contH - resultH) / 2;
581      } // apply result to item
582
583
584      self.css(self.image.$item, {
585        height: `${resultH}px`,
586        marginTop: `${resultMT}px`,
587        left: 'fixed' === self.image.position ? `${rect.left}px` : '0',
588        width: `${rect.width}px`
589      }); // call onCoverImage event
590
591      if (self.options.onCoverImage) {
592        self.options.onCoverImage.call(self);
593      } // return some useful data. Used in the video cover function
594
595
596      return {
597        image: {
598          height: resultH,
599          marginTop: resultMT
600        },
601        container: rect
602      };
603    }
604
605    isVisible() {
606      return this.isElementInViewport || false;
607    }
608
609    onScroll(force) {
610      const self = this;
611      const rect = self.$item.getBoundingClientRect();
612      const contT = rect.top;
613      const contH = rect.height;
614      const styles = {}; // check if in viewport
615
616      let viewportRect = rect;
617
618      if (self.options.elementInViewport) {
619        viewportRect = self.options.elementInViewport.getBoundingClientRect();
620      }
621
622      self.isElementInViewport = 0 <= viewportRect.bottom && 0 <= viewportRect.right && viewportRect.top <= wndH && viewportRect.left <= global$1.innerWidth; // stop calculations if item is not in viewport
623
624      if (force ? false : !self.isElementInViewport) {
625        return;
626      } // calculate parallax helping variables
627
628
629      const beforeTop = Math.max(0, contT);
630      const beforeTopEnd = Math.max(0, contH + contT);
631      const afterTop = Math.max(0, -contT);
632      const beforeBottom = Math.max(0, contT + contH - wndH);
633      const beforeBottomEnd = Math.max(0, contH - (contT + contH - wndH));
634      const afterBottom = Math.max(0, -contT + wndH - contH);
635      const fromViewportCenter = 1 - 2 * ((wndH - contT) / (wndH + contH)); // calculate on how percent of section is visible
636
637      let visiblePercent = 1;
638
639      if (contH < wndH) {
640        visiblePercent = 1 - (afterTop || beforeBottom) / contH;
641      } else if (beforeTopEnd <= wndH) {
642        visiblePercent = beforeTopEnd / wndH;
643      } else if (beforeBottomEnd <= wndH) {
644        visiblePercent = beforeBottomEnd / wndH;
645      } // opacity
646
647
648      if ('opacity' === self.options.type || 'scale-opacity' === self.options.type || 'scroll-opacity' === self.options.type) {
649        styles.transform = 'translate3d(0,0,0)';
650        styles.opacity = visiblePercent;
651      } // scale
652
653
654      if ('scale' === self.options.type || 'scale-opacity' === self.options.type) {
655        let scale = 1;
656
657        if (0 > self.options.speed) {
658          scale -= self.options.speed * visiblePercent;
659        } else {
660          scale += self.options.speed * (1 - visiblePercent);
661        }
662
663        styles.transform = `scale(${scale}) translate3d(0,0,0)`;
664      } // scroll
665
666
667      if ('scroll' === self.options.type || 'scroll-opacity' === self.options.type) {
668        let positionY = self.parallaxScrollDistance * fromViewportCenter; // fix if parallax block in absolute position
669
670        if ('absolute' === self.image.position) {
671          positionY -= contT;
672        }
673
674        styles.transform = `translate3d(0,${positionY}px,0)`;
675      }
676
677      self.css(self.image.$item, styles); // call onScroll event
678
679      if (self.options.onScroll) {
680        self.options.onScroll.call(self, {
681          section: rect,
682          beforeTop,
683          beforeTopEnd,
684          afterTop,
685          beforeBottom,
686          beforeBottomEnd,
687          afterBottom,
688          visiblePercent,
689          fromViewportCenter
690        });
691      }
692    }
693
694    onResize() {
695      this.coverImage();
696    }
697
698  } // global definition
699
700
701  const jarallax = function (items, options, ...args) {
702    // check for dom element
703    // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
704    if ('object' === typeof HTMLElement ? items instanceof HTMLElement : items && 'object' === typeof items && null !== items && 1 === items.nodeType && 'string' === typeof items.nodeName) {
705      items = [items];
706    }
707
708    const len = items.length;
709    let k = 0;
710    let ret;
711
712    for (k; k < len; k += 1) {
713      if ('object' === typeof options || 'undefined' === typeof options) {
714        if (!items[k].jarallax) {
715          items[k].jarallax = new Jarallax(items[k], options);
716        }
717      } else if (items[k].jarallax) {
718        // eslint-disable-next-line prefer-spread
719        ret = items[k].jarallax[options].apply(items[k].jarallax, args);
720      }
721
722      if ('undefined' !== typeof ret) {
723        return ret;
724      }
725    }
726
727    return items;
728  };
729
730  jarallax.constructor = Jarallax;
731
732  const $ = global$1.jQuery; // jQuery support
733
734  if ('undefined' !== typeof $) {
735    const $Plugin = function (...args) {
736      Array.prototype.unshift.call(args, this);
737      const res = jarallax.apply(global$1, args);
738      return 'object' !== typeof res ? res : this;
739    };
740
741    $Plugin.constructor = jarallax.constructor; // no conflict
742
743    const old$Plugin = $.fn.jarallax;
744    $.fn.jarallax = $Plugin;
745
746    $.fn.jarallax.noConflict = function () {
747      $.fn.jarallax = old$Plugin;
748      return this;
749    };
750  } // data-jarallax initialization
751
752
753  ready(() => {
754    jarallax(document.querySelectorAll('[data-jarallax]'));
755  });
756
757  return jarallax;
758
759}));
760//# 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.