PageSourceSearch

https://www.jenniferschrappe.com/themes/website/assets/js/theme.js?v.62a1ec

js jenniferschrappe.com collected 2026-10-04 07:33:13 UTC 19,738 bytes, 571 lines download raw bytes

1$(document).ready(function () {
2    initHideLogo();
3    initStickyLogo();
4    
5    jQuery(".preview_iframe").height(jQuery(window).height());
6    
7    // mmenu subnav indication helpers
8	var menu = $('#navigation_header'),
9		menuParent = menu.parent(),
10		menuList = menu.find('ul'),
11		subnavClass = 'sublevel-';
12
13	// loop through main navigation, define if there are any sub-levels and wrap into container with corresponding class name
14	// the function is needed because mmenu doesn't provide this functionality, and we need it to style sub-levels differently, with their own color schemes
15	navigationSublevelWrap(menuList, subnavClass);
16
17	// init mmenu
18	menu.clone().attr('id', 'navigation--mobile').mmenu({
19		extensions: ["multiline"]
20	});
21
22	// move hamburger button inside nav container
23	$('#hamburger').moveElement({
24		place: menuParent
25	});
26	
27	// init more nav - the rest of init that needs to be updated after switching from tablet/mobile to desktop is below
28	// should be called after mmenu init to eliminate  MORE nav's structure inheritance by mmenu
29	// this function will be called only once, on document.ready or window.load
30	// default structure for window.moreButton = $('<li class="navigation__item has-child more"> <a href="#">More</a> <ul class="navigation__list--sub"></ul> </li>'); uncomment if you need to change it
31	// WARN: navigation shouldn't be hidden with the usage of display:none, this will break nav init when switching from tablet/mobile to desktop. Please use, e.g. top: -9999px; left: -9999px; instead
32	if ($('.navigation').not('.mm-menu').data('more-button') === 1) {
33		// custom JS for theme
34		// class is needed to add specific classes for nav with enabled/disabled more_nav option
35		var	mainNav = $('.navigation').not('.mm-menu').find('.navigation__list').eq(0),
36			moreNavActiveClass = 'more_nav--active';
37
38		if(!mainNav.hasClass(moreNavActiveClass)) {
39			mainNav.addClass(moreNavActiveClass);
40		} else {
41			mainNav.removeClass(moreNavActiveClass);
42		}
43	}
44	// create layout for dotdotdot
45    $('.slider__caption').each(function() {
46        var lines = 4,
47            holder = $(this),
48            textH = parseInt(holder.css('line-height'));
49
50        holder.css({ maxHeight: textH * lines }).dotdotdot({
51            tolerance: 10,
52            watch: true,
53        });
54    });
55    
56     $('.featuredblock__description').each(function() {
57        var lines = 7,
58            holder = $(this),
59            textH = parseInt(holder.css('line-height'));
60
61        holder.css({ maxHeight: textH * lines }).dotdotdot({
62            tolerance: 10,
63            watch: true,
64        });
65    });
66	
67	
68	
69	// init more nav - the rest of init that needs to be updated after switching from tablet/mobile to desktop is below
70	// should be called after mmenu init to eliminate  MORE nav's structure inheritance by mmenu
71	// this function will be called only once, on document.ready or window.load
72	if ($('.navigation').not('.mm-menu').data('more-button') === 1) {
73		window.moreButton = $('<li class="navigation__item has-child more"> <a href="#">More</a> <ul class="navigation__list--sub"></ul> </li>');
74	}
75	// moreNavResizeUpdate function will be triggered on resize
76	// Put functions inside moreNavResizeUpdate that need to be updated on resize
77	function moreNavResizeUpdate() {
78		window.currentMenuWidth = window.menuHolder.outerWidth();
79		// this function will be needed on resize
80		addMoreButton();
81		// moreNav helper - re-initiate content wrapper for more nav content
82		moreNav();
83
84		$('.navigation:not(.mm-menu) .navigation__list>li>div>ul').desktopDropNav();
85		// accordion for desktop nav
86		$('.navigation:not(.mm-menu)').slideAccordion({
87			activeClass: 'open-close--active',
88			opener: '>a span.opener',
89			slider: '>div.slide',
90			animSpeed: 300
91		});
92
93		$('.navigation:not(.mm-menu) > ul').dropdownFit({
94			dropDown: '>li>div>ul'
95		});
96
97		// init after moreNav generation
98		$('.navigation:not(.mm-menu) > ul').setClassOnHover({
99			setOnElem: '.wrap__header'
100		});
101		
102		// main nav drop down max height calculation
103		$('.navigation:not(.mm-menu) .sublevel-1').each(function(){
104			$(this).scrollArea({
105				heightProp: 'maxHeight',
106				setScrollTo: '> .navigation__list--sub'
107			});
108		});
109	}
110	
111	// move image from bx-slider LI, as we need to move image itself with transition. Because background-position + relative/absolute breaks in Chrome on resize
112	// NOTE: special conditional (!$('.wrap__slider').hasClass('text-only') && !!$('.slider__image').length && !!$('.slider__image').css('background-image').length) is required,
113	// because in the theme there's a variation of text-only slider, it doesn't require images
114	// In common situations use only this conditional (!!$('.slider__image').length && !!$('.slider__image').css('background-image').length)
115	if(!$('.wrap__slider').hasClass('text-only') && !!$('.slider__image').length && !!$('.slider__image').css('background-image').length) {
116		if(!$('.slider__image--fixed').length) {
117			$('.slider__image').each(function(){
118				var imgHolder = $(this),
119					img = imgHolder.css('background-image').replace(/(url\(|\)|'|")/gi, '');
120
121				$(this).css('background-image', 'none');
122				imgHolder.append('<div class="slider__image--fixed" style="background-image: url(' + img + ')">');
123			});
124		}
125		
126		// set class if slider text area is empty
127		$('.slider__text').each(function() {
128			if(!$(this).children().length) {
129				$(this).addClass('empty');
130			}
131		});
132	}
133
134	// handle layout resize
135	ResponsiveHelper.addRange({
136		'1025..': {
137			on: function() {
138			    
139			    if ($('.navigation').not('.mm-menu').data('more-button') === 1) {
140					menuPrepare();
141					// init more nav
142					window.currentMenuWidth = window.menuHolder.outerWidth();
143					// this function will be needed on resize
144					addMoreButton();
145					// moreNav helper - re-initiate content wrapper for more nav content
146					moreNav();
147					// update moreNav on resize
148					$(window).on('resize orientationchange', moreNavResizeUpdate);
149				}
150
151				// prepare desktop nav for accordion
152				$('.navigation:not(.mm-menu) .navigation__list>li>div>ul').desktopDropNav();
153				// accordion for desktop nav
154				$('.navigation:not(.mm-menu)').slideAccordion({
155					activeClass: 'open-close--active',
156					opener: '>a span.opener',
157					slider: '>div.slide',
158					animSpeed: 300
159				});
160
161				// init dropdownFit
162				$('.navigation:not(.mm-menu) > ul').dropdownFit({
163					dropDown: '>li>div>ul'
164				});
165
166				// init after moreNav generation
167				$('.navigation:not(.mm-menu) > ul').setClassOnHover({
168					setOnElem: '.wrap__header'
169				});
170				
171				// init main nav drop down max height calculation
172				$('.navigation:not(.mm-menu) .sublevel-1').each(function(){
173					$(this).scrollArea({
174						heightProp: 'maxHeight',
175						setScrollTo: '> .navigation__list--sub'
176					});
177				});
178
179				// close mmenu on desktop
180				$('#navigation--mobile').data('mmenu').close();
181			},
182			off: function() {
183			    if ($('.navigation').not('.mm-menu').data('more-button') === 1) {
184					// destroy update moreNav on resize
185					$(window).off('resize orientationchange', moreNavResizeUpdate);
186				}
187				
188				// destroy main nav drop down max height calculation
189				setTimeout(function(){
190					$('.navigation:not(.mm-menu) .sublevel-1').each(function(){
191						if (typeof $(this).data('scrollArea') !== 'undefined') {
192							$(this).data('scrollArea').destroy();
193						}
194					});
195				}, 100);
196
197				// destroy dropdownFit
198				$('.navigation:not(.mm-menu) > ul').data('dropdownFit').destroy();
199			}
200		},
201		'768..': {
202			on: function() {
203			    // check if form component exists on page
204				if($('.form').length) {
205					// set timer for layoutContactForm func (wait until form is loaded from 3rd party service)
206					contactFormTimer = setInterval(function() {
207						// init function if form is created
208						if($('.form').find('.leadForm').length) {
209							// init contact form layout
210							$('.wrap__form.contact .form').layoutContactForm();
211							$('.wrap__form.appointment .form').layoutContactForm();
212
213							// init set top position in contact form
214                        	$('.wrap__form.contact .form-input-comments .input_container').setSpacing({
215                        		takeSizeFrom: '.wrap__form.contact .form-input-comments .label_container',
216                        		takeSize: 'height',
217                        		spacingProp: 'top'
218                        	});
219                        	$('.wrap__form.appointment .form-input-comments .input_container').setSpacing({
220                        		takeSizeFrom: '.wrap__form.appointment .form-input-comments .label_container',
221                        		takeSize: 'height',
222                        		spacingProp: 'top'
223                        	});
224
225							// remove timer
226							clearInterval(contactFormTimer);
227						}
228					}, 500);
229				}
230			},
231			off: function() {
232			    // destroy layoutContactForm
233				if (typeof $('.wrap__form.contact .form').data('layoutContactForm') !== 'undefined') {
234					$('.wrap__form.contact .form').data('layoutContactForm').destroy();
235				} else {
236					clearInterval(contactFormTimer); // remove timer if it has been activated, but window has been resized back to mobiles
237				}
238				if (typeof $('.wrap__form.appointment .form').data('layoutContactForm') !== 'undefined') {
239					$('.wrap__form.appointment .form').data('layoutContactForm').destroy();
240				} else {
241					clearInterval(contactFormTimer); // remove timer if it has been activated, but window has been resized back to mobiles
242				}
243
244				// destroy setSpacing
245				if (typeof $('.wrap__form.contact .form-input-comments .input_container').data('setSpacing') !== 'undefined') {
246					$('.wrap__form.contact .form-input-comments .input_container').data('setSpacing').destroy();
247				} else {
248					clearInterval(contactFormTimer); // remove timer if it has been activated, but window has been resized back to mobiles
249				}
250				if (typeof $('.wrap__form.appointment .form-input-comments .input_container').data('setSpacing') !== 'undefined') {
251					$('.wrap__form.appointment .form-input-comments .input_container').data('setSpacing').destroy();
252				} else {
253					clearInterval(contactFormTimer); // remove timer if it has been activated, but window has been resized back to mobiles
254				}
255			}
256		}
257	});
258
259	$('.wrap__layout').setSpacing({
260		takeSizeFrom: '.wrap__header',
261		takeSize: 'height'
262	});
263});
264
265
266// initialize fixed logo on scroll
267function initStickyLogo() {
268	jQuery('.wrap__content').fixedScrollBlock({
269		slideBlock: '.wrap__logoBody',
270		extraTop: 100,
271		extraBottom: -100
272	});
273}
274
275
276function initHideLogo() {
277    var win = $(window);
278    function scrollHandler() {
279        var bo = $(window).scrollTop();
280        if ( bo < 100 ) {
281            $('.wrap__logoHome').removeClass("sticky-logo");
282        }
283        else {
284            $('.wrap__logoHome').addClass("sticky-logo");
285        }
286        
287    }
288    ResponsiveHelper.addRange({
289        '1025..': {
290    	    on: function() {
291    			win.on('scroll', scrollHandler);
292    			$('.wrap__logoHome').removeClass("sticky-logo");
293    		},
294    		off: function() {
295    			// destroy layoutContactForm
296    			win.off('scroll', scrollHandler);
297    		}
298    	}
299    });
300}
301
302jQuery(window).load(function() {
303    initAnchors();
304});
305
306// initialize smooth anchor links
307function initAnchors() {
308	new SmoothScroll({
309		anchorLinks: '.button-anchor',
310		easing: 'linear',
311		animMode: 'speed',
312		animSpeed: 1500
313	});
314}
315
316/*!
317 * SmoothScroll module
318 */
319;(function($, exports) {
320	// private variables
321	var page,
322		win = $(window),
323		activeBlock, activeWheelHandler,
324		wheelEvents = ('onwheel' in document || document.documentMode >= 9 ? 'wheel' : 'mousewheel DOMMouseScroll');
325
326	// animation handlers
327	function scrollTo(offset, options, callback) {
328		// initialize variables
329		var scrollBlock;
330		if (document.body) {
331			if (typeof options === 'number') {
332				options = { duration: options };
333			} else {
334				options = options || {};
335			}
336			page = page || $('html, body');
337			scrollBlock = options.container || page;
338		} else {
339			return;
340		}
341
342		// treat single number as scrollTop
343		if (typeof offset === 'number') {
344			offset = { top: offset };
345		}
346
347		// handle mousewheel/trackpad while animation is active
348		if (activeBlock && activeWheelHandler) {
349			activeBlock.off(wheelEvents, activeWheelHandler);
350		}
351		if (options.wheelBehavior && options.wheelBehavior !== 'none') {
352			activeWheelHandler = function(e) {
353				if (options.wheelBehavior === 'stop') {
354					scrollBlock.off(wheelEvents, activeWheelHandler);
355					scrollBlock.stop();
356				} else if (options.wheelBehavior === 'ignore') {
357					e.preventDefault();
358				}
359			};
360			activeBlock = scrollBlock.on(wheelEvents, activeWheelHandler);
361		}
362
363		// start scrolling animation
364		scrollBlock.stop().animate({
365			scrollLeft: offset.left,
366			scrollTop: offset.top
367		}, options.duration, function() {
368			if (activeWheelHandler) {
369				scrollBlock.off(wheelEvents, activeWheelHandler);
370			}
371			if ($.isFunction(callback)) {
372				callback();
373			}
374		});
375	}
376
377	// smooth scroll contstructor
378	function SmoothScroll(options) {
379		this.options = $.extend({
380			anchorLinks: 'a[href^="#"]',	// selector or jQuery object
381			container: null,		// specify container for scrolling (default - whole page)
382			extraOffset: null,		// function or fixed number
383			activeClasses: null,	// null, "link", "parent"
384			easing: 'swing',		// easing of scrolling
385			animMode: 'duration',	// or "speed" mode
386			animDuration: 800,		// total duration for scroll (any distance)
387			animSpeed: 1500,		// pixels per second
388			anchorActiveClass: 'anchor-active',
389			sectionActiveClass: 'section-active',
390			wheelBehavior: 'stop', // "stop", "ignore" or "none"
391			useNativeAnchorScrolling: false // do not handle click in devices with native smooth scrolling
392		}, options);
393		this.init();
394	}
395	SmoothScroll.prototype = {
396		init: function() {
397			this.initStructure();
398			this.attachEvents();
399		},
400		initStructure: function() {
401			var self = this;
402
403			this.container = this.options.container ? $(this.options.container) : $('html,body');
404			this.scrollContainer = this.options.container ? this.container : win;
405			this.anchorLinks = jQuery(this.options.anchorLinks).filter(function() {
406				return document.getElementById(this.getAttribute('href').slice(1));
407			});
408		},
409		getAnchorTarget: function(link) {
410			// get target block from link href
411			var targetId = $(link).attr('href');
412			return $(targetId.length > 1 ? targetId : 'html');
413		},
414		getTargetOffset: function(block) {
415			// get target offset
416			var blockOffset = block.offset().top;
417			if (this.options.container) {
418				blockOffset -= this.container.offset().top - this.container.prop('scrollTop');
419			}
420
421			// handle extra offset
422			if (typeof this.options.extraOffset === 'number') {
423				blockOffset -= this.options.extraOffset;
424			} else if (typeof this.options.extraOffset === 'function') {
425				blockOffset -= this.options.extraOffset(block);
426			}
427			return { top: blockOffset };
428		},
429		attachEvents: function() {
430			var self = this;
431
432			// handle active classes
433			if (this.options.activeClasses && this.anchorLinks.length) {
434				// cache structure
435				this.anchorData = [];
436
437				for (var i = 0; i < this.anchorLinks.length; i++) {
438					var link = jQuery(this.anchorLinks[i]),
439						targetBlock = self.getAnchorTarget(link),
440						anchorDataItem;
441
442					$.each(self.anchorData, function(index, item) {
443						if (item.block[0] === targetBlock[0]) {
444							anchorDataItem = item;
445						}
446					});
447
448					if (anchorDataItem) {
449						anchorDataItem.link = anchorDataItem.link.add(link);
450					} else {
451						self.anchorData.push({
452							link: link,
453							block: targetBlock
454						});
455					}
456				};
457
458				// add additional event handlers
459				this.resizeHandler = function() {
460					self.recalculateOffsets();
461				};
462				this.scrollHandler = function() {
463					self.refreshActiveClass();
464				};
465
466				this.recalculateOffsets();
467				this.scrollContainer.on('scroll', this.scrollHandler);
468				win.on('resize', this.resizeHandler);
469			}
470
471			// handle click event
472			this.clickHandler = function(e) {
473				self.onClick(e);
474			};
475			if (!this.options.useNativeAnchorScrolling) {
476				this.anchorLinks.on('click', this.clickHandler);
477			}
478		},
479		recalculateOffsets: function() {
480			var self = this;
481			$.each(this.anchorData, function(index, data) {
482				data.offset = self.getTargetOffset(data.block);
483				data.height = data.block.outerHeight();
484			});
485			this.refreshActiveClass();
486		},
487		refreshActiveClass: function() {
488			var self = this,
489				foundFlag = false,
490				containerHeight = this.container.prop('scrollHeight'),
491				viewPortHeight = this.scrollContainer.height(),
492				scrollTop = this.options.container ? this.container.prop('scrollTop') : win.scrollTop();
493
494			// user function instead of default handler
495			if (this.options.customScrollHandler) {
496				this.options.customScrollHandler.call(this, scrollTop, this.anchorData);
497				return;
498			}
499
500			// sort anchor data by offsets
501			this.anchorData.sort(function(a, b) {
502				return a.offset.top - b.offset.top;
503			});
504			function toggleActiveClass(anchor, block, state) {
505				anchor.toggleClass(self.options.anchorActiveClass, state);
506				block.toggleClass(self.options.sectionActiveClass, state);
507			}
508
509			// default active class handler
510			$.each(this.anchorData, function(index) {
511				var reverseIndex = self.anchorData.length - index - 1,
512					data = self.anchorData[reverseIndex],
513					anchorElement = (self.options.activeClasses === 'parent' ? data.link.parent() : data.link);
514
515				if (scrollTop >= containerHeight - viewPortHeight) {
516					// handle last section
517					if (reverseIndex === self.anchorData.length - 1) {
518						toggleActiveClass(anchorElement, data.block, true);
519					} else {
520						toggleActiveClass(anchorElement, data.block, false);
521					}
522				} else {
523					// handle other sections
524					if (!foundFlag && (scrollTop >= data.offset.top - 1 || reverseIndex === 0)) {
525						foundFlag = true;
526						toggleActiveClass(anchorElement, data.block, true);
527					} else {
528						toggleActiveClass(anchorElement, data.block, false);
529					}
530				}
531			});
532		},
533		calculateScrollDuration: function(offset) {
534			var distance;
535			if (this.options.animMode === 'speed') {
536				distance = Math.abs(this.scrollContainer.scrollTop() - offset.top);
537				return (distance / this.options.animSpeed) * 1000;
538			} else {
539				return this.options.animDuration;
540			}
541		},
542		onClick: function(e) {
543			var targetBlock = this.getAnchorTarget(e.currentTarget),
544				targetOffset = this.getTargetOffset(targetBlock);
545
546			e.preventDefault();
547			scrollTo(targetOffset, {
548				container: this.container,
549				wheelBehavior: this.options.wheelBehavior,
550				duration: this.calculateScrollDuration(targetOffset)
551			});
552		},
553		destroy: function() {
554			if (this.options.activeClasses) {
555				win.off('resize', this.resizeHandler);
556				this.scrollContainer.off('scroll', this.scrollHandler);
557			}
558			this.anchorLinks.off('click', this.clickHandler);
559		}
560	};
561
562	// public API
563	$.extend(SmoothScroll, {
564		scrollTo: function(blockOrOffset, durationOrOptions, callback) {
565			scrollTo(blockOrOffset, durationOrOptions, callback);
566		}
567	});
568
569	// export module
570	exports.SmoothScroll = SmoothScroll;
571}(jQuery, this));

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.