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