1/** 2 * Project: Minerva KB 3 * Copyright: 2015-2016 @KonstruktStudio 4 */ 5(function ($) { 6 7 var GLOBAL_DATA = window.MinervaKB; 8 9 var i18n = GLOBAL_DATA.i18n; 10 var platform = GLOBAL_DATA.platform; 11 var settings = GLOBAL_DATA.settings; 12 var info = GLOBAL_DATA.info; 13 14 var $body = $('body'); 15 16 /** 17 * libs 18 */ 19 if (!String.prototype.trim) { 20 String.prototype.trim = function () { 21 return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); 22 }; 23 } 24 25 /** 26 * Debounces function execution 27 * TODO: make shared utils lib 28 * @param func 29 * @param wait 30 * @param immediate 31 * @returns {Function} 32 */ 33 function debounce(func, wait, immediate) { 34 var timeout; 35 return function () { 36 var context = this, args = arguments; 37 var later = function () { 38 timeout = null; 39 if (!immediate) { 40 func.apply(context, args); 41 } 42 }; 43 var callNow = immediate && !timeout; 44 clearTimeout(timeout); 45 timeout = setTimeout(later, wait); 46 if (callNow) { 47 func.apply(context, args); 48 } 49 }; 50 } 51 52 /** 53 * Throttles function execution. Based on Ben Alman implementation 54 * TODO: make shared utils lib 55 * @param delay 56 * @param noTrailing 57 * @param callback 58 * @param atBegin 59 * @returns {wrapper} 60 */ 61 function throttle(delay, noTrailing, callback, atBegin) { 62 var timeoutId; 63 var lastExec = 0; 64 65 if (typeof noTrailing !== 'boolean') { 66 atBegin = callback; 67 callback = noTrailing; 68 noTrailing = undefined; 69 } 70 71 function wrapper() { 72 var elapsed = +new Date() - lastExec; 73 var args = arguments; 74 75 var exec = function _exec() { 76 lastExec = +new Date(); 77 callback.apply(this, args ); 78 }.bind(this); 79 80 function clear() { 81 timeoutId = undefined; 82 } 83 84 if (atBegin && !timeoutId) { 85 exec(); 86 } 87 88 timeoutId && clearTimeout(timeoutId); 89 90 if (atBegin === undefined && elapsed > delay) { 91 exec(); 92 } else if (noTrailing !== true) { 93 timeoutId = setTimeout( 94 atBegin ? 95 clear : 96 exec, 97 atBegin === undefined ? 98 delay - elapsed : 99 delay 100 ); 101 } 102 } 103 104 return wrapper; 105 } 106 107 function addAjaxNonce(data) { 108 data['nonce_key'] = GLOBAL_DATA.nonce.nonceKey; 109 data['nonce_value'] = GLOBAL_DATA.nonce.nonce; 110 111 return data; 112 } 113 114 /** 115 * Sends Google Analytics event, if API available 116 * @param category 117 * @param action 118 * @param label 119 * @param value 120 */ 121 function trackGoogleAnalytics(category, action, label, value) { 122 if (window.ga && typeof window.ga === 'function') { 123 ga('send', 'event', category, action, label, value, { 124 nonInteraction: true 125 }); 126 } 127 } 128 129 // theme 130 var ajaxUrl = GLOBAL_DATA.ajaxUrl; 131 var $kbSearch = $('.kb-search__input'); 132 var NO_RESULTS_CLASS = 'kb-search__input-wrap--no-results'; 133 var HAS_CONTENT_CLASS = 'kb-search__input-wrap--has-content'; 134 var HAS_RESULTS_CLASS = 'kb-search__input-wrap--has-results'; 135 var REQUEST_CLASS = 'kb-search__input-wrap--request'; 136 var hasResults = false; 137 var resultsCount = 0; 138 var activeResult = -1; 139 var ESC = 27; 140 var ENTER = 13; 141 var ARROW_LEFT = 37; 142 var ARROW_UP = 38; 143 var ARROW_RIGHT = 39; 144 var ARROW_DOWN = 40; 145 var $doc = $('html, body'); 146 var $adminBar = $('#wpadminbar'); 147 var adminOffset = $adminBar.length ? $adminBar.height() : 0; 148 var searchMode = settings['search_mode']; 149 var searchNeedleLength = parseInt(settings['search_needle_length']); 150 var searchRequestsCount = 0; 151 var searchCache = {}; 152 var trackingCache = {}; 153 154 /** 155 * Live search result handler 156 * @param $search 157 * @param response 158 */ 159 function handleSearchResultsReceive($search, response) { 160 var $wrap = $search.parents('.kb-search__input-wrap'); 161 var $summary = $wrap.find('.kb-summary-text-holder'); 162 var $results = $wrap.find('.kb-search__results'); 163 var results = response.result; 164 var searchNeedle = response.search; 165 var resultsContent; 166 var resultsInfoHtml = response.results_info || ''; 167 var searchShowTopics = $search.data('show-results-topic') === 1; 168 var showTopicsLabel = $search.data('topic-label'); 169 var showExcerpt = settings['live_search_show_excerpt']; 170 171 if (searchMode === 'nonblocking') {
172 var needle = $search.val() && $search.val().trim(); 173 174 if (!needle || needle.length < searchNeedleLength) { 175 // in nonblocking mode user could have already removed the typed string 176 177 results = []; 178 hasResults = false; 179 resultsCount = 0; 180 activeResult = -1; 181 $wrap.removeClass(HAS_RESULTS_CLASS).removeClass(NO_RESULTS_CLASS); 182 $summary.html(''); 183 $results.html(''); 184 185 return; 186 } 187 } 188 189 if (results && results.length) { 190 if (settings['track_search_with_results']) { 191 trackGoogleAnalytics( 192 settings['ga_good_search_category'], 193 settings['ga_good_search_action'], 194 searchNeedle, 195 settings['ga_good_search_value'] || 0 196 ); 197 } 198 199 hasResults = true; 200 resultsCount = results.length; 201 activeResult = -1; 202 $wrap.removeClass(NO_RESULTS_CLASS).addClass(HAS_RESULTS_CLASS); 203 $summary.html(results.length + ' ' + (results.length === 1 ? i18n['result'] : i18n['results'])); 204 resultsContent = results.reduce(function ($el, result) { 205 return $el.append( 206 '<li>' + 207 '<a href="' + result.link + '">' + 208 '<span class="kb-search__result-header">' + 209 '<span class="kb-search__result-title">' + 210 result.title + 211 '</span>' + 212 (searchShowTopics && result.topics[0] ? 213 '<span class="kb-search__result-topic">' + 214 '<span class="kb-search__result-topic-label">' + showTopicsLabel + '</span>' + 215 '<span class="kb-search__result-topic-name">' + (result.topics[0]) + '</span>' + 216 '</span>' : 217 '') + 218 '</span>' + 219 (showExcerpt ? '<span class="kb-search__result-excerpt">' + result.excerpt + ' ...</span>' : '') + 220 '</a>' + 221 '</li>' 222 ); 223 }, $('<ul></ul>')); 224 225 $results.html(''); 226 $results.append(resultsInfoHtml); 227 $results.append(resultsContent); 228 } else { 229 if (settings['track_search_without_results']) { 230 trackGoogleAnalytics( 231 settings['ga_bad_search_category'], 232 settings['ga_bad_search_action'], 233 searchNeedle, 234 settings['ga_bad_search_value'] || 0 235 ); 236 } 237 238 hasResults = false; 239 resultsCount = 0; 240 activeResult = -1; 241 $wrap.removeClass(HAS_RESULTS_CLASS).addClass(NO_RESULTS_CLASS); 242 $summary.html(i18n['no-results']); 243 $results.html(''); 244 } 245 } 246 247 function focusInput() { 248 $kbSearch.filter('[data-autofocus="1"]').focus(); 249 } 250 251 function nextSearchResult() { 252 var $resultItems = $('.kb-search__results li a'); 253 254 activeResult = activeResult + 1 >= resultsCount ? 0 : activeResult + 1; 255 $resultItems.eq(activeResult).focus(); 256 } 257 258 function prevSearchResult() { 259 var $resultItems = $('.kb-search__results li a'); 260 261 activeResult = activeResult - 1 < 0 ? resultsCount - 1 : activeResult - 1; 262 $resultItems.eq(activeResult).focus(); 263 } 264 265 /** 266 * Live search keypress handler 267 * @param e 268 */ 269 function onSearchKeyPress(e) { 270 271 if (!$(".kb-search__input").is(":focus") && !$(".kb-search__results a").is(":focus")) { 272 return; //we do not to mess with keypress unless search is in focus 273 } 274 275 var $search = $(".kb-search__input:focus"); 276 277 switch (e.keyCode) { 278 case ESC: 279 focusInput(); 280 break; 281 282 case ARROW_UP: 283 prevSearchResult(); 284 break; 285 286 case ARROW_DOWN: 287 nextSearchResult(); 288 break; 289 290 case ENTER: 291 if ($search.length && !$search.val().trim()) { 292 e.preventDefault(); 293 e.stopImmediatePropagation(); 294 } 295 return; 296 297 default: 298 return; 299 } 300 301 e.preventDefault(); // prevent the default action (scroll / move caret) 302 } 303
304 function serializeRequestParams(params) { 305 var serialized = ""; 306 307 for (var key in params) { 308 if (serialized !== "") { 309 serialized += "&"; 310 } 311 312 serialized += key + "=" + encodeURIComponent(params[key]); 313 } 314 315 return serialized; 316 } 317 318 319 320 /** 321 * Search request stats is saved separately for non-blocking search 322 */ 323 function trackSearchRequest(searchParams) { 324 325 searchParams.trackResults = true; 326 327 return $.ajax({ 328 method: 'POST', 329 url: ajaxUrl, 330 dataType: 'json', 331 data: searchParams 332 }); 333 } 334 335 /** 336 * Waits for a timeout untill tracking results 337 * @param searchParams 338 */ 339 var trackingTimerId = null; 340 341 function handleNonBlockingTracking(searchParams) { 342 var serializedParams = serializeRequestParams(searchParams); 343 344 if (trackingTimerId) { 345 clearTimeout(trackingTimerId); 346 } 347 348 if (trackingCache[serializedParams]) { 349 return; 350 } 351 352 trackingTimerId = setTimeout(function() { 353 trackSearchRequest(searchParams).then(function() { 354 trackingCache[serializedParams] = true; 355 }); 356 }, 1000); 357 } 358 359 /** 360 * Lice search type handler 361 * @param $search 362 */ 363 function onSearchType($search) { 364 var $wrap = $search.parents('.kb-search__input-wrap'); 365 var needle = $search.val() && $search.val().trim(); 366 var topics = $search.data('topic-ids'); 367 var $kbId = $wrap.find('input[name="kb_id"]'); 368 var $lang = $wrap.find('input[name="lang"]'); 369 var kbId = $kbId.length ? parseInt($kbId.val()) : null; 370 var searchParams = { 371 action: 'mkb_kb_search', 372 search: needle, 373 mode: searchMode 374 }; 375 376 if (kbId) { 377 searchParams.kb_id = kbId; 378 } 379 380 if (topics) { 381 searchParams.topics = topics; 382 } 383 384 if ($lang.length && $lang.val()) { 385 searchParams.lang = $lang.val(); 386 } 387
388 var serializedParams = serializeRequestParams(searchParams); 389 390 if (needle) { 391 $wrap.addClass(HAS_CONTENT_CLASS); 392 } else { 393 $wrap.removeClass(HAS_CONTENT_CLASS); 394 } 395 396 // check cache for response 397 if (settings['search_request_fe_cache'] && searchCache[serializedParams]) { 398 // track cached result, if it wasn tracked before 399 if (searchMode === 'nonblocking') { 400 handleNonBlockingTracking(searchParams); 401 } 402 403 return handleSearchResultsReceive.call(this, $search, searchCache[serializedParams]); 404 } 405 406 if (!needle || needle.length < searchNeedleLength) { 407 hasResults = false; 408 resultsCount = 0; 409 activeResult = -1; 410 $wrap.removeClass(HAS_RESULTS_CLASS).removeClass(NO_RESULTS_CLASS); 411 412 if (searchMode === 'nonblocking' && needle.length > 0) { 413 fakeRequest($wrap); // progress indicator for input to be more responsive 414 } 415 416 return; 417 } 418 419 if (searchMode === 'nonblocking') { 420 handleNonBlockingTracking(searchParams); 421 } 422 423 if (searchMode === 'blocking') { 424 $search.attr('disabled', 'disabled'); 425 } 426 427 $wrap.addClass(REQUEST_CLASS); 428 ++searchRequestsCount; 429 430 $.ajax({ 431 method: settings['live_search_use_post'] ? 'POST' : 'GET', 432 url: ajaxUrl, 433 dataType: 'json', 434 data: searchParams 435 }) 436 .then(function(response) { 437 if (settings['search_request_fe_cache']) { 438 searchCache[serializedParams] = response; 439 } 440 441 return handleSearchResultsReceive.call(this, $search, response); 442 }.bind(this)) 443 .always(function () { 444 if (searchMode === 'blocking') { 445 $search 446 .attr('disabled', false) 447 .focus(); 448 } 449 450 --searchRequestsCount; 451 452 if (searchRequestsCount === 0) { 453 $wrap.removeClass(REQUEST_CLASS); 454 } 455 }); 456 } 457 458 /** 459 * Progress indicator for short requests 460 * @param $wrap 461 */
462 function fakeRequest($wrap) { 463 $wrap.addClass(REQUEST_CLASS); 464 ++searchRequestsCount; 465 466 setTimeout(function() { 467 --searchRequestsCount; 468 if (searchRequestsCount === 0) { 469 $wrap.removeClass(REQUEST_CLASS); 470 } 471 }, 500); 472 } 473 474 /** 475 * Article pageview tracking 476 */ 477 function trackArticleView() { 478 var $tracking_meta = $('.mkb-article-extra__tracking-data'); 479 480 if (!$tracking_meta.length) { 481 return; 482 } 483 484 var $id = $tracking_meta.data('article-id'); 485 486 if (!$id) { 487 return; 488 } 489 490 jQuery.ajax({ 491 method: 'POST', 492 url: ajaxUrl, 493 dataType: 'json', 494 data: addAjaxNonce({ 495 action: 'mkb_article_pageview', 496 id: $id 497 }) 498 }); 499 } 500 501 /** 502 * Article like 503 * @param e 504 */ 505 function handleArticleLike(e) { 506 e.preventDefault(); 507 508 var $likeBtn = $(e.currentTarget); 509 var id = $likeBtn.data('article-id'); 510 var title = $likeBtn.data('article-title'); 511 var $count = $('.mkb-article-extra__stats-likes'); 512 var likes = parseInt($count.text(), 10); 513 514 if (!id || $likeBtn.hasClass('mkb-voted') || $likeBtn.hasClass('mkb-disabled')) { 515 return; 516 } 517 518 $likeBtn.addClass('mkb-voted'); 519 $('.mkb-article-extra__dislike').addClass('mkb-disabled'); 520 $count.text(++likes); 521 522 jQuery.ajax({ 523 method: 'POST', 524 url: ajaxUrl, 525 dataType: 'json', 526 data: addAjaxNonce({ 527 action: 'mkb_article_like', 528 id: id 529 }) 530 }).done(function() { 531 if (settings['track_article_likes']) { 532 trackGoogleAnalytics( 533 settings['ga_like_category'], 534 settings['ga_like_action'], 535 settings['ga_like_label'] === 'article_title' ? title : id, 536 settings['ga_like_value'] || 0 537 ); 538 } 539 540 if (settings['show_like_message']) { 541 $('.fn-rating-likes-block') 542 .html($('<div class="mkb-article-extra__message">' + i18n['like_message_text'] + '</div>')); 543 } 544 545 if (settings['enable_feedback'] && 546 (settings['feedback_mode'] === 'like' || settings['feedback_mode'] === 'any')) { 547 addFeedbackForm(); 548 } 549 }); 550 } 551 552 /** 553 * Article dislike 554 * @param e 555 */ 556 function handleArticleDislike(e) { 557 e.preventDefault(); 558 559 var $dislikeBtn = $(e.currentTarget); 560 var id = $dislikeBtn.data('article-id'); 561 var title = $dislikeBtn.data('article-title'); 562 var $count = $('.mkb-article-extra__stats-dislikes'); 563 var dislikes = parseInt($count.text(), 10); 564 565 if (!id || $dislikeBtn.hasClass('mkb-voted') || $dislikeBtn.hasClass('mkb-disabled')) { 566 return; 567 } 568 569 $dislikeBtn.addClass('mkb-voted'); 570 $('.mkb-article-extra__like').addClass('mkb-disabled'); 571 $count.text(++dislikes); 572 573 jQuery.ajax({ 574 method: 'POST', 575 url: ajaxUrl, 576 dataType: 'json', 577 data: addAjaxNonce({ 578 action: 'mkb_article_dislike', 579 id: id 580 }) 581 }).done(function() { 582 if (settings['track_article_dislikes']) { 583 trackGoogleAnalytics( 584 settings['ga_dislike_category'], 585 settings['ga_dislike_action'], 586 settings['ga_dislike_label'] === 'article_title' ? title : id, 587 settings['ga_dislike_value'] || 0 588 ); 589 } 590 591 if (settings['show_dislike_message']) { 592 $('.fn-rating-likes-block') 593 .html($('<div class="mkb-article-extra__message">' + i18n['dislike_message_text'] + '</div>')); 594 } 595 596 if (settings['enable_feedback'] &&
597 (settings['feedback_mode'] === 'dislike' || settings['feedback_mode'] === 'any')) { 598 addFeedbackForm(); 599 } 600 }); 601 } 602 603 /** 604 * Renders feedback form if configured 605 */ 606 function addFeedbackForm() { 607 $('.fn-article-feedback-container').append($( 608 '<div class="mkb-article-extra__feedback-form mkb-article-extra__feedback-form--no-content fn-feedback-form">' + 609 '<div class="mkb-article-extra__feedback-form-title">' + 610 i18n['feedback_label'] + 611 '</div>' + 612 '<div class="mkb-article-extra__feedback-form-message">' + 613 '<textarea class="mkb-article-extra__feedback-form-message-area" rows="5"></textarea>' + 614 (i18n['feedback_info_text'] ? 615 ('<div class="mkb-article-extra__feedback-info">' + i18n['feedback_info_text'] + '</div>') : 616 '') + 617 '</div>' + 618 '<div class="mkb-article-extra__feedback-form-submit">' + 619 '<a href="#">' + i18n['feedback_submit_label'] + '</a>' + 620 '</div>' + 621 '</div>' 622 )); 623 } 624 625 /** 626 * Sends article feedback to server 627 * @param e 628 */ 629 function handleFeedbackSubmit(e) { 630 var $trackingMeta = $('.mkb-article-extra__tracking-data'); 631 632 e.preventDefault(); 633 634 if (!$trackingMeta.length) { 635 return; 636 } 637 638 var id = $trackingMeta.data('article-id'); 639 var title = $trackingMeta.data('article-title'); 640 var $btn = $(e.target); 641 var $content = $('.mkb-article-extra__feedback-form-message-area'); 642 643 if (!id || !$content.val()) { 644 return; 645 } 646 647 $btn
648 .text(i18n['feedback_submit_request_label']) 649 .attr('disabled', 'disabled'); 650 651 jQuery.ajax({ 652 method: 'POST', 653 url: ajaxUrl, 654 dataType: 'json', 655 data: addAjaxNonce({ 656 action: 'mkb_article_feedback', 657 id: id, 658 content: $content.val() 659 }) 660 }).done(function() { 661 if (settings['track_article_feedback']) { 662 trackGoogleAnalytics( 663 settings['ga_feedback_category'], 664 settings['ga_feedback_action'], 665 settings['ga_feedback_label'] === 'article_title' ? title : id, 666 settings['ga_feedback_value'] || 0 667 ); 668 } 669 670 $('.fn-article-feedback-container').html( 671 '<div class="mkb-article-extra__feedback-sent-message">' + 672 i18n['feedback_sent_text'] + 673 '</div>' 674 ); 675 }); 676 } 677 678 /** 679 * Toggle submit available 680 * @param e 681 */ 682 function handleFeedbackType(e) { 683 $('.fn-feedback-form').toggleClass('mkb-article-extra__feedback-form--no-content', Boolean($(e.currentTarget).val() < 1)); 684 } 685 686 /** 687 * Back to top in articles 688 */ 689 function handleArticleBackToTop() { 690 var $container = $('.mkb-container'); 691 692 $container.on('click', '.mkb-back-to-top', function (e) { 693 e.preventDefault(); 694 695 $doc.animate({ 696 scrollTop: settings['back_to_site_top'] ? 0 : $container.offset().top - adminOffset 697 }, 300); 698 699 window.location.hash = ''; 700 }); 701 } 702 703 /** 704 * Article Table of Contents 705 */ 706 function handleArticleTOC() { 707 var $entryContent = $('.mkb-article-text'); 708 var $tocList = $('.mkb-anchors-list'); 709 var scrollOffset = parseInt(settings['toc_scroll_offset']['size']); 710 var headingsExclude = settings['toc_headings_exclude'].trim().toLowerCase(); 711 var $headings; 712 var isScrollSpy = settings['scrollspy_switch'] && settings['toc_in_content_disable'] && 713 platform === 'desktop' && 714 window.outerWidth >= parseInt(settings['article_sidebar_sticky_min_width']['size'], 10); 715 716 // dynamic TOC 717 if ($tocList.hasClass('mkb-anchors-list--dynamic')) { 718 var headingsPool = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; 719 720 if (headingsExclude) { 721 var excluded = headingsExclude.split(',').map(function(heading) { 722 return heading.trim(); 723 }).filter(function(heading) { 724 return Boolean(heading); 725 }); 726 727 headingsPool = headingsPool.filter(function(heading) { 728 return excluded.indexOf(heading) === -1; 729 }); 730 } 731 732 $headings = $entryContent.find(headingsPool.join(', ')); 733 734 if (settings['show_back_to_top']) { 735 wrapTOCAnchors($headings); 736 } 737 738 } else { // via shortcodes 739 $headings = $('.mkb-anchor__title'); 740 } 741 742 // scroll 743 var headingOffsets = Array.prototype.map.call($headings, function(heading) { 744 return $(heading).offset().top; 745 }); 746 var $links = $tocList.find('.mkb-anchors-list__item-link'); 747 var linksArr = Array.prototype.map.call($links, function(link) { 748 return link; 749 }).filter(function(link) { 750 return link.dataset.index !== '-1'; // other pages links 751 }); 752 753 function recalculateMetrics() { 754 headingOffsets = Array.prototype.map.call($headings, function(heading) { 755 return $(heading).offset().top; 756 }); 757 } 758 759 $(window).on('load', recalculateMetrics); 760 $(window).on('resize', debounce(recalculateMetrics, 1000)); 761 762 setTimeout(recalculateMetrics, 3000); // in case load fails 763 764 var navScrolling = false; 765 766 function navigateToChapter(index) { 767 navScrolling = true; 768 769 recalculateMetrics(); 770 771 if (settings['toc_url_update']) { // TODO: replace with browserHistory instead 772 window.location.hash = 'ch_' + (index + 1); 773 } 774 775 $doc.animate({ 776 scrollTop: headingOffsets[index] - scrollOffset - 10 777 }, 300, 'swing', function() { 778 779 780 if (isScrollSpy && linksArr) { 781 $(linksArr).removeClass('active'); 782 $(linksArr[index]).addClass('active'); 783 } 784 785 setTimeout(function() { 786 navScrolling = false;
787 }, 300); 788 }); 789 } 790 791 // link click handlers 792 $tocList.on('click', '.mkb-anchors-list__item-link', function (e) { 793 var $item = $(e.currentTarget); 794 795 if ($item.attr('href') !== '#') { 796 return; // normal link, just navigate 797 } 798 799 var index = parseInt($item.data('index'), 10); 800 801 e.preventDefault(); 802 803 if (isScrollSpy) { 804 $links.removeClass('active'); 805 $item.addClass('active'); 806 } 807 808 navigateToChapter(index); 809 }); 810 811 var chapterHash = window.location.hash && window.location.hash.replace(/^#/, ''); 812 813 if (chapterHash && chapterHash.indexOf('ch_') !== -1) { 814 var chapterIndex = parseInt(chapterHash.replace(/^ch_/, '')) - 1; 815 816 if (chapterIndex && chapterIndex > 0) { 817 setTimeout(function() { 818 navigateToChapter(chapterIndex); 819 }, 300); 820 } else if (isScrollSpy && chapterIndex === 0) { 821 $(linksArr[0]).addClass('active'); 822 } 823 } else if (isScrollSpy) { 824 $(linksArr[0]).addClass('active'); 825 } 826 827 if (!isScrollSpy) { 828 return; 829 } 830 831 // ScrollSpy 832 var win = window; 833 var doc = document.documentElement; 834 835 $(win).on('scroll', throttle(150, function() { 836 if (navScrolling) { return; } 837 838 var top = win.pageYOffset || doc.scrollTop; 839 840 linksArr.forEach(function(item, index) { 841 var curr = index === 0 ? 0 : headingOffsets[index] - scrollOffset - 1; 842 var next = (index === headingOffsets.length - 1 ? 9999999 : headingOffsets[index + 1]) - scrollOffset - 1; 843 844 $(item).toggleClass('active', top >= curr && top < next); 845 }); 846 })); 847 } 848 849 /** 850 * Wraps headings n back to top containers when necessary 851 * @param $headings 852 */ 853 function wrapTOCAnchors($headings) { 854 $headings.each(function(index, el) { 855 var $heading = $(el); 856 857 $heading.wrap('<div class="mkb-anchor mkb-clearfix mkb-back-to-top-' + 858 settings['back_to_top_position'] + 859 '"></div>'); 860 861 $heading.addClass('mkb-anchor__title'); 862 863 $heading.parent().append('<a href="#" class="mkb-back-to-top" title="' + 864 settings['back_to_top_text'] + '">' + 865 settings['back_to_top_text'] + 866 ( 867 settings['show_back_to_top_icon'] ? 868 '<i class="mkb-back-to-top-icon fa ' + settings['back_to_top_icon'] + '"></i>' : 869 '' 870 ) + 871 '</a>'); 872 }); 873 } 874 875 /** 876 * Articles fancy box 877 */ 878 function initArticlesFancyBox() { 879 if (!$.fn.fancybox) { 880 return; 881 } 882 883 // with captions 884 $('figure[id^="attachment"] a').each(function (index, item) { 885 var $item = $(item); 886 var text = $item.parent().find('.wp-caption-text').text(); 887 888 $item.fancybox({ 889 titlePosition: 'over', 890 title: text 891 }); 892 }); 893 894 // no captions 895 $('.mkb-single-content img').each(function(i, img) { 896 var $img = $(img); 897 var $link = $img.parent(); 898 899 if ($img.parents('figure.wp-caption').length || !$link.attr('href')) { 900 return; 901 } 902 903 $link.fancybox({ 904 titlePosition: 'none', 905 title: '' 906 }); 907 }); 908 } 909 910 /** 911 * Search clear 912 * @param e 913 */ 914 function handleSearchClear(e) { 915 e.preventDefault(); 916 917 $(e.currentTarget) 918 .parents('.kb-search__input-wrap') 919 .find('.kb-search__input') 920 .val('') 921 .trigger('input') 922 .focus(); 923 } 924 925 function initSearchInputs() { 926 $kbSearch.each(function (index, el) { 927 var $search = $(el); 928 929 var searchHandler = searchMode === 'blocking' ? 930 debounce(onSearchType.bind(this, $search), parseInt(settings['search_delay'], 10) || 1000, false) : 931 throttle(parseInt(settings['search_delay'], 10) || 300, true, onSearchType.bind(this, $search), true); 932 933 $search.on('input', searchHandler); 934 }); 935 } 936 937 /** 938 * Detects if live search disabled for current platform 939 * @returns {boolean} 940 */ 941 function isSearchDisabled() { 942 return Boolean(settings['live_search_disable_' + platform]); 943 } 944 945 /** 946 * Sticky sidebar 947 */ 948 function setupArticleStickySidebar() { 949 950 if (settings['single_template'] === 'theme' ||
951 !settings['article_sidebar_sticky'] || 952 !info.isSingle || 953 platform !== 'desktop' || 954 window.outerWidth < parseInt(settings['article_sidebar_sticky_min_width']['size'], 10)) { 955 956 // sticky sidebar not enabled or not allowed 957 return; 958 } 959 960 var sticky = false; 961 var sidebarPosition = settings['article_sidebar']; 962 var atBottom = false; 963 var $sidebar = $('.mkb-sidebar'); 964 var sidebarHeight = $sidebar.outerHeight(); 965 var $root = $sidebar.parents('.mkb-root'); 966 var rootHeight = $root.outerHeight(); 967 var rootHeightInner = $root.height(); 968 var rootPad = rootHeight - rootHeightInner; 969 var rootTop = $root.offset().top; 970 var triggerPos = rootTop - rootPad / 2; 971 var winHeight = window.innerHeight; 972 var bottomOffset = winHeight > sidebarHeight + rootPad ? winHeight - sidebarHeight - rootPad : 0; 973 var width = $sidebar.outerWidth(); 974 var win = window; 975 var doc = document.documentElement; 976 977 function recalculateMetrics() { 978 rootHeight = $root.outerHeight(); 979 rootHeightInner = $root.height(); 980 rootPad = rootHeight - rootHeightInner; 981 rootTop = $root.offset().top; 982 triggerPos = rootTop - rootPad / 2; 983 winHeight = window.innerHeight; 984 sidebarHeight = $sidebar.outerHeight(); 985 bottomOffset = winHeight > sidebarHeight + rootPad ? winHeight - sidebarHeight - rootPad : 0; 986 } 987 988 $(win).on('load', recalculateMetrics); 989 $(win).on('resize', debounce(recalculateMetrics, 1000)); 990 991 setInterval(recalculateMetrics, 500); // sometimes content height changes dynamically 992 993 function updateSidebarLeftPosition() { 994 var left = $root.get(0).getBoundingClientRect().left; 995 $sidebar.css('left', left + parseInt($root.css('padding-left')) + 'px'); 996 } 997 998 function handleScroll() { 999 var top = win.pageYOffset || doc.scrollTop; 1000 var bottom = top + winHeight - bottomOffset; 1001 1002 if (bottom > rootHeight + rootTop && !atBottom || bottom <= rootHeight + rootTop && atBottom) { 1003 atBottom = !atBottom; 1004 $sidebar.toggleClass('mkb-fixed-bottom', atBottom); 1005 1006 if (sidebarPosition === 'left') { 1007 if (atBottom) { 1008 $sidebar.css('left', ''); 1009 } else { 1010 updateSidebarLeftPosition(); 1011 } 1012 } 1013 } 1014 1015 if (sticky && top >= triggerPos || !sticky && top < triggerPos) { 1016 return; 1017 } 1018 1019 sticky = !sticky; 1020 $sidebar.toggleClass('mkb-fixed', sticky); 1021 $sidebar.css('max-width', sticky ? width + 'px' : 'none'); 1022 1023 if (sidebarPosition === 'left') { 1024 if (sticky) { 1025 if (!atBottom) { 1026 updateSidebarLeftPosition(); 1027 } 1028 1029 $(window).on('resize', updateSidebarLeftPosition); 1030 } else { 1031 $sidebar.css('left', 0); 1032 $(window).off('resize', updateSidebarLeftPosition); 1033 } 1034 } 1035 } 1036 1037 $(win).on('scroll', handleScroll); 1038 } 1039 1040 /** 1041 * FAQ 1042 */ 1043 function setupFaq() { 1044 var $faqContainer = $('.fn-kb-faq-container'); 1045 1046 if (!$faqContainer.length) { 1047 return; 1048 } 1049 1050 function getHashFaqId() { 1051 var currentHash = window.location.hash.replace('#', ''); 1052 return /^qa_/.test(currentHash) && currentHash.replace('qa_', '') || false; 1053 } 1054 1055 function setHashFaq(id) { 1056 history.replaceState(null, '', 1057 window.location.origin + window.location.pathname + window.location.search + 1058 '#qa_' + id); 1059 } 1060 1061 function clearHash() { 1062 history.replaceState(null, '', 1063 window.location.origin + window.location.pathname + window.location.search); 1064 } 1065 1066 $faqContainer.each(function(index, item) { 1067 var $container = $(item); 1068 var FAQ_HIDDEN_CLASS = 'mkb-faq-item-hidden'; 1069 var FAQ_SECTION_HIDDEN_CLASS = 'mkb-faq-section-hidden'; 1070 var $filterForm = $container.find('.fn-kb-faq-filter'); 1071 var $filter = $filterForm.find('.fn-kb-faq-filter-input'); 1072 var $sections = $container.find('.fn-kb-faq-section'); 1073 var $noResults = $container.find('.fn-kb-faq-no-results'); 1074 1075 // FAQ sections 1076 var sections = [].map.call($sections, function(section) { 1077 var $section = $(section); 1078 var $count = $section.find('.fn-kb-faq-section-count'); 1079 var $items = [].map.call($section.find('.fn-kb-faq-item'), function(item) { 1080 var $item = $(item); 1081 1082 return { 1083 $el: $item, 1084 question: $item.find('.fn-kb-faq-question').text().trim().toLowerCase(), 1085 answer: $item.find('.fn-kb-faq-answer').text().trim().toLowerCase(), 1086 isVisible: true 1087 }; 1088 }); 1089 1090 return { 1091 $el: $section, 1092 $countEl: $count.length ? $count : null, 1093 items: $items, 1094 visible: [].map.call($items, function(item) { return $(item) }), 1095 hidden: [] 1096 }; 1097 }); 1098 1099 /** 1100 * FAQ Filter 1101 */ 1102 var currentFilter; 1103 1104 function updateFilter(visCheck) { 1105 visCheck = visCheck || checkVisibility; 1106 1107 var totalVisible = 0; 1108 1109 // check visibility 1110 sections.forEach(function(section) { 1111 section.visible = []; 1112 section.hidden = [];
1113 section.items.forEach(function(item) { 1114 section[(item.isVisible = visCheck(item)) ? 'visible' : 'hidden'].push(item); 1115 }); 1116 totalVisible += section.visible.length; 1117 }); 1118 1119 $noResults.toggleClass('mkb-hidden', totalVisible > 0); 1120 1121 sections.forEach(function(section) { 1122 section.visible.forEach(function(item) { 1123 item.$el.removeClass(FAQ_HIDDEN_CLASS); 1124 }); 1125 section.hidden.forEach(function(item) { 1126 item.$el.addClass(FAQ_HIDDEN_CLASS); 1127 }); 1128 section.$countEl && section.$countEl.html(section.visible.length + 1129 ' ' + (section.visible.length === 1 ? i18n['question'] : i18n['questions']) 1130 ); 1131 section.$el.toggleClass(FAQ_SECTION_HIDDEN_CLASS, !section.visible.length); 1132 section.visible = []; 1133 section.hidden = []; 1134 }); 1135 1136 if (settings['faq_filter_open_single'] && totalVisible === 1) { 1137 var $onlyItem = $container.find('.fn-kb-faq-item:not(.' + FAQ_HIDDEN_CLASS + ')'); 1138 1139 if ($onlyItem.length && !$onlyItem.hasClass('kb-faq__questions-list-item--open')) { 1140 toggleAnswer($onlyItem); 1141 } 1142 } 1143 } 1144 1145 var resetFilter = updateFilter.bind(this, function() { return true; }); 1146 1147 function checkVisibility(item) { 1148 return item.question.indexOf(currentFilter) !== -1 || item.answer.indexOf(currentFilter) !== -1; 1149 } 1150 1151 function handleFilterChange(e) { 1152 var needle = (e.currentTarget.value || '').trim(); 1153 1154 if (needle.length < 3) { 1155 $filterForm.addClass('kb-faq__filter--empty'); 1156 currentFilter = ''; 1157 resetFilter(); 1158 1159 $filter.focus(); 1160 return 1161 } 1162 1163 $filterForm.removeClass('kb-faq__filter--empty'); 1164 currentFilter = needle.toLowerCase(); 1165 updateFilter(); 1166 } 1167 1168 function handleFilterClear(e) { 1169 e.preventDefault(); 1170 1171 $filter.val('').trigger('input'); 1172 } 1173 1174 if ($filterForm.length) { 1175 $container.on('input', '.fn-kb-faq-filter-input', handleFilterChange); 1176 $container.on('click', '.fn-kb-faq-filter-clear', handleFilterClear); 1177 } 1178 1179 var OPEN_SPEED = settings['faq_slow_animation'] ? 400 : 100; 1180 1181 function getMaxHeight(el) { 1182 return Array.prototype.reduce.call(el.childNodes, function(store, current) { 1183 return store + (current.offsetHeight || 0); 1184 }, 0); 1185 } 1186 1187 /** 1188 * FAQ Toggle 1189 */ 1190 function toggleAnswer($item) { 1191 var $answer = $item.find('.fn-kb-faq-answer'); 1192 var $link = $item.find('.fn-kb-faq-link'); 1193 var answerEl = $answer.get(0); 1194 var maxHeight = getMaxHeight(answerEl); 1195 1196 if ($item.hasClass('kb-faq__questions-list-item--open')) { 1197 $answer.css('max-height', maxHeight); 1198 $answer.animate({maxHeight: 0}, OPEN_SPEED, 'swing', function() { 1199 $item.removeClass('kb-faq__questions-list-item--open'); 1200 }); 1201 1202 if (settings['faq_url_update']) { 1203 var hashFaqId = getHashFaqId(); 1204 1205 if (hashFaqId && hashFaqId == $link.data('id')) { 1206 clearHash(); 1207 } 1208 } 1209 } else { 1210 if (settings['faq_toggle_mode']) { 1211 sections.forEach(function(section) { 1212 section.items.forEach(function(item) { 1213 closeAnswer(item.$el); 1214 }); 1215 }); 1216 } 1217 1218 $answer.animate({maxHeight: maxHeight}, OPEN_SPEED, 'swing', function() { 1219 $answer.css('max-height', 'none'); 1220 $item.addClass('kb-faq__questions-list-item--open'); 1221 }); 1222 1223 if (settings['faq_url_update']) { 1224 setHashFaq($link.data('id')); 1225 } 1226 } 1227 } 1228 1229 function openAnswer($item) { 1230 var $answer = $item.find('.fn-kb-faq-answer'); 1231 var answerEl = $answer.get(0); 1232 var maxHeight = getMaxHeight(answerEl); 1233 1234 if (!$item.hasClass('kb-faq__questions-list-item--open')) { 1235 $answer.animate({maxHeight: maxHeight}, OPEN_SPEED, 'swing', function() { 1236 $answer.css('max-height', 'none'); 1237 $item.addClass('kb-faq__questions-list-item--open'); 1238 }); 1239 } 1240 } 1241 1242 function closeAnswer($item) { 1243 var $answer = $item.find('.fn-kb-faq-answer'); 1244 var answerEl = $answer.get(0); 1245 var maxHeight = getMaxHeight(answerEl); 1246 1247 if ($item.hasClass('kb-faq__questions-list-item--open')) { 1248 $answer.css('max-height', maxHeight); 1249 $answer.animate({maxHeight: 0}, OPEN_SPEED, 'swing', function() { 1250 $item.removeClass('kb-faq__questions-list-item--open'); 1251 }); 1252 } 1253 } 1254 1255 function handleQuestionClick (e) { 1256 e.preventDefault(); 1257 1258 var $link = $(e.currentTarget); 1259 var $item = $link.parent(); 1260 1261 toggleAnswer($item); 1262 } 1263 1264 function handleToggleAllClick (e) { 1265 e.preventDefault(); 1266 1267 var $link = $(e.currentTarget); 1268 var isOpen = $link.hasClass('kb-faq__toggle-all-link--open'); 1269 1270 sections.forEach(function(section) {
1271 section.items.forEach(function(item) { 1272 isOpen ? closeAnswer(item.$el) : openAnswer(item.$el); 1273 }); 1274 }); 1275 1276 $link.toggleClass('kb-faq__toggle-all-link--open'); 1277 } 1278 1279 $container.on('click', '.fn-kb-faq-link', handleQuestionClick); 1280 $container.on('click', '.fn-kb-faq-toggle-all', handleToggleAllClick); 1281 }); 1282 1283 if (settings['faq_url_update']) { 1284 // navigate to faq from hash 1285 var hashFaqId = getHashFaqId(); 1286 var $faqLink = hashFaqId && $('.fn-kb-faq-link[data-id="' + hashFaqId + '"]'); 1287 var scrollOffset = parseInt(settings['faq_scroll_offset']['size']); 1288 1289 if ($faqLink && $faqLink.length) { 1290 $faqLink.trigger('click'); 1291 1292 setTimeout(function() { 1293 $doc.animate({ 1294 scrollTop: $faqLink.offset().top - scrollOffset 1295 }, 300); 1296 }, 800); 1297 } 1298 } 1299 } 1300 1301 /** 1302 * Content Tree 1303 */ 1304 function setupContentTreeWidgets() { 1305 var $contentTree = $('.mkb_content_tree_widget'); 1306 var openActiveBranch = settings['content_tree_widget_open_active_branch']; 1307 1308 function setListMaxHeight(index, list) { 1309 var $list = $(list); 1310 $list.animate({'max-height': list.scrollHeight}, 200, function() { 1311 $list.css('max-height', 'none'); 1312 }); 1313 } 1314 1315 $contentTree.each(function(index, tree) { 1316 var $tree = $(tree); 1317 var $topics = $tree.find('.mkb-widget-content-tree__topic'); 1318 1319 $tree.on('click', '.mkb-widget-content-tree__topic-name', function(e) { 1320 var topicName = e.currentTarget; 1321 var $topic = $(topicName).parent(); 1322 var topic = $topic.get(0); 1323 1324 if ($topic.hasClass('topic-open')) { 1325 $topic.removeClass('topic-open'); 1326 $topic.find('>ul').css('max-height', '0'); 1327 } else { 1328 var activeBranch = [topic.dataset.id]; 1329 var $parents = $topic.parents('.mkb-widget-content-tree__topic'); 1330 1331 $parents.each(function(index, parent) { 1332 activeBranch.push(parent.dataset.id); 1333 }); 1334 1335 // hide topics that are not in current branch 1336 $topics.each(function(index, item) { 1337 if (activeBranch.indexOf(item.dataset.id) !== -1) { 1338 return; 1339 } 1340 1341 var $item = $(item); 1342 1343 $item.removeClass('topic-open'); 1344 $item.find('>ul').css('max-height', '0'); 1345 }); 1346 1347 $topic.find('>ul').each(setListMaxHeight); 1348 $topic.addClass('topic-open'); 1349 1350 $parents.each(function(index, item) { 1351 var $parentTopic = $(item); 1352 1353 if ($parentTopic.hasClass('topic-open')) { 1354 return; 1355 } 1356 1357 $parentTopic.find('>ul').each(setListMaxHeight); 1358 $parentTopic.addClass('topic-open'); 1359 }); 1360 } 1361 1362 e.preventDefault(); 1363 e.stopImmediatePropagation(); 1364 }); 1365 1366 if (openActiveBranch) { 1367 setTimeout(function() { 1368 $tree.find('.mkb-widget-content-tree__article--active') 1369 .closest('.mkb-widget-content-tree__topic') 1370 .find('>.mkb-widget-content-tree__topic-name') 1371 .trigger('click'); 1372 }, 300); 1373 } 1374 }); 1375 } 1376 1377 /** 1378 * Client submission form 1379 */ 1380 function setupSubmissionForm() { 1381 var $submisionContainers = $('.js-mkb-client-submission'); 1382 1383 $submisionContainers.each(function(index, container) { 1384 var $container = $(container); 1385 var $form = $container.find('.js-mkb-client-submission-form'); 1386 1387 var $title = $container.find('.js-mkb-submission-title'); 1388 var $topic = $container.find('.js-mkb-submission-topic'); 1389 var $content = $container.find('#mkb-client-editor'); 1390 var $antispamAnswer = $container.find('.js-mkb-real-human-answer'); 1391 var $messagesContainer = $container.find('.js-mkb-form-messages'); 1392 1393 var toolbarOptions = [ 1394 [{ 'header': [1, 2, 3, 4, 5, 6, false] }], 1395 ['bold', 'italic', 'underline', 'strike'], 1396 [{ 'list': 'ordered'}, { 'list': 'bullet' }], 1397 ['link'], 1398 ['blockquote', 'code-block'], 1399 ['clean'] 1400 ]; 1401 1402 var quillEditor = new Quill('#mkb-client-editor', { 1403 modules: { 1404 toolbar: toolbarOptions, 1405 history: { 1406 delay: 2000,
1407 maxStack: 500, 1408 userOnly: true 1409 } 1410 }, 1411 theme: settings.submit_content_editor_skin 1412 }); 1413 1414 function getData() { 1415 return { 1416 action: 'mkb_save_client_submission', 1417 title: $title.val().trim(), 1418 content: $content.find('.ql-editor').get(0).innerHTML.trim(), 1419 topic: $topic.length ? $topic.val().trim() : '', 1420 antispam: $antispamAnswer.length ? $antispamAnswer.val().trim() : '' 1421 } 1422 } 1423 1424 function validateData(data) { 1425 var status = 0; 1426 var errors = []; 1427 1428 if (!data.title) { 1429 status = 1; 1430 errors.push(i18n.submission_empty_title); 1431 } 1432 1433 if (!data.content 1434 .replace('<p>', '') 1435 .replace('</p>', '') 1436 .replace('<br>', '')) { 1437 1438 status = 1; 1439 errors.push(i18n.submission_empty_content); 1440 } 1441 1442 if (!data.antispam && $antispamAnswer.length) { 1443 status = 1; 1444 errors.push(settings.antispam_failed_message); 1445 } 1446 1447 return { 1448 status: status, 1449 errors: errors 1450 } 1451 } 1452 1453 function renderMessages(messages) { 1454 $messagesContainer.html(messages.reduce(function(html, err) { 1455 return html + '<div class="mkb-form-message">' + err + '</div>' 1456 }), ''); 1457 } 1458 1459 function showMessages(messageClass, noScroll) { 1460 noScroll = noScroll || false; 1461 1462 $messagesContainer.addClass(messageClass).removeClass('mkb-hidden'); 1463 1464 if (!noScroll) { 1465 $doc.animate({ 1466 scrollTop: $messagesContainer.offset().top - adminOffset - 30 1467 }, 100); 1468 } 1469 } 1470 1471 function showErrorMessages(noScroll) { 1472 showMessages('mkb-form-error', noScroll); 1473 } 1474 1475 function showSuccessMessages(noScroll) { 1476 showMessages('mkb-form-success', noScroll); 1477 } 1478 1479 $container.on('click', '.js-mkb-client-submission-send', function(e) { 1480 var requestData = getData(); 1481 var $btn = $(e.currentTarget); 1482 1483 if ($btn.hasClass('mkb-disabled')) { 1484 return; 1485 } 1486 1487 var validationResult = validateData(requestData); 1488 1489 if (validationResult.status !== 0) { 1490 renderMessages(validationResult.errors); 1491 showErrorMessages(); 1492 1493 $doc.animate({ 1494 scrollTop: $messagesContainer.offset().top - adminOffset - 30 1495 }, 100); 1496 1497 return; 1498 } 1499 1500 $btn.addClass('mkb-disabled'); 1501 1502 jQuery.ajax({ 1503 method: 'POST', 1504 url: ajaxUrl, 1505 dataType: 'json', 1506 data: addAjaxNonce(requestData) 1507 }).done(function(response) { 1508 1509 $btn.removeClass('mkb-disabled'); 1510 1511 if (response.status == 1) { 1512 renderMessages([response.error]); 1513 showErrorMessages(); 1514 } else { 1515 renderMessages([settings.submit_success_message]); 1516 showSuccessMessages(true); 1517 1518 $form.html(''); 1519 1520 $doc.animate({ 1521 scrollTop: $container.offset().top - adminOffset - 30 1522 }, 100); 1523 } 1524 }); 1525 }); 1526 }); 1527 } 1528 1529 /** 1530 * KB floating helper 1531 */ 1532 function setupHelper() { 1533 var $helper = $('.js-mkb-floating-helper'); 1534 1535 if (!$helper.length) { 1536 return; 1537 } 1538 1539 var $searchInput = $helper.find('.kb-search__input'); 1540 1541 $helper.on('click', '.js-mkb-floating-helper-btn', function() { 1542 $helper.addClass('mkb-floating-helper-wrap--open'); 1543 $searchInput.get(0).focus(); 1544 }); 1545 1546 $helper.on('click', '.js-mkb-floating-helper-close', function() { 1547 $helper.removeClass('mkb-floating-helper-wrap--open'); 1548 }); 1549 1550 setTimeout(function() { 1551 $helper.addClass('mkb-floating-helper-wrap--ready'); 1552 }, settings.fh_show_delay); 1553 } 1554 1555 /** 1556 * Attachments 1557 */ 1558 function setupAttachments() { 1559 // download tracking 1560 $body.on('click', '.js-mkb-attachment-link', function(e) { 1561 var link = e.currentTarget; 1562 var id = link.dataset.id; 1563 1564 if (!id) { 1565 return; 1566 } 1567 1568 jQuery.ajax({ 1569 method: 'POST', 1570 url: ajaxUrl, 1571 dataType: 'json', 1572 data: addAjaxNonce({ 1573 action: 'mkb_track_attachment_download', 1574 id: id 1575 }) 1576 }) 1577 }); 1578 } 1579 1580 /** 1581 * Main plugin startup 1582 */
1583 function init() { 1584 $adminBar = $('#wpadminbar'); 1585 adminOffset = $adminBar.length ? $adminBar.height() : 0; 1586 1587 if ($kbSearch.length && !isSearchDisabled()) { 1588 initSearchInputs(); 1589 $body.on('keydown', onSearchKeyPress); 1590 $body.on('click', '.kb-search__clear', handleSearchClear); 1591 focusInput(); 1592 onSearchType($kbSearch.eq(0)); // restore previous search 1593 } 1594 1595 // FAQ items 1596 setupFaq(); 1597 1598 // article related code 1599 if (info.isSingle) { 1600 $body.on('click', '.mkb-article-extra__like', handleArticleLike); 1601 $body.on('click', '.mkb-article-extra__dislike', handleArticleDislike); 1602 $body.on('click', '.mkb-article-extra__feedback-form-submit', handleFeedbackSubmit); 1603 $body.on('input', '.mkb-article-extra__feedback-form-message-area', handleFeedbackType); 1604 1605 handleArticleBackToTop(); 1606 handleArticleTOC(); 1607 1608 if (settings['article_fancybox']) { 1609 initArticlesFancyBox(); 1610 } 1611 1612 trackArticleView(); 1613 1614 // sticky articles sidebar 1615 setupArticleStickySidebar(); 1616 1617 setupAttachments(); 1618 } 1619 1620 setupContentTreeWidgets(); 1621 setupSubmissionForm(); 1622 setupHelper(); 1623 } 1624 1625 // start 1626 $(document).ready(init); 1627 1628})(jQuery);
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.