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