PageSourceSearch

https://posnerproperty.com/wp-content/plugins/thegem-elements-elem…/assets/js/thegem-section-parallax.js

js posnerproperty.com collected 2026-10-02 16:04:12 UTC 16,162 bytes, 523 lines download raw bytes

1(function($, elementor) {
2	"use strict";
3
4	$.fn.parallaxVertical = function(xpos, options) {
5		var $window = $(window),
6			$page = $('#page');
7
8		var windowHeight = $window.height(),
9			perspectiveOpened = false;
10
11		options = options || {};
12
13		this.each(function() {
14			var $this = $(this),
15				speedFactor,
16				offsetFactor = 0,
17				getHeight,
18				topOffset = 0,
19				containerHeight = 0,
20				containerWidth = 0,
21				disableParallax = false,
22				parallaxIsDisabled = false,
23				baseImgHeight = 0,
24				baseImgWidth = 0,
25				isBgCover = ($this.css('background-size') == 'cover'),
26				curImgHeight = 0,
27				reversed = $this.hasClass('parallax-reversed'),
28				baseSpeedFactor = reversed ? -0.1 : 0.3,
29				outerHeight = true;
30
31			if (xpos === undefined) xpos = "50%";
32
33			if (outerHeight){
34				getHeight = function(jqo){
35					return jqo.parent().outerHeight(true);
36				};
37			} else {
38				getHeight = function(jqo){
39					return jqo.parent().height();
40				};
41			}
42
43			function getBackgroundSize(callback){
44				var img = new Image(),
45					// here we will place image's width and height
46					width, height,
47					// here we get the size of the background and split it to array
48					backgroundSize = ($this.css('background-size') || ' ').split(' ');
49
50				// checking if width was set to pixel value
51				if (/px/.test(backgroundSize[0])) width = parseInt(backgroundSize[0]);
52				// checking if width was set to percent value
53				if (/%/.test(backgroundSize[0])) width = $this.parent().width() * (parseInt(backgroundSize[0]) / 100);
54				// checking if height was set to pixel value
55				if (/px/.test(backgroundSize[1])) height = parseInt(backgroundSize[1]);
56				// checking if height was set to percent value
57				if (/%/.test(backgroundSize[1])) height = $this.parent().height() * (parseInt(backgroundSize[0]) / 100);
58
59				if (width !== undefined && height !== undefined){
60					// Image is not needed
61					return callback({ width: width, height: height });
62				}
63
64				// Image is needed
65				img.onload = function () {
66					// check if width was set earlier, if not then set it now
67					if (typeof width == 'undefined') width = this.width;
68					// do the same with height
69					if (typeof height == 'undefined') height = this.height;
70					// call the callback
71					callback({ width: width, height: height });
72				};
73				// extract image source from css using one, simple regex
74				// src should be set AFTER onload handler
75				img.src = ($this.css('background-image') || '').replace(/url\(['"]*(.*?)['"]*\)/g, '$1');
76			}
77
78			function update(){
79				if (disableParallax){
80					if ( ! parallaxIsDisabled){
81						$this.css('backgroundPosition', '');
82						if(!$this.is('.page-title-block')) {
83							$this.removeClass('fullwidth-block-parallax-vertical');
84						}
85						parallaxIsDisabled = true;
86					}
87					return;
88				}else{
89					if (parallaxIsDisabled){
90						if(!$this.is('.page-title-block')) {
91							$this.addClass('fullwidth-block-parallax-vertical');
92						}
93						parallaxIsDisabled = false;
94					}
95				}
96				if (isNaN(speedFactor))
97					return;
98
99				var pos = getScrollTop();
100
101				// Check if totally above or totally below viewport
102				if ((topOffset + containerHeight < pos) || (pos < topOffset - windowHeight)) return;
103
104				var ypos = offsetFactor + speedFactor * (topOffset - pos);
105
106				if (ypos > 0) {
107					ypos = 0;
108				} else if (curImgHeight + ypos < containerHeight) {
109					ypos = containerHeight - curImgHeight;
110				}
111
112				if (curImgHeight > 0) {
113					$this.css('height', curImgHeight+'px');
114				}
115
116				var translate = 'translate3d(0, ' + ypos + 'px' + ', 0)';
117				$this[0].style['-webkit-transform'] = translate;
118				$this[0].style['-moz-transform'] = translate;
119				$this[0].style['transform'] = translate;
120			}
121
122			function getScrollTop() {
123				return perspectiveOpened ? $page.scrollTop() : $window.scrollTop();
124			}
125
126			function resize(){
127				setTimeout(function(){
128					if (perspectiveOpened) {
129						windowHeight = $page.height();
130					} else {
131						windowHeight = $window.height();
132					}
133
134					containerHeight = getHeight($this);
135					containerWidth = $this.parent().width();
136
137					if (isBgCover){
138						if (baseImgWidth / baseImgHeight <= containerWidth / containerHeight){
139							curImgHeight = baseImgHeight * (containerWidth / baseImgWidth);
140						} else {
141							curImgHeight = containerHeight*1.2;
142						}
143					}
144
145					topOffset = $this.parent().offset().top;
146
147					// Improving speed factor to prevent showing image limits
148					if (curImgHeight !== 0) {
149						if (baseSpeedFactor >= 0) {
150							if (options.position !== undefined && options.position == 'top') {
151								speedFactor = Math.min(0.25, (curImgHeight - containerHeight) / (containerHeight + topOffset));
152								offsetFactor = -speedFactor * topOffset;
153							} else {
154								speedFactor = -Math.min(baseSpeedFactor, (curImgHeight - containerHeight) / (windowHeight + containerHeight));
155								offsetFactor = containerHeight * speedFactor;
156							}
157						} else {
158							speedFactor = Math.min(baseSpeedFactor, (windowHeight - containerHeight) / (windowHeight + containerHeight));
159							offsetFactor = Math.max(0, speedFactor * containerHeight);
160						}
161					} else {
162						speedFactor = baseSpeedFactor;
163						offsetFactor = 0;
164					}
165
166					update();
167				}, 10);
168			}
169
170			getBackgroundSize(function(sz){
171				curImgHeight = baseImgHeight = sz.height;
172				baseImgWidth = sz.width;
173				resize();
174			});
175
176			$window.on({scroll: update, load: resize, resize: resize, touchmove: update});
177
178			var tgpliVisibleTimeout = false;
179			$window.on('tgpliVisible', function(e) {
180				if (tgpliVisibleTimeout) {
181					clearTimeout(tgpliVisibleTimeout);
182				}
183
184				tgpliVisibleTimeout = setTimeout(function() {
185					resize();
186				}, 100);
187			});
188			//$page.bind({scroll: update, resize: resize});
189			resize();
190		});
191
192		$(window).on('perspective-modalview-opened', function() {
193			perspectiveOpened = true;
194			windowHeight = $page.height();
195		});
196		$(window).on('perspective-modalview-closed', function() {
197			perspectiveOpened = false;
198			windowHeight = $window.height();
199		});
200	};
201
202
203
204	$.fn.parallaxHorizontal = function(options) {
205		this.each(function(){
206			return new ParallaxHorizontal(this, options);
207		});
208	};
209
210	function ParallaxHorizontal(el, options) {
211		var that = this;
212		this.$window = $(window);
213		this.el = el;
214		this.$el = $(el);
215		this.$parent = $(el).parent();
216		this.$section = $(el).closest('.elementor-section, .e-con');
217
218		this.defaults = {
219			fps: 60,
220			basePoint: .5,
221			duration: 500,
222			speed: 278 / 500,
223			easing: 'swing'// 'easeOutElastic'
224		};
225
226		// Apply options
227		if (el.onclick != undefined){
228			options = $.extend({}, el.onclick() || {}, typeof options == 'object' && options);
229			this.$el.removeProp('onclick');
230		}
231		options = $.extend({}, this.defaults, typeof options == 'object' && options);
232		this.options = options;
233		// Count sizes
234		this.containerWidth = this.$el.outerWidth();
235		this.containerHeight = this.$el.outerHeight();
236		this.windowHeight = this.$window.height();
237		// Count frame rate
238		this._frameRate = Math.round(1000 / this.options.fps);
239		// To fix IE bug that handles mousemove before mouseenter
240		this.mouseInside = false;
241
242		var img = new Image();
243		img.onload = function () {
244			that.bgWidth = this.width;
245
246			// Mouse events for desktop browsers
247			if ( ! ('ontouchstart' in window) || ! ('DeviceOrientationEvent' in window)){
248				that.$section
249					.mouseenter(function(e){
250						that.mouseInside = true;
251						var offset = that.bgPosition(),
252							coord = (e.pageX - offset.left) / that.bgWidth;
253						that.cancel();
254						that._hoverAnimation = true;
255						that._hoverFrom = that.now;
256						that._hoverTo = coord;
257						that.start(that._hoverTo);
258					})
259					.mousemove(function(e){
260						// To fix IE bug that handles mousemove before mouseenter
261						if ( ! that.mouseInside) return;
262						// Reducing processor load for too frequent event calls
263						if (that._lastFrame + that._frameRate > Date.now()) return;
264						var offset = that.bgPosition(),
265							coord = (e.pageX - offset.left) / that.bgWidth;
266						// Handle hover animation
267						if (that._hoverAnimation){
268							that._hoverTo = coord;
269							return;
270						}
271						that.set(coord);
272						that._lastFrame = Date.now();
273					})
274					.mouseleave(function(e){
275						that.mouseInside = false;
276						that.cancel();
277						that.start(that.options.basePoint);
278					});
279			}
280			// Handle resize
281			that.$window.resize(function(){ that.handleResize(); });
282			// Device orientation events for touch devices
283			that._orientationDriven = ('ontouchstart' in window && 'DeviceOrientationEvent' in window);
284			if (that._orientationDriven){
285				// Check if container is visible
286				that._checkIfVisible();
287				window.addEventListener("deviceorientation", function(e){
288					// Reducing processor load for too frequent event calls
289					if ( ! that.visible || that._lastFrame + that._frameRate > Date.now()) return;
290					that._deviceOrientationChange(e);
291					that._lastFrame = Date.now();
292				});
293				that.$window.resize(function(){ that._checkIfVisible(); });
294				that.$window.scroll(function(){ that._checkIfVisible(); });
295			}
296			// Set to basepoint
297			that.set(that.options.basePoint);
298			that._lastFrame = Date.now();
299		};
300
301		img.src = (this.$el.css('background-image') || '').replace(/url\(['"]*(.*?)['"]*\)/g, '$1');
302	}
303
304	ParallaxHorizontal.prototype = {
305		_deviceOrientationChange: function(e){
306			var gamma = e.gamma,
307				beta = e.beta,
308				x, y;
309			switch (window.orientation){
310				case -90:
311					beta = Math.max(-45, Math.min(45, beta));
312					x = (beta + 45) / 90;
313					break;
314				case 90:
315					beta = Math.max(-45, Math.min(45, beta));
316					x = (45 - beta) / 90;
317					break;
318				case 180:
319					gamma = Math.max(-45, Math.min(45, gamma));
320					x = (gamma + 45) / 90;
321					break;
322				case 0:
323				default:
324					// Upside down
325					if (gamma < -90 || gamma > 90) gamma = Math.abs(e.gamma)/e.gamma * (180 - Math.abs(e.gamma));
326					gamma = Math.max(-45, Math.min(45, gamma));
327					x = (45 - gamma) / 90;
328					break;
329			}
330			this.set(x);
331		},
332
333		bgPosition: function() {
334			var position = this.$el.css('background-position');
335			var posList = position.split(' ');
336			var left = posList[0];
337
338			if (left == 'center') {
339				left = '50%';
340			}
341
342			if (left.match(/\d+\%/)) {
343				left = this.containerWidth * (parseFloat(left) / 100) - this.bgWidth / 2;
344			} else if (left.match(/\d+px/)) {
345				left = parseFloat(left);
346			}
347
348			return {
349				left: left
350			};
351		},
352
353		handleResize: function()
354		{
355			this.containerWidth = this.$el.outerWidth();
356			this.containerHeight = this.$el.outerHeight();
357			this.windowHeight = this.$window.height();
358			this.set(this.now);
359		},
360
361		_checkIfVisible: function()
362		{
363			var scrollTop = this.$window.scrollTop(),
364				containerTop = this.$el.offset().top;
365			this.visible = (containerTop + this.containerHeight > scrollTop && containerTop < scrollTop + this.windowHeight);
366		},
367
368		set: function(x)
369		{
370			this.$el.css('background-position', ((this.containerWidth - this.bgWidth) * x) + 'px center');
371			this.now = x;
372			return this;
373		},
374
375		compute: function(from, to, delta)
376		{
377			if (this._hoverAnimation) return (this._hoverTo - this._hoverFrom) * delta + this._hoverFrom;
378			return (to - from) * delta + from;
379		},
380
381		start: function(to)
382		{
383			var from = this.now,
384				that = this,
385				fromPosition = (this.bgWidth - this.containerWidth) * from,
386				toPosition = (this.bgWidth - this.containerWidth) * to,
387				duration = Math.abs(toPosition - fromPosition) / 0.1;
388
389			this.$el
390				.css('delta', 0)
391				.animate({
392					delta: 1
393				}, {
394					duration: duration,
395					easing: this.options.easing,
396					complete: function(){
397						that._hoverAnimation = false;
398					},
399					step: function(delta){
400						that.set(that.compute(from, to, delta));
401					},
402					queue: false
403				});
404			return this;
405		},
406
407		cancel: function()
408		{
409			this._hoverAnimation = false;
410			this.$el.stop(true, false);
411			return this;
412		}
413	};
414
415	$(window).on('elementor/frontend/init', function () {
416
417		let FrontEndExtended = elementorModules.frontend.handlers.Base.extend({
418
419			generateParallaxConfig: function() {
420				var parallaxConfig = {};
421				if(this.getElementSettings('thegem_parallax_activate') === 'yes') {
422					parallaxConfig['parallax'] = true;
423					parallaxConfig['mobile'] = this.getElementSettings('thegem_parallax_activate_mobile') === 'yes';
424					parallaxConfig['type'] = this.getElementSettings('thegem_parallax_type') ? this.getElementSettings('thegem_parallax_type') : 'vertical';
425				}
426				if(Object.keys(parallaxConfig).length !== 0) {
427					return parallaxConfig;
428				}
429				return false;
430			},
431
432			onInit: function () {
433				var editMode = Boolean(elementor.isEditMode()),
434					parallaxConfig = false;
435
436				/*if(!editMode || thegem_section_parallax[this.getID()] != undefined) {
437					parallaxConfig = thegem_section_parallax[this.getID()] || false;
438				} else {*/
439					parallaxConfig = this.generateParallaxConfig();
440				//}
441				if (parallaxConfig) {
442					this.$element.addClass('e-no-lazyload');
443					$( '.thegem-section-parallax-background', this.$element ).remove();
444					var $backgroundWrap = $('<div class="thegem-section-parallax"></div>');
445					var $background = $('<div class="thegem-section-parallax-background"></div>');
446					$backgroundWrap.prependTo(this.$element);
447					$background.prependTo($backgroundWrap);
448
449					var parallax_type = parallaxConfig['type'] ? parallaxConfig['type'] : 'vertical';
450					$backgroundWrap.addClass('thegem-section-parallax-' + parallax_type);
451
452					var mobile_enabled = parallaxConfig['mobile'] ? '1' : '0',
453						is_custom_title = $backgroundWrap.hasClass('custom-title-background'),
454						backgroundImageCss = $background.css('background-image') || '';
455
456					if (!window.gemSettings.isTouch || mobile_enabled === '1') {
457						if ($backgroundWrap.hasClass('thegem-section-parallax-vert
457ical')) {
458							var parallaxOptions = {};
459							if (is_custom_title) {
460								parallaxOptions.position = 'top';
461							}
462
463							if (backgroundImageCss == 'none' || backgroundImageCss == '') {
464								$background.on('tgpliVisible', function() {
465									$(this).parallaxVertical('50%', parallaxOptions);
466								});
467
468								//return;
469							}
470
471							$background.parallaxVertical('50%', parallaxOptions);
472
473						} else if ($backgroundWrap.hasClass('thegem-section-parallax-horizontal')) {
474
475							if (window.gemSettings.parallaxDisabled) {
476								return;
477							}
478
479							if (backgroundImageCss == 'none' || backgroundImageCss == '') {
480								$background.on('tgpliVisible', function() {
481									$(this).parallaxHorizontal();
482								});
483
484								//return;
485							}
486
487							$background.parallaxHorizontal();
488						}
489					} else {
490						$background.css({backgroundAttachment: 'scroll'});
491					}
492
493				}
494			},
495		});
496
497		elementor.hooks.addAction( 'frontend/element_ready/section', function ($element) {
498			new FrontEndExtended({
499				$element: $element
500			});
501		} );
502		elementor.hooks.addAction( 'frontend/element_ready/column', function ($element) {
503			new FrontEndExtended({
504				$element: $element
505			});
506		} );
507		elementor.hooks.addAction( 'frontend/element_ready/container', function ($element) {
508			new FrontEndExtended({
509				$element: $element
510			});
511		} );
512
513		if (window.gemSettings.parallaxDisabled) {
514			var head  = document.getElementsByTagName('head')[0],
515				link  = document.createElement('style');
516			link.rel  = 'stylesheet';
517			link.type = 'text/css';
518			link.innerHTML = ".thegem-section-parallax.thegem-section-parallax-fixed .thegem-section-parallax-background { background-attachment: scroll !important; }";
519			head.appendChild(link);
520		}
521	});
522
523}(jQuery, window.elementorFrontend));

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.