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