PageSourceSearch

https://capital.ba/wp-content/themes/smart-mag/js/theme.js?ver=10.2.1

js capital.ba collected 2026-10-02 06:09:35 UTC 53,943 bytes, 2,257 lines download raw bytes

1/**
2 * SmartMag Theme Frontend JS.
3 * 
4 * @copyright 2024 ThemeSphere.
5 */
6"use strict";
7
8if (!Bunyad) {
9	var Bunyad = {};
10}
11
12Bunyad.theme = (function($) {
13	
14	var hasTouch = false,
15		responsiveMenu = false,
16		isIframe = window.BunyadIsIframe || false,
17		schemeKey = window.BunyadSchemeKey || 'bunyad-scheme';
18		
19	if (!window.requestIdleCallback) {
20		window.requestIdleCallback = requestAnimationFrame;
21	}
22	
23	// module
24	var self = {
25		init: function() {
26
27			// Detect touch capability dynamically.
28			$(window).on('touchstart', function() {
29				// First touch.
30				if (!hasTouch) {
31					$('body').addClass('touch');
32					self.touchNav();
33				}
34
35				hasTouch = true;
36			});
37
38			this.imageEffects();
39			
40			// Setup all sliders
41			this.sliders();
42			this.contentSlideshow();
43			
44			// Setup mobile header and navigation
45			this.header();
46			this.responsiveNav();
47			this.megaMenus();
48			
49			// Start the news ticker
50			this.newsTicker();
51			
52			// Setup the lightbox
53			requestIdleCallback(this.lightbox);
54
55			// Search modal
56			this.searchModal();
57			
58			// Use sticky sidebar if enabled
59			this.initStickySidebar();
60			
61			// User ratings
62			this.userRatings();
63
64			this.tabWidget();
65
66			/**
67			 * Woocommerce
68			 */ 
69			$('.woocommerce-ordering .drop li a').on('click', function(e) {
70				var form = $(this).closest('form');
71				form.find('[name=orderby]').val($(this).parent().data('value'));
72				form.trigger('submit');
73				
74				e.preventDefault();
75			});
76
77			$(document).on('ts-ajax-pagination', e => {
78				$(document).trigger('scroll.TSS');
79				Bunyad.sliders();
80			});
81
82			// Lower priority.
83			requestIdleCallback(() => {
84				self.contextualBinds(document);
85
86				// Nav icons class if missing
87				$('.navigation i.only-icon').each(function() {	
88					var el = $(this).closest('li');
89					if (!el.hasClass('only-icon')) {
90						el.addClass('only-icon');
91					}
92				});
93
94				// Single show comments.
95				$(document).on('click', '.ts-comments-show', function() {
96					$(this).hide()
97						.closest('.comments')
98						.find('.ts-comments-hidden').show();
99					return false;
100				});
101	
102			}, {timeout: 1000});
103		},
104
105		/**
106		 * @param {HTMLDocument|HTMLElement} document
107		 */
108		contextualBinds: function(document)
109		{
110			// Fit videos to container.
111			requestAnimationFrame(() => {
112				$('.featured-vid, .post-content', document).fitVids();
113			});
114
115			// Ratings
116			$('.review-number', document).each(function() {
117				var raw = $(this).find('span:not(.progress)').html(),
118					progress = parseFloat(raw);
119				
120				$(this).find('.progress').css('width', (raw.search('%') === -1 ? (Math.round(progress / 10 * 100)) + '%' : progress));
121			});
122
123			$('.review-box ul li .bar', document).each(function() {
124				$(this).data('width', $(this)[0].style.width).css('width', 0);
125			});
126		
127			new Bunyad_InView(
128				document.querySelectorAll('.review-box ul li', document),
129				entry => {
130					const bar = $(entry.target).find('.bar');
131					bar.addClass('appear').css('width', bar.data('width'));
132				},
133				{once: true}
134			);
135
136			// Single top social sharing view all buttons
137			$('.post-share-b .show-more', document).on('click', function() {
138				$(this).parent().addClass('all');
139				return false;
140			});
141
142			$('.service.s-link').on('click', e => {
143				// Temporary input copy trick as clipboard API only available over https.
144				const temp = document.createElement('input');
145				Object.assign(temp, {
146					value: location.href,
147					style: {position: 'absolute', left: '-200vw'}
148				});
149				document.body.prepend(temp);
150
151				temp.select();
152				document.execCommand('copy');
153				temp.remove();
154
155				let snackbar = document.querySelector('.ts-snackbar.copy-link');
156				if (!snackbar) {
157					snackbar = document.createElement('div');
158
159					const message = e.currentTarget.querySelector('[data-message]').dataset.message;
160					snackbar.className = 'ts-snackbar copy-link';
161					snackbar.innerHTML = `<span>${message}</span><i class="tsi tsi-close"></i>`;
162	
163					document.body.append(snackbar);
164				}
165
166				if (snackbar.classList.contains('ts-snackbar-active')) {
167					return false;
168				}
169
170				requestAnimationFrame(() => snackbar.classList.add('ts-snackbar-active'));
171
172				const hideMessage = () => {
173					snackbar.classList.remove('ts-snackbar-active');
174					snackbar.hide();
175					return false;
176				}
177
178				setTimeout(hideMessage, 5 * 1000);
179				$(snackbar).on('click', '.tsi-close', hideMessage);
180
181				return false;
182			});
183
184			// Go back handler for 404.
185			$('.go-back').on('click', function() { 
186				window.history.back();
187				return false;
188			});
189		},
190
191		/**
192		 * Reinit common theme functionality, in a context, usually ALP feature.
193		 * 
194		 * @param {HTMLDocument|HTMLElement} context
195		 */
196		reInit: function(context) {
197
198			// Only for elements.
199			if (context !== document) {
200				this.imageEffects(context.querySelectorAll('img'));
201			}
202
203			this.contextualBinds(context);
204			this.lightbox();
205			this.contentSlideshow();
206			this.userRatings();
207
208			setTimeout(this.stickySidebar, 50);
209
210			Bunyad.sliders();
211		},
212
213		/**
214		 * Image scroll animations.
215		 */
216		imageEffects: function(elements) {
217
218			// Image effects - remove temp CSS when loaded.
219			if (!$('body').hasClass('img-effects')) {
220				return;
221			}
222
223			if (!elements) {
224				elements = document.querySelectorAll('.main-wrap .wp-post-image, .post-content img');
225			}
226
227			elements.forEach(e => e.classList.add('no-display'));
228			$('.bunyad-img-effects-css').remove();
229
230			new Bunyad_InView(
231				elements,
232				entry => entry.target.classList.add('appear'),
233				{once: true},
234				() => $(elements).addClass('appear')
235			);
236		},
237		
238		newsTicker: function() {
239
240			$('.trending-ticker').each(function() {
241
242				if (!$(this).find('li.active').length) {
243					$(this).find('li:first').addClass('active');
244				}
245				
246				let ticker   = $(this);
247				let interval = false;
248				const delay  = parseInt(ticker.data('delay') || 5) * 1000;
249				
250				const setTick = () => {
251					var active = ticker.find('li.active');
252					active.fadeOut(function() {
253
254						var next = active.next();
255						if (!next.length) {
256							next = ticker.find('li:first');
257						}
258						
259						next.addClass('active').fadeIn();
260						active.removeClass('active');
261					});
262				}
263				
264				const startTicker = () => {
265					if (interval) {
266						return;
267					}
268
269					interval = window.setInterval(setTick, delay);
270				}
271
272				startTicker();
273
274				ticker.on('mouseenter', 'li', () => {
275					clearInterval(interval);
276					interval = false;
277				});
278
279				ticker.on('mouseleave', 'li', startTicker);
280			});
281		},
282		
283		header: function() {
284			
285			const toggleDarkMode = (scheme) => {
286				const prevScheme = (scheme == 'dark' ? 'light' : 'dark');
287
288				// Set initial scheme.
289				const htmlClass = $('html').data('origClass');
290				let initialScheme;
291				if (htmlClass) {
292					initialScheme = htmlClass.includes('s-dark') ? 'dark' : 'light';
293				}
294
295				if ($('html').data('autoDark')) {
296					initialScheme = 'dark';
297				}
298
299				if (scheme !== initialScheme) {
300					localStorage.setItem(schemeKey, scheme);
301				}
302				else {
303					localStorage.removeItem(schemeKey);
304				}
305				
306				$('html')
307					.removeClass(`s-${prevScheme} site-s-${prevScheme}`)
308					.addClass(`s-${scheme} site-s-${scheme}`);
309			};
310			
311			$('.scheme-switcher .toggle').on('click', function() {
312				const ele  = $(this);
313				let scheme = ele.hasClass('toggle-dark') ? 'dark' : 'light';
314
315				toggleDarkMode(scheme);
316
317				// Notify auto-loaded posts.
318				document.querySelectorAll('iframe.spc-auto-load-post').forEach(element => {
319					element.contentWindow.postMessage({
320						action: 'toggle-dark-mode',
321						scheme
322					}, '*');
323				});
324
325				return false;
326			});
327
328			// Listen and change in auto-loaded posts.
329			if (isIframe) {
330				window.addEventListener('message', e => {
331					if (!e.data || e.data.action !== 'toggle-dark-mode') {
332						return;
333					}
334
335					toggleDarkMode(e.data.scheme);
336				});
337			}
338		},
339		
340		/**
341		 * Setup the responsive nav events and markup
342		 */
343		responsiveNav: function() {
344			this.initResponsiveNav();
345		},
346
347		initResponsiveNav: function() {
348			
349			if (responsiveMenu || !$('.off-canvas').length) {
350				responsiveMenu = true;
351				return;
352			}
353			
354			// Set responsive initialized
355			responsiveMenu = true;
356
357			// Test for overlay/non-obstrusive scrollbar (MacOS).
358			const hasOverlayScroll = () => {
359				const ele = document.createElement("div");
360				ele.setAttribute("style", "width:30px;height:30px;overflow:scroll;opacity:0");
361				document.body.appendChild(ele);
362
363				const result = ele.offsetWidth === ele.clientWidth;
364				document.body.removeChild(ele);
365				
366				return result;
367			}
368
369			const setupScroll = () => {
370				if (!hasTouch && !hasOverlayScroll()) {
371					document.body.classList.add("has-scrollbar");
372	
373					// Reflow to fix scrollbars.
374					const ele = $('.off-canvas-content');
375					ele.css('display', 'block');
376	
377					requestAnimationFrame(function() {
378						ele.css('display', 'flex');
379					});
380				}
381			}
382
383			const setupMenuHandlers = () => {
384				// Setup mobile menu click handlers
385				$('.mobile-menu li > a').each(function() {
386					
387					if ($(this).parent().children('ul').length) {
388						$('<span class="chevron"><i class="tsi tsi-chevron-down"></i></span>').insertAfter($(this));
389					}
390				});
391				
392				$('.mobile-menu li .chevron').on('click', function() {
393					$(this).closest('li').find('ul')
394						.first()
395						.parent()
396						.toggleClass('active item-active');
397
398					return false;
399				});
400			}
401
402			const setupMobileMenu = () => {
403				var container = $('.mobile-menu-container');
404
405				// No items for mobile menu? Grab from others
406				if (!container.find('.mobile-menu').children().length) {
407					
408					// Merge existing menus
409					var mobileMenu = container.find('.mobile-menu'),
410						mainMenu = $('.navigation-main .menu');
411						
412					if (!mainMenu.length) {
413						return;
414					}
415
416					var menu = mainMenu.children().clone();
417					
418					// add category mega menu links
419					menu.find('.mega-menu .sub-cats').each(function() {
420						var container = $(this).closest('.menu-item');
421						
422						container.append($(this).find('.sub-nav'));
423						container.find('.sub-nav').replaceWith(function() {
424							return $('<ul />', {html: $(this).html()});
425						});
426						
427						$(this).remove();
428					});
429					
430					mobileMenu.append(menu);
431				}
432			}
433
434			// Delayed setup.
435			let isMenuSetup = false;
436			const initSetup = () => {
437				setupScroll();
438				setupMobileMenu();
439				setupMenuHandlers();
440				
441				isMenuSetup = true;
442			};
443
444			// Bunyad.util.onLoad(() => requestIdleCallback(initSetup, {timeout: 1000}));
445			const showMenu = () => {
446				if (!isMenuSetup) {
447					initSetup();
448				}
449				
450				$('html').toggleClass('off-canvas-active');
451			}
452		
453			$('.mobile-head .menu-icon a, .smart-head .offcanvas-toggle')
454				.on('click', function() {
455					$(document).trigger('ts-sticky-bar-remove');
456					showMenu();
457				});
458
459			// Off-canvas close
460			$('.off-canvas .close').on('click', function() {
461				showMenu();
462				return false;
463			});
464
465			$('.off-canvas-backdrop').on('click', function() {
466				$('.off-canvas .close').click();
467				return false;
468			})
469		},
470
471		megaMenus: function() {
472			const media = window.matchMedia('(min-width: 940px)');
473			const init  = e => {
474				if (e.matches) {
475					this.initMegaMenus();
476				}
477				media.removeListener(init);
478			};
479
480			init(media);
481			media.addListener(init);
482		},
483
484		initMegaMenus: function() {
485			// Bind events for mega menus
486			$('.navigation .mega-menu-a').each(function() {
487				
488				var menu = $(this),
489				    number = menu.find('.recent-posts').data('columns');
490				
491				// Init mega menus
492				var default_mega_menu = function f() {
493					menu.find('.posts').last().addClass('active');
494					return f;
495				}();
496
497				// Mega-menu sub-cats handling.
498				menu.find('.menu-item').on('mouseenter', function() {
499
500					var id = parseInt( $(this).attr('class').match(/menu-cat-(\d+)/)[1] || 0 ),
501					    menu = $(this).parents('.mega-menu'),
502					    view_all = $(this).hasClass('view-all');
503				
504					if (!view_all && !id) {
505						return;
506					}
507					
508					// Remove other actives
509					menu.find('.active, .current-menu-item').removeClass('active current-menu-item');
510
511					// Activate current
512					$(this).addClass('current-menu-item');
513					var posts = menu.find('[data-id=' + id + ']').addClass('active');
514					
515					return false;
516				});
517				
518				// Reset mega menu state on fresh hover
519				menu.parent().on('mouseenter', function() {
520					
521					var menu = $(this).find('.mega-menu');
522					
523					// reset at beginning
524					menu.find('.active, .current-menu-item')
525						.removeClass('active current-menu-item');
526
527					$(this)[0].offsetWidth; // reflow
528					default_mega_menu();
529				});
530				
531			});
532		},
533		
534		/**
535		 * Setup touch navigation for larger touch devices.
536		 */
537		touchNav: function() {
538			
539			var targets = $('.menu:not(.mobile-menu) a'),
540				open_class = 'item-active',
541				child_tag = 'ul, .mega-menu';
542			
543			targets.each(function() {
544				
545				var $this = $(this),
546					$parent = $this.parent('li'),
547					$siblings = $parent.siblings().find('a');
548				
549				$this.on('click', function(e) {
550
551					var $this = $(this);
552					e.stopPropagation();
553					
554					$siblings.parent('li').removeClass(open_class);
555					
556					// has a child? open the menu on tap
557					if (!$this.parent().hasClass(open_class) && $this.next(child_tag).length > 0 && !$this.parents('.mega-menu.links').length) {
558						e.preventDefault();
559						$this.parent().addClass(open_class);
560					}
561				});
562			});
563			
564			// close all menus
565			$(document).on('click', function(e) {
566				if (!$(e.target).is('.menu') && !$(e.target).parents('.menu').length) {
567					targets.parent('li').removeClass(open_class);
568				}
569			});
570		},
571
572		initStickySidebar: function() {
573
574			let hasInit  = false;
575			const events = 'resize orientationchange scroll';
576			const init = () => {
577				if (hasInit) {
578					return;
579				}
580
581				if (document.documentElement.clientWidth >= 940) {
582					self.stickySidebar();
583					hasInit = true;
584					$(window).off(events, init);
585				}
586			}
587
588			$(window).on(events, init);
589			Bunyad.util.onLoad(init);
590		},
591		
592		/**
593		 * Setup sticky sidebar
594		 */
595		stickySidebar: function() {
596
597			const stickyHeaderHeight = () => {
598				const stickyHead = $('.smart-head-sticky');
599				let addHeight = 0;
600				if (stickyHead.length) {
601					let height = window.getComputedStyle(stickyHead[0]).getPropertyValue('--head-h');
602					addHeight = parseInt(height) || 0;
603				}
604
605				return addHeight;
606			}
607
608			const initNative = () => {
609				if (!$('.ts-sticky-native').length) {
610					return;
611				}
612
613				let baseTop = 20;
614				if ($('.admin-bar').length) {
615					baseTop += $('#wpadminbar').height();
616				}
617				
618				const setTop = top => $('body').css('--ts-sticky-top', top + 'px');				
619
620				$(document).on('sticky-bar-show', () => setTop(baseTop + stickyHeaderHeight()));
621				$(document).on('sticky-bar-hide', () => setTop(baseTop));
622			}
623
624			const init = (addExtra) => {
625
626				const sticky = $('.ts-sticky-col, .main-sidebar[data-sticky=1]');
627				if (!sticky.length) {
628					return;
629				}
630				
631				let add = 20;
632				if ($('.admin-bar').length) {
633					add += $('#wpadminbar').height();
634				}
635				add += addExtra || 0;
636
637				sticky.each(function() {
638
639					var stickyCol = $(this);
640					if (stickyCol.data('init') || stickyCol.hasClass('.ts-sticky-native')) {
641						return;
642					}
643
644					stickyCol.data('init', 1);
645					
646					// Add wrapper class if missing
647					if (!stickyCol.find('.theiaStickySidebar').length) {
648						stickyCol.find('.elementor-widget-wrap').first().addClass('theiaStickySidebar');
649						stickyCol.css({display: 'block'});
650					}
651
652					stickyCol.theiaStickySidebar({
653						minWidth: 940, 
654						updateSidebarHeight: false,
655						additionalMarginTop: add,
656						additionalMarginBottom: 20,
657						disableOnResponsiveLayouts: false
658					});
659					
660					// Update TSS internal options on sticky header on / off.
661					const options = stickyCol.data('tss-options');
662
663					if (!options) {
664						return;
665					}
666
667					const stickyInner       = stickyCol.find('.theiaStickySidebar');
668					const stickyColHeight   = stickyCol.outerHeight();
669					const stickyInnerHeight = stickyInner.outerHeight();
670
671					$(document).on('sticky-bar-show', () => {
672						const newMarginTop = add + stickyHeaderHeight();
673
674						// Do nothing if the height will exceed. This would cause a bug at bottom of sticky turning static.
675						if (stickyInnerHeight + newMarginTop >= stickyColHeight) {
676							return;
677						}
678
679						if (newMarginTop !== options.additionalMarginTop) {
680							// Ignore unexpected negative transform of scrolling down.
681							if (!sticky.css('transform').includes('-')) {
682								options.additionalMarginTop = newMarginTop;
683								setTimeout(() => $(window).trigger('scroll.TSS'), 300);
684							}
685						}
686					});
687
688					// Reset margins on sticky bar hide.
689					$(document).on('sticky-bar-hide', () => {
690						options.additionalMarginTop = add;
691						setTimeout(() => $(window).trigger('scroll.TSS'), 300);
692					});
693				});
694			};
695
696			init();
697			initNative();
698			
699			// for iframe loads in sidebar
700			Bunyad.util.onLoad(() => setTimeout(() => {
701				$(window).trigger('resize.TSS');
702			}, 3500));
703		},
704		
705		/**
706		 * Setup legacy slider and slideshow.
707		 */
708		sliders: function() {
709
710			if (!$.fn.flexslider) {
711				return;
712			}
713			
714			var is_rtl = ($('html').attr('dir') == 'rtl' ? true : false);
715			
716			// Classic / Legacy slider.
717			var slider = $('.classic-slider');
718
719			if (!slider.length) {
720				return;
721			}
722			
723			slider.find('.flexslider').flexslider({
724				controlNav: true,
725				animationSpeed: slider.data('animation-speed'),
726				animation: slider.data('animation'),
727				slideshowSpeed: slider.data('slide-delay'),
728				manualControls: '.main-featured .flexslider .pages a',
729				pauseOnHover: true,
730				start: function() {
731					$('.main-featured .slider').css('opacity', 1);
732				},
733				rtl: is_rtl
734			});
735			
736			// Dynamic pagination - if not at default of 5
737			var pages  = slider.find('.pages'),
738				number = parseInt(pages.data('number'));
739			
740			if (number && number !== 5) {
741				var width = (100 - ((number + 1) * 0.284900285)) / number;
741  // 100 - (slides + 1 * margin-left) / slides
742				pages.find('a').css('width', width + '%');
743			}
744		},
745
746		/**
747		 * Post Content Slideshow: AJAX
748		 */
749		contentSlideshow: function() {
750
751			var slideshow_cache = {},
752				slideshow_wrap  = '.post-slideshow .post-pagination-large';
753			
754			if ($(slideshow_wrap).length && !$(slideshow_wrap).data('init') && $(slideshow_wrap).data('type') == 'ajax') {
755			
756				var processing;
757				$(slideshow_wrap).data('init', true);
758
759				$('.main-content').on('click', '.post-slideshow .post-pagination-large .links a', function() {
760					
761						// showing on home-page?
762						if ($('body').hasClass('page')) {
763							return;
764						}
765						
766						// bottom links, scroll top
767						var scroll;
768						if ($(this).parents('.bottom').length) {
769							scroll = true;
770						}
771						
772						// abort existing request
773						if (processing && processing.hasOwnProperty('abort')) {
774							processing.abort();
775						}
776					
777						var parent = $(this).closest('.post-slideshow'),
778							url    = $(this).attr('href');
779						
780						parent.find('.content-page').removeClass('active').addClass('hidden previous');
781					
782						var show_slide = function(data) {
783							
784							// change browser url
785							if (history.pushState) {
786								history.pushState({}, '', url);
787							}
788							
789							var page = $(data).find('.post-slideshow');
790							
791							if (page.length) {
792								parent.find('.post-pagination-large').html(page.find('.post-pagination-large').html());
793								parent.find('.content-page').after(page.find('.content-page').addClass('hidden loading'));
794								
795								setTimeout(function() {
796
797									if (scroll) {
798										$('html, body').animate({scrollTop: parent.offset().top - 50}, 200);
799									}
800									
801									parent.find('.content-page.previous').remove();
802									parent.find('.content-page.loading')
803										.removeClass('previous hidden loading')
804										.find('img').addClass('appear');
805								}, 1);
806							}
807							
808							processing = null;
809							
810						};
811						
812						// in cache?
813						if (slideshow_cache[url]) {
814							show_slide(slideshow_cache[url]);
815						}
816						else {
817							
818							// get via ajax
819							processing = $.get(url, function(data) {
820								slideshow_cache[url] = data;
821								show_slide(data);
822								
823							});
824						}
825						
826						return false;
827				});
828				
829				// keyboard nav
830				$(document).on('keyup', function(e) {				
831						if (e.which == 37) {
832							$(slideshow_wrap).find('.prev').parent().click();
833						}
834						else if (e.which == 39) {
835							$(slideshow_wrap).find('.next').parent().click();
836						}
837				});
838				
839			}
840		},
841				
842		/**
843		 * User Ratings handling
844		 */
845		userRatings: function() {
846			
847			var compute_percent = function(e) {
848				
849				var offset = $(this).offset(),
850				    position, percent;
851				
852				// count from right for RTL
853				if ($('html').attr('dir') == 'rtl')  {
854					offset.left = offset.left + $(this).outerWidth();
855				}
856				
857				position = Math.abs(e.pageX - Math.max(0, offset.left));
858				percent  = Math.min(100, Math.round(position / $(this).width() * 100));
859				
860				return percent;
861			};
862
863			// percent or points?
864			var is_points = true,
865				scale = parseInt($('.review-box .value-title').text()) || 10;
866			
867			if ($('.review-box .overall .percent').length) {
868				is_points = false;
869			}
870			
871			// update the bar and percent/points on hover
872			$('.user-ratings .main-stars, .user-ratings .rating-bar').on('mousemove mouseenter mouseleave', 
873				function(e) {
874				
875					// set main variables
876					var bar = $(this).find('span'),
877						user_ratings = $(this).closest('.user-ratings');
878				
879					bar.css('transition', 'none');
880					
881					if (user_ratings.hasClass('voted')) {
882						return;
883					}
884				
885					// hover over?
886					if (e.type == 'mouseleave') {
887						bar.css('width', bar.data('orig-width'));
888						user_ratings.find('.hover-number').hide();
889						user_ratings.find('.rating').show();
890						return;
891					}
892					
893					var percent = compute_percent.call(this, e);
894					
895					if (!bar.data('orig-width')) {
896						bar.data('orig-width', bar[0].style.width);
897					}
898					
899					bar.css('width', percent + '%');
900					user_ratings.find('.rating').hide();
901					user_ratings.find('.hover-number').show().text((is_points ? +parseFloat(percent / 100 * scale).toFixed(1) : percent + '%'));
902				}
903			);
904			
905			// add the rating
906			$('.user-ratings .main-stars, .user-ratings .rating-bar').on('click', function(e) {
907				
908				// set main variables
909				var bar = $(this).find('span'),
910					user_ratings = $(this).closest('.user-ratings');
911				
912				if (user_ratings.hasClass('voted')) {
913					return;
914				}
915				
916				// setup ajax post data
917				var post_data = {
918						'action': 'bunyad_rate', 
919						'id': user_ratings.data('post-id'), 
920						'rating': compute_percent.call(this, e)
921				};
922				
923				// get current votes
924				var votes = user_ratings.find('.number'),
925					cur_votes = parseInt(votes.text()) || 0;
926				
927				user_ratings.css('opacity', '0.3');
928				bar.data('orig-width', bar[0].style.width);
929				
930				// add to votes and disable further voting 
931				votes.text((cur_votes + 1).toString());
932				
933				$(this).trigger('mouseleave');
934				user_ratings.addClass('voted');
935				
936				$.post(Bunyad.ajaxurl, post_data, function(data) {
937					
938					// update data
939					if (data === Object(data)) {
940
941						// change rating
942						var cur_rating = user_ratings.find('.rating').text();
943						user_ratings.find('.rating').text( cur_rating.search('%') !== -1 ? data.percent + ' %' : data.decimal );
944						
945						bar.css('width', data.percent + '%');
946						bar.data('orig-width', data.percent);
947					}
948					
949					user_ratings.hide().css('opacity', 1).fadeIn('slow');			
950				}, 'json');
951			});
952		},
953		
954		tabWidget: function() {
955			$(document).on('click', '.widget-tabbed .heading a', function() {
956				
957				var tab = $(this).data('tab'),
958					tabs_data = $(this).closest('.widget-tabbed').find('.tabs-data'),
959					parent = $(this).parent().parent(),
960					active = parent.find('.active');
961				
962				if (!active.length) {
963					active = parent.find('li:first-child');
964				}
965
966				active.removeClass('active').addClass('inactive');
967				$(this).parent().addClass('active').removeClass('inactive');
968				
969				// hide current and show the clicked one
970				var active_data = tabs_data.find('.tab-posts.active');
971				if (!active_data.length) {
972					active_data = tabs_data.find('.tab-posts:first-child');
973				}
974				
975				active_data.hide();
976				tabs_data.find('#recent-tab-' + tab).fadeIn().addClass('active').removeClass('inactive');
977
978				return false;
979			});
980		},
981
982		searchModal: function() {
983			let isSetup = false;
984			const target = $('.smart-head .search-icon');
985
986			const setup = () => {
987				if (isSetup || !$.fn.magnificPopup) {
988					return;
989				}
990
991				isSetup = true;
992				const scheme = $('.search-modal-wrap').data('scheme') == 'dark' ? ' s-dark' : '';
993
994				// Search modal
995				target.magnificPopup({
996					items: {
997						src: '.search-modal-wrap',
998						type: 'inline'
999					},
1000					removalDelay: 400,
1001					focus: 'input',
1002					closeBtnInside: false,
1003					closeOnBgClick: false,
1004					mainClass: 'search-modal' + scheme,
1005					midClick: true,
1006					fixedContentPos: true,
1007					autoFocusLast: false
1008				}); 
1009			};
1010
1011			target.on('mouseover', setup);
1012			setTimeout(setup, 400);
1013		},
1014
1015		/**
1016		 * Setup prettyPhoto
1017		 */
1018		lightbox: function() {
1019			
1020			// Can be disabled overall, or on mobile.
1021			if (
1022				!$.fn.magnificPopup 
1023				|| !$('body').hasClass('has-lb') 
1024				|| (!$('body').hasClass('has-lb-sm') && $(window).width() < 768)
1025			) {
1026				return;
1027			}
1028
1029			if (isIframe) {
1030				return;
1031			}
1032			
1033			var mfpInit = {
1034				type: 'image',
1035				tLoading: '',
1036				mainClass: 'mfp-fade mfp-img-mobile',
1037				removalDelay: 300,
1038				callbacks: {
1039					afterClose: function() {
1040						if (this._lastFocusedEl) {
1041							$(this._lastFocusedEl).addClass('blur');
1042						}
1043					},
1044					open: () => $(document).trigger('ts-sticky-bar-remove')
1045				}
1046			};
1047
1048			/**
1049			 * Filter to handle valid images only.
1050			 */
1051			var filterImages = function() {
1052				
1053				if (!$(this).attr('href')) {
1054					return false;
1055				}
1056				
1057				return $(this).attr('href').match(/\.(jpe?g|webp|png|bmp|gif)($|\?.+?)/); 
1058			};
1059			
1060			/**
1061			 * Handle Galleries in post.
1062			 * 
1063			 * @param {HTMLElement} context
1064			 */
1065			var addGalleryLightbox = function(context) {
1066				var gal_selectors = '.gallery-slider .slick-slide:not(.slick-cloned), .post-content .gallery, .post-content .blocks-gallery-item, .post-content .tiled-gallery, .wp-block-gallery .wp-block-image, .tiled-gallery__gallery .tiled-gallery__item';
1067		
1068				// Filter to only tie valid images.
1069				$(gal_selectors, context)
1070					.find('a')
1071					.has('img, .img.bg-cover')
1072					.filter(filterImages)
1073					.addClass('lightbox-gallery-img');
1074
1075				// Add WooCommerce support
1076				$('.woocommerce a[data-rel^="prettyPhoto"], a.zoom').addClass('lightbox-gallery-img');
1077				gal_selectors += ', .woocommerce .images';
1078
1079				// Attach the lightbox as gallery
1080				$(gal_selectors, context).magnificPopup($.extend({}, mfpInit, {
1081					delegate: '.lightbox-gallery-img',
1082					gallery: {enabled: true},
1083					image: {
1084						titleSrc: function(item) {
1085							var image   = item.el.find('img'), 
1086								caption = item.el.find('.caption').html();
1087							
1088							return (caption || image.attr('title') || ' ');
1089						}
1090					}
1091				}));
1092			};
1093			
1094			/**
1095			 * Non-gallery images in posts.
1096			 * 
1097			 * @param {HTMLElement} context 
1098			 */
1099			var addLightboxImages = function(context) {
1100
1101				var selector = 
1102					$('.post-content, .main .featured, .single-creative .featured', context)
1103						.find('a:not(.lightbox-gallery-img)')
1104						.has('img, .img');
1105
1106				selector.add('.lightbox-img')
1107					.filter(filterImages)
1108					.magnificPopup(mfpInit);
1109
1110			};
1111
1112			addGalleryLightbox();
1113			addLightboxImages();
1114
1115			$('iframe.spc-auto-load-post').each(function() {
1116				const iframe = $(this).get(0).contentDocument;
1117				addGalleryLightbox(iframe);
1118				addLightboxImages(iframe);
1119			});
1120			
1121		}
1122	};
1123
1124	self.stickyBarPause = false;
1125
1126	return self;
1127	
1128})(jQuery);
1129/**
1130 * Utility methods.
1131 */
1132Bunyad.util = {
1133	
1134	throttle(fn, delay = 150, trails = true) {
1135
1136		let pause = false;
1137		let trail;
1138		let timeout = function() {
1139			if (trail) {
1140				fn.apply(trail[0], trail[1]);
1141				trail = null;
1142				setTimeout(timeout, delay);
1143			} else {
1144				pause = false;
1145			}
1146		};
1147
1148		return function(...args) {
1149			if (!pause) {
1150				fn.apply(this, args);
1151				pause = true;
1152				setTimeout(timeout, delay);
1153			} else if (trails) {
1154				trail = [this, args];
1155			}
1156		};
1157	},
1158
1159	writeRaf(fn, options) {
1160		var running, 
1161		    args, 
1162		    that;
1163
1164		var run = function() {
1165			running = false;
1166			fn.apply(that, args);
1167		};
1168
1169		if (!options) {
1170			options = {};
1171		}
1172
1173		return function() {
1174			args = arguments;
1175			that = options.that || this;
1176
1177			if (!running) {
1178				running = true;
1179				requestAnimationFrame(run);
1180			}
1181		};
1182	},
1183
1184	onLoad(cb) {
1185		document.readyState === 'complete' ? cb() : window.addEventListener('load', cb);
1186	}
1187};
1188
1189/**
1190 * Authentication modal.
1191 */
1192(function($) {
1193
1194	/** @type {Boolean} */
1195	let isInitialized = false;
1196
1197	function init() {
1198
1199		if (!$('#auth-modal').length) {
1200			return;
1201		}
1202
1203		$(document).on('click', 'a', e => {
1204			if (e.currentTarget.hash === '#auth-modal') {
1205				if (!window.MicroModal) {
1206					return;
1207				}
1208		
1209				initModal();
1210				MicroModal.show('auth-modal', {
1211					awaitCloseAnimation: true
1212				});
1213
1214				e.preventDefault();
1215			}
1216		});
1217	}
1218
1219	function initModal(e) {
1220
1221		if (isInitialized) {
1222			return;
1223		}
1224
1225		const modal  = $('#auth-modal');
1226		const toggle = e => {
1227			modal.find('.auth-modal-login, .auth-modal-register').toggle();
1228			e.preventDefault();
1229		}
1230		
1231		modal.on('click', '.register-link, .login-link', toggle);
1232		isInitialized = true;
1233	}
1234
1235	Bunyad.authModal = init;
1236
1237})(jQuery);
1238(function($) {
1239
1240	const cache = [];
1241
1242	function init() {
1243		$('.section-head .subcats a, .block-head .filters a').on('click', e => {
1244			doFilter(e.currentTarget);
1245			return false;
1246		});
1247	}
1248
1249	function doFilter(ele) {
1250
1251		if ($(this).hasClass('active')) {
1252			return false;
1253		}
1254
1255		ele = $(ele);
1256
1257		const block   = ele.closest('.block-wrap');
1258		const blockId = block.data('id');
1259		const termId  = ele.data('id');
1260		const content = block.find('.block-content');
1261		const curActive = ele.parents('.filters').find('a.active');
1262		
1263		// Store "All" in cache.
1264		if (!curActive.data('id')) {
1265			cache[`${blockId}-0`] = block[0].outerHTML;
1266		}
1267
1268		// Filter links active
1269		ele.addClass('active');
1270		curActive.removeClass('active');
1271
1272		// For pagination.
1273		block.data('filter-active', ele.data('id'));
1274		
1275		const cacheId = `${blockId}-${termId}`;
1276		content.removeClass('fade-in-up-lg')
1277			.addClass('loading');
1278
1279		if (!cache[cacheId]) {
1280
1281			const blockData = block.data('block') || {};
1282			blockData.props.filter = termId;
1283
1284			// AJAX params
1285			const params =  {
1286				action: 'bunyad_block',
1287				block: blockData
1288			};
1289
1290			$.post(
1291				Bunyad.ajaxurl,
1292				params,
1293				data => {
1294					cache[cacheId] = data;
1295					replaceContent(cache[cacheId], block, content);
1296				}
1297			);
1298		}
1299		else {
1300			replaceContent(cache[cacheId], block, content);
1301		}
1302	}
1303
1304	function replaceContent(data, block, content) {
1305		const newHtml    = $(data);
1306		const newContent = newHtml.find('.block-content')
1307		newContent.find('img').addClass('no-display appear');
1308
1309		// Race condition.
1310		newContent.find('.lazyloading').removeClass('lazyloading').addClass('lazyload');
1311		content.removeClass('fade-in-up-lg');
1312
1313		requestAnimationFrame(() => {
1314			content.html(newContent.html())
1315				.removeClass('loading')
1316				.addClass('fade-in-up-lg')
1317				.addClass('ready');
1318		});
1319		
1320		// Set new block data.
1321		block.data('block', newHtml.data('block'));
1322	}
1323
1324	Bunyad.blocksFilters = init;
1325
1326})(jQuery);
1327
1328(function($) {
1329	Bunyad.stickyHeader = function(header, options = {}) {
1330		var nav,
1331			isSmart = false,
1332			isSticky = false,
1333			stickyIsStatic = false,
1334			prevScroll = 0,
1335			curScroll  = 0,
1336			extraTop   = 0,
1337			stickyTop,
1338			hideAt,
1339			head,
1340			headHeight;
1341
1342		let stickyBar;
1343		let stickyIsActive = false;
1344		let hasInitialized = false;
1345		
1346		/**
1347		 * Calculate header size.
1348		 */
1349		const calcHeadSize = Bunyad.util.writeRaf(() => {
1350
1351			// Don't reset if it's stuck
1352			if (stickyIsActive) {
1353				return;
1354			}
1355
1356			var new_height = head.css('min-height', 0).height();
1357			head.css('min-height', new_height);
1358			headHeight = new_height;
1359		});
1360
1361		function setSize(skipCalc) {
1362
1363			// Skip calculation if asked to and head height has been prev set
1364			if (skipCalc && headHeight) {
1365				return;
1366			}
1367
1368			calcHeadSize();
1369		}
1370
1371		/**
1372		 * Remove / disable sticky.
1373		 */
1374		function removeSticky(simpleHide) {
1375		
1376			// Check before dom modification 
1377			if (isSticky) {
1378				
1379				stickyIsActive = false;
1380				let visualHide = false;
1381
1382				// Mainly for smart sticky. Simple hide by not unfixing completely.
1383				if (simpleHide) {
1384					if (!nav.hasClass('off')) {
1385						nav.addClass('off');
1386						visualHide = true;
1387					}
1388				}
1389				else {
1390					// isSticky already checks for presence of smart-head-sticky.
1391					nav.removeClass('smart-head-sticky off');
1392					visualHide = true;
1393				}
1394				
1395				if (visualHide) {
1396					setSize(true);
1397					$(document).trigger('sticky-bar-hide');
1398				}
1399			}
1400		}
1401
1402		/**
1403		 * Make the bar sticky and setup handlers.
1404		 */
1405		function toggleSticky() {
1406
1407			if (stickyIsStatic) {
1408				return;
1409			}
1410
1411			curScroll = $(window).scrollTop();
1412			isSticky  = nav.hasClass('smart-head-sticky');
1413
1414			// Check and apply sticky - separated writes
1415			const stickyWrites = Bunyad.util.writeRaf(() => {
1416				
1417				// Make it sticky when viewport is scrolled beyond the sticky bar.
1418				if (curScroll > stickyTop) {
1419
1420					// For smart sticky, test for scroll change
1421					if (isSmart && (!prevScroll || curScroll > prevScroll)) {
1422						removeSticky(true);
1423					}
1424					else {
1425						stickyIsActive = true;
1426						
1427						if (!isSmart || curScroll < prevScroll-4 && !Bunyad.theme.stickyBarPause) {
1428							if (!nav.hasClass('smart-head-sticky') || nav.hasClass('off')) {
1429								nav.removeClass('off');
1430								nav.addClass('smart-head-sticky');
1431
1432								$(document).trigger('sticky-bar-show');
1433							}
1434						}
1435					}
1436					
1437					prevScroll = curScroll;
1438					
1439				} else {
1440					
1441					// hide at a certain point
1442					if (curScroll <= hideAt) {
1443						prevScroll = 0;
1444						removeSticky();
1445					}
1446				}
1447			});
1448			
1449			stickyWrites();
1450		}
1451
1452		/**
1453		 * Check if smart sticky is enabled.
1454		 */
1455		function checkSmart() {
1456			if (head.data('sticky-type') == 'smart') {
1457				isSmart = true;
1458			}
1459			
1460			if (isSmart && nav.length) {
1461				nav.addClass('is-smart');
1462			}
1463		}
1464
1465		/**
1466		 * Setup calculations and DOM targets.
1467		 */
1468		function setup() {
1469
1470			// Reset vars - might be a re-init
1471			stickyIsStatic = false;
1472			
1473			if ($('.admin-bar').length) {
1474				extraTop = $('#wpadminbar').height();
1475			}
1476
1477			stickyBar = head.data('sticky');
1478			if (!stickyBar) {
1479				return;
1480			}
1481
1482			// Auto detect.
1483			if (stickyBar === 'auto') {
1484				const menu = head.find('.navigation-main .menu');
1485				if (menu.length) {
1486					const match = menu.eq(0).closest('.smart-head-row').attr('class')
1487						.match(/smart-head-(top|mid|bot)/);
1488
1489					if (!match || !match[1]) {
1490						return;
1491					}
1492					
1493					stickyBar = match[1];
1494				}
1495			}
1496		
1497			stickyBar = head.find('.smart-head-' + stickyBar);
1498			nav = stickyBar;
1499
1500			if (!stickyBar.length) {
1501				return;
1502			}
1503
1504			if (head.data('sticky-full')) {
1505				stickyBar.addClass('sticky-is-full');
1506			}
1507			
1508			stickyTop = nav.offset().top - extraTop;
1509			hideAt = stickyTop + 1;
1510
1511			const isLast = head.find('.smart-head-row:last-child').is(stickyBar);
1512			
1513			// If not the last row, show late and hide early to prevent jumps for bottom row.
1514			if (!isLast) {
1515				stickyTop = head.offset().top + head.height();
1516				hideAt = stickyTop + 1;
1517				nav.addClass('animate');
1518			}
1519				
1520			checkSmart();
1521
1522			// For smart, always animate.
1523			if (isSmart) {
1524				nav.addClass('animate');
1525			}
1526
1527			// Android fix on addr bar hide
1528			if (isSmart && !stickyIsActive) {
1529				prevScroll = 0;
1530			}
1531		}
1532
1533		/**
1534		 * Init it - called multiple times to test for init based on width.
1535		 */
1536		function initHandler() {
1537
1538			if (hasInitialized) {
1539				return false;
1540			}
1541	
1542			if (
1543				(options.mobile && $(window).width() > 940)
1544				|| (!options.mobile && $(window).width() <= 940)
1545			) {
1546				return;
1547			}
1548	
1549			setup();
1550			if (!nav || !nav.length) {
1551				return;
1552			}
1553	
1554			// Setup sticky handler 
1555			toggleSticky();
1556	
1557			Bunyad.util.onLoad(calcHeadSize);
1558			$(window).on('scroll', toggleSticky);
1559			
1560			// Re-initialize on resize.
1561			$(window).on('resize orientationchange', () => {
1562				removeSticky();
1563				calcHeadSize();
1564				setup();
1565				toggleSticky();
1566			});
1567
1568			hasInitialized = true;
1569
1570			// Hooks.
1571			$(document).on('ts-sticky-bar-remove', removeSticky);
1572		}
1573
1574		function init(header) {
1575			hasInitialized = false;
1576			head = $(header);
1577
1578			if (!head.length) {
1579				return;
1580			}
1581
1582			initHandler();
1583			$(window).on('resize orientationchange', initHandler);
1584		}
1585
1586		init(header);
1587
1588		return {
1589			init,
1590			getSticky: () => stickyBar,
1591		};
1592	};
1593})(jQuery);
1594/**
1595 * Minimal Element in-viewport handler.
1596 */
1597class Bunyad_InView {
1598	
1599	/**
1600	 * @param {NodeList} elements 
1601	 * @param {CallableFunction} callback 
1602	 * @param {Object} options 
1603	 * @param {CallableFunction} fallback
1604	 */
1605	constructor(elements = [], callback, options = {}, fallback) {
1606
1607		this.elements = elements;
1608		this.callback = callback;
1609		this.options  = Object.assign({
1610			intersectOnly: true,
1611			observeOptions: {},
1612			once: false
1613		}, options);
1614
1615		if (!this.callback) {
1616			return false;
1617		}
1618
1619		if (!window.IntersectionObserver) {
1620			console.warn('Intersection observer missing.');
1621			fallback();
1622			return false;
1623		}
1624
1625		// eslint-disable-next-line compat/compat
1626		this.observer = new IntersectionObserver(
1627			this.handleIntersection.bind(this), 
1628			this.options.observeOptions
1629		);
1630
1631		elements.forEach(element => {
1632			this.observer.observe(element);
1633		});
1634	}
1635
1636	handleIntersection(entries) {
1637
1638		entries.forEach(entry => {
1639			if (this.options.intersectOnly && !entry.isIntersecting) {
1640				return;
1641			}
1642
1643			this.callback.call(this, entry);
1644
1645			if (this.options.once) {
1646				this.observer.unobserve(entry.target);
1647			}
1648		});
1649	}
1650}
1651(function($) {
1652
1653	const cache = [];
1654
1655	/**
1656	 * Setup pagination for blocks.
1657	 */
1658	function init() {
1659
1660		// AJAX pagination handlers
1661		$('.main-wrap')
1662			.on('click', '.main-pagination .load-button, .pagination-numbers[data-type=numbers-ajax] a', e => {
1663				const target = e.currentTarget;
1664
1665				if (target.dataset.processing) {
1666					return;
1667				}
1668
1669				target.dataset.processing = true;
1670
1671				ajaxPagination(target, () => {
1672					target.dataset.processing = false;
1673				});
1674
1675				return false;
1676			});
1677
1678		$('.main-pagination[data-type=infinite]').each(function() {
1679			infiniteLoad($(this).get(0));
1680		});
1681	}
1682
1683	/**
1684	 * Infinite load handler.
1685	 * 
1686	 * @param {Element} pageEle Pagination element container.
1687	 * @param {Object} options 
1688	 */
1689	function infiniteLoad(pageEle, options) {
1690		const block = pageEle.closest('.block-wrap');
1691		pageEle._loads = 1;
1692		options = Object.assign({bottomDeduct: 30}, options);
1693
1694		if (!block) {
1695			return;
1696		}
1697
1698		const offset = {top: 0, bottom: 0};
1699		const compute = () => {
1700			const rect    = block.getBoundingClientRect();
1701			offset.top    = rect.top + window.scrollY;
1702			offset.bottom = offset.top + block.clientHeight - window.innerHeight;
1703
1704			// Page element's height shouldn't be counted + extra deduction.
1705			offset.bottom -= pageEle.clientHeight + options.bottomDeduct;
1706		}
1707
1708		compute();
1709		
1710		let isLoading = false;	
1711		const loadOnScroll = (e) => {
1712
1713			if (isLoading || pageEle._loads > 5) {
1714				return;
1715			}
1716
1717			if (window.scrollY > offset.bottom) {
1718				isLoading = true;
1719				pageEle._loads++;
1720
1721				ajaxPagination(pageEle.querySelector('.load-button'), () => {
1722					compute();
1723					isLoading = false;
1724				});
1725			}
1726		};
1727
1728		$(window).on('scroll', Bunyad.util.throttle(loadOnScroll, 150, false));
1729		$(window).on('resize', Bunyad.util.throttle(compute));
1730	}
1731
1732	function ajaxPagination(ele, callback) {
1733		
1734		ele = $(ele);
1735			
1736		let block    = ele.closest('.block-wrap');
1737		let isBlock = true;
1738		let process  = processLoadMore;
1739		
1740		// Not a block, archives
1741		if (!block.length) {
1742			isBlock = false;
1743			block = ele.closest('.main-content');
1744		}
1745
1746		// AJAX params
1747		const params =  {
1748			action: 'bunyad_block',
1749			block: block.data('block') || {},
1750			paged: (ele.data('page') || 0) + 1
1751		};
1752
1753		// Type of pagination
1754		const type = ele.closest('.main-pagination').data('type');
1755		
1756		switch(type) {
1757		
1758			// Type: Load More
1759			case 'load-more':
1760			case 'infinite':
1761				ele.addClass('loading').find('.tsi').addClass('tsi-spin');
1762				process = processLoadMore;
1763
1764				break;
1765
1766			// Type: Numbered
1767			case 'numbers-ajax':
1768
1769				block.find('.block-content').addClass('loading');
1770				
1771				// Change page number
1772				const permalink = ele.attr('href').match(/\/page\/(\d+)(\/|$)/);
1773				if (permalink !== null) {
1774					params.paged = permalink[1];
1775				}
1776				else {
1777					// Plain links
1778					const src = ele.attr('href').match(/(\?|&)paged?=(\d+)/);
1779					params.paged = src ? src[2] : 1;
1780				}
1781				
1782				// Set height
1783				block.css('height', block.height());
1784				process = processNumbered;
1785
1786				break;
1787		}
1788
1789		// Process block.
1790		if (isBlock) {
1791			
1792			// Home blocks AJAX
1793			const ajaxUrl = Bunyad.ajaxurl;
1794
1795			const blockId = block.data('id');
1796			const cacheId = `${blockId}-${ block.data('filter-active') }-${params.paged}`;
1797
1798			if (!cache[cacheId]) {
1799				$.post(
1800					ajaxUrl,
1801					params,
1802					data => {
1803						requestAnimationFrame(() => {
1804							process(data, block, ele);
1805							cache[cacheId] = data;
1806							!callback || callback();
1807						});
1808					}
1809				);
1810			}
1811			else {
1812				requestAnimationFrame(() => {
1813					process(cache[cacheId], block, ele);
1814					!callback || callback();
1815				});
1816			}
1817		}
1818	}
1819
1820	/**
1821	 * Numbered Ajax callback.
1822	 */
1823	function processNumbered(data, block) {
1824		const blockContent = block.find('.block-content');
1825		
1826		blockContent
1827			.removeClass('fade-in-down-lg')
1828			.html($(data).find('.block-content').html());
1829		
1830		block.css('height', 'auto');
1831		blockContent.addClass('fade-in-down-lg').removeClass('loading');
1832		blockContent.on('animationend', e => blockContent.removeClass('fade-in-down-lg'));
1833
1834		// Scroll to top, temporarily pausing sticky bar.
1835		Bunyad.theme.stickyBarPause = true;
1836		setTimeout(() => { Bunyad.theme.stickyBarPause = false; }, 300);
1837		$('html, body').animate({scrollTop: block.offset().top - 50}, 200);
1838		
1839		$(document).trigger('ts-ajax-pagination');		
1840	}
1841
1842	/**
1843	 * Load More Ajax callback method.
1844	 */
1845	function processLoadMore(data, block, ele) {
1846		
1847		var content = $(data),
1848			posts;
1849		
1850		// Mixed blocks: News Focus, Highlights, Focus Grid.
1851		const isMixed = block.data('is-mixed');
1852		const wrap = isMixed ? block.find('.block-content') : block.find('.loop');
1853		const pagination = block.find('.main-pagination');
1854		
1855		posts = content.find('.loop').children().addClass('fade-in-up-lg');
1856		posts.each(function() {
1857			$(this).on('animationend', () => $(this).removeClass('fade-in-up-lg'));
1858		});
1859
1860		if (isMixed) {
1861			pagination.remove();
1862			wrap.append(content.find('.block-content').children());
1863		}
1864		else {
1865			wrap.append(posts);
1866			
1867			const newPagination = content.find('.main-pagination');
1868			if (newPagination.length) {
1869				pagination.html(newPagination.html());
1870			}
1871			else {
1872				pagination.remove();
1873			}
1874		}
1875
1876		// Masonry reload
1877		if (wrap.hasClass('masonry')) {
1878			// posts.removeClass('fade-in-up-lg');
1879			// Bunyad.theme.masonry(true, posts, wrap.find('.posts-wrap'));
1880		}
1881		
1882		$(document).trigger('ts-ajax-pagination');
1883		
1884		ele.removeClass('loading')
1885			.find('.tsi').removeClass('tsi-spin');
1886	}
1887
1888	Bunyad.pagination = init;
1889})(jQuery);
1890
1891/**
1892 * Live Search Handler
1893 */
1894(function($) {
1895	"use strict";
1896	
1897	var cache = {}, timer, element;
1898	
1899	const self = {
1900		
1901		init: function() {
1902			
1903			var search = $('.live-search-query');
1904
1905			if (!search.length) {
1906				return;
1907			}
1908			
1909			// Turn off browser's own auto-complete
1910			$('.live-search-query').attr('autocomplete', 'off');
1911			
1912			// setup the live search on key press
1913			$('.live-search-query').on('keyup focus', function() {
1914				
1915				element = $(this).parent();
1916			
1917				var query = $(this).val(), result;
1918
1919				// clear existing debounce
1920				clearTimeout(timer);
1921				
1922				// minimum of 1 character
1923				if (query.length < 1) {
1924					self.add_result('');
1925					return;
1926				}
1927				
1928				// debounce to prevent excessive ajax queries
1929				timer = setTimeout(function() {
1930					self.process(query);
1931				}, 250);
1932			});
1933			
1934			// setup hide 
1935			$(document).on('click', function(e) {
1936				
1937				var results = $('.live-search-results');
1938				
1939				if (results.is(':visible') && !$(e.target).closest('.search-form').length) {
1940					results.removeClass('fade-in');
1941					results.closest('form').removeClass('has-live-results');
1942				}
1943			});
1944		},
1945		
1946		/**
1947		 * Process the search query
1948		 */
1949		process: function(query) {
1950
1951			// have it in cache?
1952			if (query in cache) {
1953				self.add_result(cache[query]);
1954			}
1955			else {
1956				$.get(Bunyad.ajaxurl, {action: 'bunyad_live_search', 'query': query}, function(data) {
1957					
1958					// add to cache and add results
1959					cache[query] = data;
1960					self.add_result(data);
1961				});
1962			}
1963		},
1964		
1965		/**
1966		 * Add live results to the container
1967		 */
1968		add_result: function(result) {
1969			
1970			if (!element.find('.live-search-results').length) {
1971				element.append($('<div class="live-search-results"></div>'));
1972			}
1973			
1974			var container = element.find('.live-search-results');
1975
1976			if (!result) {
1977				container.removeClass('fade-in');
1978				return;
1979			}
1980			
1981			// add the html result
1982			container.html(result);
1983			
1984			requestAnimationFrame(function() {
1985				container.addClass('fade-in');
1986				container.closest('form').addClass('has-live-results');
1987			});
1988			
1989		}
1990	};
1991
1992	Bunyad.liveSearch = self.init;
1993	
1994})(jQuery);
1995
1996(function($) {
1997	/**
1998	 * Setup sliders.
1999	 */
2000	function init() {
2001
2002		if (!$.fn.slick) {
2003			return;
2004		}
2005
2006		// Setup all sliders
2007		$('.common-slider .slides:not(.loaded), .loop-carousel:not(.loaded), .gallery-slider:not(.loaded)').each(function() {
2008
2009			const slider = $(this);
2010			if (slider.hasClass('loaded')) {
2011				return;
2012			}
2013
2014			slider.one('init', function() {
2015				$(this).addClass('loaded');
2016			});
2017		
2018			// Common setting
2019			var vars = {
2020				rows: 0,
2021				prevArrow: '<a href="#" class="prev-arrow"><i class="tsi tsi-angle-left"></i></a>',
2022				nextArrow: '<a href="#" class="next-arrow"><i class="tsi tsi-angle-right"></i></a>',
2023				autoplay: slider.data('autoplay') ? true : false,
2024				autoplaySpeed: slider.data('speed') || 5000,
2025				fade: slider.data('animation') == 'fade' ? true : false,
2026				rtl: $('html').prop('dir') === 'rtl'
2027			};
2028				
2029			const sliderType = slider.data('slider');
2030
2031			// First image load.
2032			if (vars.autoplay) {
2033
2034				slider.on('init afterChange', function(e, slick) {				
2035					var ele     = $(this),
2036						current = ele.find('[data-slick-index="' + slick.currentSlide + '"]');
2037
2038					var img = current.find('.wp-post-image').first();
2039					if (!img.length) {
2040						return;
2041					}					
2042
2043					var loaded = img.hasClass('lazyloaded') || (img.is('img:not(.lazyload)') && img.prop('complete'));
2044
2045					if (!loaded) {
2046						img.on('lazyloaded load', function(e) {
2047							slick.slickPlay();
2048						});
2049						
2050						slick.slickPause();
2051					}
2052				});
2053			}
2054			
2055			switch (sliderType) {
2056				case 'feat-grid':
2057					setupFeatGrid(slider, vars);
2058					break;
2059					
2060				case 'carousel':
2061					setupCarousel(slider, vars);
2062					break;
2063
2064				case 'gallery':
2065					setupGallery(slider, vars);
2066					break;
2067			}
2068
2069		});
2070
2071		// Disable anchor jump on arrows
2072		$('.common-slider').on('click', '.slick-arrow', function(e) {
2073			e.preventDefault();
2074		});
2075	}
2076
2077	/**
2078	 * For posts carousels.
2079	 */
2080	function setupCarousel(slider, vars) {
2081
2082		const slidesNum = slider.data('slides') || 1;
2083
2084		// If not defined, use columns - 1, min 2, max 4.
2085		const slidesNumMd = slider.data('slides-md') || (
2086			Math.min(4, Math.max(2, slidesNum - 1))
2087		);
2088
2089		const slidesNumSm = slider.data('slides-sm') || 1;
2090
2091		if (slider.data('arrows')) {
2092			// Set correct top for arrows on init.
2093			slider.on('init', (ele) => {
2094				const height = slider.find('.l-post:first-child .media-ratio').outerHeight();
2095				if (height) {
2096					slider[0].style.setProperty('--arrow-top', (height/2) + 'px');
2097				}
2098			});
2099		}
2100
2101		slider.slick($.extend(vars, {
2102			arrows: slider.data('arrows') ? true : false,
2103			infinite: true,
2104			cssEase: 'ease-out',
2105			speed: 400,
2106			dots: slider.data('dots') ? true : false,
2107			dotsClass: 'nav-dots',
2108			adaptiveHeight: true,
2109			slidesToShow: slidesNum,
2110			slidesToScroll: slidesNum, 
2111			responsive: [
2112				{
2113					breakpoint: 940,
2114					settings: {slidesToShow: slidesNumMd, slidesToScroll: slidesNumMd}
2115				},
2116				{
2117					breakpoint: 540,
2118					settings: {slidesToShow: slidesNumSm, slidesToScroll: slidesNumSm}
2119				}
2120			]
2121		}));
2122	}
2123
2124	/**
2125	 * Setup slider for featured grid.
2126	 */
2127	function setupFeatGrid(slider, vars) {
2128
2129		const scrollNum = slider.data('scroll-num') || 1;
2130		let scrollNumMd = slider.data('scroll-num-md');
2131			
2132		if (!scrollNumMd) {
2133			scrollNumMd = Math.min(2, Math.max(1, scrollNum - 1));
2134		}
2135
2136		slider.slick($.extend(vars, {
2137			arrows: true,
2138			infinite: true,
2139			cssEase: 'ease-out',
2140			speed: 500,
2141			slidesToShow: scrollNum,
2142			slidesToScroll: scrollNumMd, 
2143			responsive: [
2144				{
2145					breakpoint: 940,
2146					settings: {slidesToShow: scrollNumMd, slidesToScroll: scrollNumMd}
2147				},
2148				{
2149					breakpoint: 540,
2150					settings: {slidesToShow: 1, slidesToScroll: 1}
2151				}
2152			]
2153		}));
2154	}
2155
2156	/**
2157	 * Setup gallery slider.
2158	 */
2159	function setupGallery(slider, vars) {
2160			
2161		vars = Object.assign(vars, {
2162			infinite: true,
2163			slidesToShow: 1,
2164			slidesToScroll: 1,
2165			adaptiveHeight: true
2166		});
2167		
2168		const init = () => {
2169			slider.slick(vars);
2170		};
2171
2172		// slider.find('img').eq(0).imagesLoaded(init);
2173		init();
2174	}
2175
2176	Bunyad.sliders = init;
2177})(jQuery);
2178
2179// load when ready
2180jQuery(function($) {
2181		
2182	Bunyad.theme.init();
2183	Bunyad.pagination();
2184	Bunyad.sliders();
2185	Bunyad.blocksFilters();
2186
2187	// Setup sticky headers as applicable.
2188	Bunyad.stickyHeaders = {
2189		main: Bunyad.stickyHeader('.smart-head-main', {mobile: false}),
2190		mobile: Bunyad.stickyHeader('.smart-head-mobile', {mobile: true})
2191	};
2192
2193	Bunyad.liveSearch();
2194	Bunyad.authModal();
2195});
2196/**
2197 * Plugins and 3rd Party Libraries
2198 */
2199
2200/*!
2201* FitVids 1.1
2202* Released under the WTFPL license - http://sam.zoy.org/wtfpl/
2203*/
2204;(function($){$.fn.fitVids=function(options){var settings={customSelector:null,ignore:null};if(!document.getElementById("fit-vids-style")){var head=document.head||document.getElementsByTagName("head")[0];var css=".fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}";var div=document.createElement("div");div.innerHTML='<p>x</p><style id="fit-vids-style">'+css+"</style>";head.appendChild(div.childNodes[1])}if(options){$.extend(settings,options)}return this.each(function(){var selectors=['iframe[src*="player.vimeo.com"]','iframe[src*="youtube.com"]','iframe[src*="youtube-nocookie.com"]','iframe[src*="kickstarter.com"][src*="video.html"]',"object","embed"];if(settings.customSelector){selectors.push(settings.customSelector)}var ignoreList=".fitvidsignore";if(settings.ignore){ignoreList=ignoreList+", "+settings.ignore}var $allVideos=$(this).find(selectors.join(","));$allVideos=$allVideos.not("object object");$allVideos=$allVideos.not(ignoreList);$allVideos.each(function(){var $this=$(this);if($this.parents(ignoreList).length>0){return}if(this.tagName.toLowerCase()==="embed"&&$this.parent("object").length||$this.parent(".fluid-width-video-wrapper").length){return}if((!$this.css("height")&&!$this.css("width"))&&(isNaN($this.attr("height"))||isNaN($this.attr("width")))){$this.attr("height",9);$this.attr("width",16)}var height=(this.tagName.toLowerCase()==="object"||($this.attr("height")&&!isNaN(parseInt($this.attr("height"),10))))?parseInt($this.attr("height"),10):$this.height(),width=!isNaN(parseInt($this.attr("width"),10))?parseInt($this.attr("width"),10):$this.width(),aspectRatio=height/width;if(!$this.attr("id")){var videoID="fitvid"+Math.floor(Math.random()*999999);$this.attr("id",videoID)}$this.wrap('<div class="fluid-width-video-wrapper"></div>').parent(".fluid-width-video-wrapper").css("padding-top",(aspectRatio*100)+"%");$this.removeAttr("height").removeAttr("width")})})}})(window.jQuery||window.Zepto);
2205
2206/**
2207 * Plus/minus polyfill for numbers - used in WooCommerce
2208 * 
2209 * Author Bryce Adams
2210 */
2211jQuery( function( $ ) {
2212
2213	// Quantity buttons
2214	$( 'div.quantity:not(.buttons_added), td.quantity:not(.buttons_added)' )
2215		.addClass( 'buttons_added' )
2216		.append( '<div class="vertical-buttons"><input type="button" value="+" class="plus" /><input type="button" value="-" class="minus" /></div>' );
2217
2218	$( document ).on( 'click', '.plus, .minus', function() {
2219
2220		// Get values
2221		var $qty		= $( this ).closest( '.quantity' ).find( '.qty' ),
2222			currentVal	= parseFloat( $qty.val() ),
2223			max			= parseFloat( $qty.attr( 'max' ) ),
2224			min			= parseFloat( $qty.attr( 'min' ) ),
2225			step		= $qty.attr( 'step' );
2226
2227		// Format values
2228		if ( ! currentVal || currentVal === '' || currentVal === 'NaN' ) currentVal = 0;
2229		if ( max === '' || max === 'NaN' ) max = '';
2230		if ( min === '' || min === 'NaN' ) min = 0;
2231		if ( step === 'any' || step === '' || step === undefined || parseFloat( step ) === 'NaN' ) step = 1;
2232
2233		// Change the value
2234		if ( $( this ).is( '.plus' ) ) {
2235
2236			if ( max && ( max == currentVal || currentVal > max ) ) {
2237				$qty.val( max );
2238			} else {
2239				$qty.val( currentVal + parseFloat( step ) );
2240			}
2241
2242		} else {
2243
2244			if ( min && ( min == currentVal || currentVal < min ) ) {
2245				$qty.val( min );
2246			} else if ( currentVal > 0 ) {
2247				$qty.val( currentVal - parseFloat( step ) );
2248			}
2249
2250		}
2251
2252		// Trigger change event
2253		$qty.trigger( 'change' );
2254
2255	});
2256
2257});

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.