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