1"use strict"; 2 3function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } 4 5function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } 6 7function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 8 9function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 10 11function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } 12 13function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } 14 15function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 16 17function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 18 19var $loader = $('#ajax-loader'); 20var $loaderInfinite = $('#ajax-loader-infinite'); 21var xhr; 22var notyfOpts = { 23 types: [{ 24 type: 'success', 25 backgroundColor: 'auto', 26 className: 'notyf-success', 27 icon: false 28 }, { 29 type: 'danger', 30 backgroundColor: 'auto', 31 className: 'notyf-danger', 32 icon: false 33 }, { 34 type: 'error', 35 backgroundColor: 'auto', 36 className: 'notyf-danger', 37 icon: false 38 }, { 39 type: 'info', 40 className: 'notyf-info', 41 icon: false 42 }, { 43 type: 'warning', 44 className: 'notyf-warning', 45 icon: false 46 }, { 47 type: 'snack', 48 className: 'notyf-snack', 49 icon: false 50 }], 51 position: { 52 x: 'center', 53 y: 'bottom' 54 }, 55 duration: 2500, 56 ripple: true 57}; 58window.notyf = new Notyf(notyfOpts); 59var datePickerOpts = { 60 selectMonths: false, 61 formatSubmit: 'dd/mmmm/yyyy' 62}; 63var recaptchaLoaded = false; 64 65if (typeof grecaptcha !== 'undefined') { 66 recaptchaLoaded = true; 67} 68 69var recaptchaWidgets = {}; 70 71if (window.Parsley) { 72 window.Parsley.addValidator('maxFileSize', { 73 validateString: function validateString(_value, maxSize, parsleyInstance) { 74 var files = parsleyInstance.$element[0].files; 75 return files.length != 1 || files[0].size <= maxSize * 1024; 76 }, 77 requirementType: 'integer', 78 messages: { 79 custom: window.locale["validation-max-file-size"] 80 } 81 }); 82} 83/** 84 * Callback for recaptcha 85 * 86 * @param token 87 */ 88 89 90function recaptchaSuccessCallback(token) { 91 $('.captcha-error').each(function (index, el) { 92 $(this).hide(); 93 }); 94 $('.recaptcha-error').each(function (index, el) { 95 $(this).removeClass('recaptcha-error'); 96 }); 97} 98/** 99 * The theme object 100 * 101 * @type {Object} 102 */ 103 104 105var $theme = {}; 106/** 107 * Options for parsley 108 * 109 * @type {Object} 110 */ 111 112$theme.parsleyOptions = { 113 errorClass: 'is-invalid', 114 successClass: 'is-valid', 115 errorsWrapper: '<span class="parsley-text form-text text-danger"></span>', 116 errorTemplate: '<span></span>', 117 trigger: 'focusout', 118 focusInvalid: true, 119 errorsContainer: function errorsContainer(el) { 120 return el.$element.closest('.form-group'); 121 } 122}; 123/** 124 * Get svg icon for an ID 125 * 126 * @param {String} id 127 * @param {String} classes 128 * @param {Object} attrs 129 * @return {String} 130 */ 131 132$theme.svgIcon = function (id, classes, attrs) { 133 attrs = attrs || {}; 134 classes = classes || ''; 135 classes = 'svg-icon ' + classes; 136 var attrText = ''; 137 138 for (prop in attrs) { 139 if (prop == 'class') { 140 continue; 141 } 142 143 attrText += '' + prop + '="' + attrs[prop] + '"'; 144 } 145 146 return '<svg class="' + classes + '" ' + attrText + '><use xlink:href="#' + id + '"/></svg>'; 147}; 148/** 149 * Get svg icon for alert type 150 * 151 * @param {String} type 152 * @return {String} 153 */ 154 155 156$theme.svgIconForAlert = function (type) { 157 var svgID = 'notifications'; 158 159 switch (type) { 160 case 'danger': 161 case 'warning': 162 svgID = 'warning'; 163 break; 164 165 case 'success': 166 svgID = 'checkmark'; 167 break; 168 169 default: 170 svgID = 'notifications'; 171 break; 172 } 173 174 return $theme.svgIcon(svgID); 175}; 176/** 177* Builds a bootstrap alert 178* 179* @param String text 180* @param String type 181* @param Boolean dismissable 182* @return String 183*/ 184 185 186$theme.buildAlert = function (text, type, dismissable, withIcon) { 187 type = type || 'success'; 188 var icon_class = ' '; 189 190 if (withIcon) { 191 icon_class = ' alert-icon '; 192 } 193 194 var html = '<div class="alert' + icon_class + 'alert-' + type + '">'; 195 196 if (withIcon) { 197 html += $theme.svgIconForAlert(type, 'mr-2'); 198 } 199 200 if (dismissable) { 201 html += '<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span></button>'; 202 } 203 204 html += text + '</div>'; 205 return html; 206}; 207/** 208 * Localize numbers 209 * 210 * @param {String} text 211 * @return {String} 212 */ 213 214 215$theme.localizeNumbers = function (text) { 216 text = text.toString(); 217 218 for (var counter = 0; counter < 10; counter++) { 219 var key = "num_" + counter; 220 text = text.replace(new RegExp(counter, 'g'), window.locale[key]); 221 } 222 223 return text; 224}; 225/**
226 * Function to update a URL parameter 227 * 228 * @param {String} uri 229 * @param {String} paramKey 230 * @param {String} paramVal 231 * @return {String} 232 */ 233 234 235$theme.updateUrlParam = function (uri, paramKey, paramVal) { 236 var url = new URL(uri); 237 var search_params = url.searchParams; 238 search_params.set(paramKey, paramVal); 239 url.search = search_params.toString(); 240 return url.toString(); 241}; 242/** 243 * String templating 244 * 245 * @param {String} text 246 * @param {Object} replacements 247 * @return {String} 248 */ 249 250 251$theme.strtpl = function (text, replacements) { 252 text = text.replace(/\%(\s*?[\w.]+\s*?)%/mg, function (match, contents, offset, input_string) { 253 if (contents in replacements) { 254 return replacements[contents]; 255 } 256 257 console.log('No replacement found for ' + match); 258 return ''; 259 }); 260 return text; 261}; 262/** 263 * Build query string from object 264 * 265 * @param {Object} obj 266 * @return {String} 267 */ 268 269 270$theme.buildQueryString = function (obj) { 271 var str = []; 272 273 for (var p in obj) { 274 if (obj.hasOwnProperty(p)) { 275 str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); 276 } 277 } 278 279 return str.join("&"); 280}; 281/** 282 * Perform a redirect 283 * 284 * @param {String} url 285 * @param {Boolean} cache 286 * @param {Boolean} httpRedirect 287 */ 288 289 290$theme.redirect = function (url, cache, httpRedirect) { 291 if (cache === false) { 292 var time = new Date().getTime(); 293 url = $theme.updateUrlParam(url, '_', time); 294 } 295 296 if (httpRedirect) { 297 window.location.replace(url); 298 } else { 299 window.location.href = url; 300 } 301 302 return true; 303}; 304/** 305 * Alias of $theme.redirect() for quick HTTP redirection 306 * 307 * @param {String} url 308 * @param {Boolean} cache 309 */ 310 311 312$theme.httpRedirect = function (url, cache) { 313 return $theme.redirect(url, cache, true); 314}; 315/** 316 * Alias of $theme.redirect() for current page reloading 317 * 318 * @param {Boolean} httpRedirect 319 * @param {Boolean} cache 320 */ 321 322 323$theme.selfReload = function (httpRedirect, cache) { 324 httpRedirect = httpRedirect || false; 325 cache = cache || true; 326 return $theme.redirect(window.location.href, cache, httpRedirect); 327}; 328 329$theme.isObject = function (value) { 330 return value && _typeof(value) === 'object' && value.constructor === Object; 331}; 332 333$theme.isString = function (value) { 334 return typeof value === 'string' || value instanceof String; 335}; 336 337$theme.scrollToID = function (id, offset, delay) { 338 offset = offset || 70; 339 delay = delay || 500; 340 341 if (!id.startsWith('#')) { 342 id = '#' + id; 343 } 344 345 var position = $(id).offset().top - offset; 346 $('html, body').animate({ 347 scrollTop: position 348 }, delay); 349}; 350/** 351 * Initilazes parley with the options 352 * 353 */ 354 355 356$theme.loadParsley = function () { 357 if (window.Parsley) { 358 $("form").parsley($theme.parsleyOptions); 359 window.Parsley.on('field:error', function (el) { 360 this.$element.closest('.form-group').removeClass('input-has-success').addClass('input-has-error'); 361 }); 362 window.Parsley.on('field:success', function (el) { 363 this.$element.closest('.form-group').removeClass('input-has-error').addClass('input-has-success'); 364 }); 365 } 366}; 367/** 368 * Refreshes Google Recaptcha 369 * 370 */ 371 372 373$theme.refreshRecaptcha = function () { 374 if (!recaptchaLoaded) { 375 return; 376 } 377 378 $('.g-recaptcha').each(function (index, el) { 379 var captcha = $(this); 380 var id = captcha.attr('id'); 381 var options = { 382 sitekey: window.app.recaptchaSiteKey 383 }; 384 var dataAttrs = ['sitekey', 'theme', 'tabindex', 'callback', 'expired-callback', 'error-callback']; 385 var val = ''; 386 387 for (var i = dataAttrs.length - 1; i >= 0; i--) { 388 val = dataAttrs[i]; 389 var value = captcha.data(val); 390 391 if (value) { 392 options[val] = value; 393 } 394 } // Google throws error when rendering twice 395 396 397 try { 398 var widgetId = grecaptcha.render(el, options); 399 recaptchaWidgets[id] = widgetId; 400 } catch (e) {} 401 }); 402}; 403/** 404 * Reload the markup after DOM changes 405 * 406 * @param {String} target 407 */ 408 409 410$theme.refreshMarkup = function (target) { 411 $theme.loadParsley(); 412 $theme.refreshRecaptcha(); 413 $('.navdrawer-backdrop').remove(); 414 dynamicThemeEvents(); 415 $('.floating-label .custom-select, .floating-label .form-control').floatinglabel(); 416}; 417/**
418 * Starts the ajaxLoader 419 * 420 * @param {Boolean} insideForm Determines if the request is sent from a form submit or not 421 */ 422 423 424$theme.startLoader = function (insideForm) { 425 $loader.width(0).show(); 426 427 if (insideForm) { 428 return; 429 } 430 431 $loader.width(30 + Math.random() * 30 + "%"); 432}; 433/** 434 * Finish the ajaxLoader 435 * 436 * @param {Boolean} insideForm Determines if the request is sent from a form submit or not 437 */ 438 439 440$theme.finishLoader = function () { 441 $loader.width("100%").delay(50).fadeOut(400, function () { 442 $(this).width("0"); 443 }); 444}; 445 446$theme.showInfiniteLoader = function () { 447 $loaderInfinite.width("100%").fadeIn('fast'); 448}; 449 450$theme.hideInfiniteLoader = function () { 451 $loaderInfinite.width("0").fadeOut('fast'); 452}; 453/** 454 * Call a function 455 * 456 * @param {String} funcname 457 * @param {Array} args 458 */ 459 460 461$theme.callFunction = function (funcname, args) { 462 if (!funcname) { 463 return; 464 } 465 466 if (typeof funcname === "function") { 467 return funcname.apply(null, args); 468 } 469 470 return window[funcname].apply(null, args); 471}; 472/** 473 * Handles Pjax loading 474 * 475 * @param {String} target 476 * @param {String} type 477 * @param {Object} params 478 * @param {Object} options 479 */ 480 481 482$theme.ajaxContentLoad = function (target, type, params, options) { 483 var formattedTarget; 484 type = type || 'get'; 485 params = params || {}; 486 options = options || {}; 487 488 if (!options.el) { 489 options.el = null; 490 } 491 492 if (type.toLowerCase() == 'post') { 493 type = 'POST'; 494 params[app.csrfKey] = app.csrfToken; 495 } else { 496 type = 'GET'; 497 } 498 499 if (options.instantFetch) { 500 formattedTarget = target; 501 } else { 502 formattedTarget = $theme.updateUrlParam(target, 'ajax', 'true'); 503 } 504 505 var finished = $theme.callFunction(options.pre_callback, [options.el]); // if explicitly returned TRUE we assume callback wants to handle the response completely on it's own 506 507 if (finished === true) { 508 return; 509 } 510 511 $theme.startLoader(); 512 $.ajax({ 513 url: formattedTarget, 514 data: params, 515 type: type, 516 xhr: function xhr() { 517 var xhr = $.ajaxSettings.xhr(); 518 519 xhr.onprogress = function (e) { 520 if (e.lengthComputable) { 521 $loader.width(e.loaded / e.total * 100 + '%'); 522 } 523 }; 524 525 xhr.upload.onprogress = function (e) { 526 if (e.lengthComputable) { 527 $loader.width(e.loaded / e.total * 100 + '%'); 528 } 529 }; 530 531 return xhr; 532 }, 533 headers: { 534 'x-spark-ajax': true 535 } 536 }).done(function (response) { 537 $('body').removeClass('ajax-started'); 538 var finished = $theme.callFunction(options.success_callback, [response, options.el]); // if explicitly returned TRUE we assume callback wants to handle the response completely on it's own 539 540 if (finished === true) { 541 return; 542 } 543 544 if (response.redirect) { 545 return $theme.redirect(response.redirect); 546 } 547 548 if (response.dash) { 549 $theme.ajaxContentLoad(response.dash, 'GET', {}); 550 return; 551 } 552 553 if (response.title) { 554 document.title = response.title; 555 } 556 557 if (response.body_class) { 558 $('body').attr('class', response.body_class); 559 } 560 561 if (response.html_class) { 562 $('html').attr('class', response.html_class); 563 } // content that needs to be replaced 564 565 566 if (response.sections) { 567 for (var element in response.sections) { 568 $('#' + element).html(response.sections[element]); 569 } 570 } // content that needs to be appended 571 572 573 if (response.append) { 574 for (var element in response.append) { 575 $('#' + element).append(response.append[element]); 576 } 577 } // content that needs to be prepended 578 579 580 if (response.prepend) { 581 for (var element in response.prepend) { 582 $('#' + element).append(response.prepend[element]); 583 } 584 } 585 586 if (response.attr) { 587 for (var selector in response.attr) { 588 var attributes = response.attr[selector]; 589 590 if (selector == 'this') { 591 options.el.attr(attributes); 592 continue; 593 } 594 595 for (var attr in attributes) { 596 var value = attributes[attr]; 597 $(selector).each(function () { 598 $(this).attr(attr, value); 599 }); 600 } 601 } 602 } // reload the plugins 603 604 605 $theme.refreshMarkup(target); // the request type is GET 606 607 if (type == 'GET') { 608 // If we are on a different URL then add set the pushstate 609 if (!options.instantFetch && target != window.location.href) { 610 if (response.url) { 611 window.history.pushState({ 612 path: response.url 613 }, '', response.url); 614 } else { 615 window.history.pushState({ 616 path: target 617 }, '', target); 618 } 619 } // Update the route URL variable if provided 620 621 622 if (response.current_route_uri && window.app.currentRouteURI) { 623 window.app.currentRouteURI = response.current_route_uri; 624 } // Update the route name variable if provided 625 626 627 if (response.current_route_name && window.app.currentRouteName) { 628 window.app.currentRouteName = response.current_route_name; 629 } 630 } // optional scroll to top 631 632 633 if (options.scrolltop) { 634 $(document).scrollTop(0); 635 } 636 }).fail(function (response) { 637 var finished = $theme.callFunction(options.error_callback, [response, options.el]); // if explicitly returned TRUE we assume callback wants to handle the response completely on it's own 638 639 if (finished === true) { 640 return; 641 } 642 643 notyf.error(response.status + ' ' + response.statusText); 644 }).always(function () { 645 $theme.finishLoader(); 646 });
647 $theme.callFunction(options.post_callback, [options.el]); 648}; 649/** 650 * Handles ajax form submit 651 * 652 * @param {Object} form The jQuery form element 653 * @return {[type]} 654 */ 655 656 657$theme.ajaxFormHandler = function (form) { 658 var data = new FormData(form[0]); 659 var action = form.prop('action'); 660 var method = form.prop('method').toLowerCase(); 661 var parameters = ''; 662 var reset = false; 663 664 if (form.data('reset') == true) { 665 reset = true; 666 } 667 668 if (method == 'get') { 669 method = 'GET'; 670 parameters = _toConsumableArray(data.entries()).map(function (e) { 671 return encodeURIComponent(e[0]) + "=" + encodeURIComponent(e[1]); 672 }); 673 parameters.push('ajax=true'); 674 parameters = parameters.join('&'); 675 676 if (parameters.length) { 677 action += '?' + parameters; 678 } 679 } else { 680 method = 'POST'; 681 } 682 683 var success_callback = form.data('success-callback'); 684 var error_callback = form.data('error-callback'); 685 var redirect_to = form.data('data-redirect'); 686 var recaptcha_id = form.data('recaptcha-id'); 687 var notyf_position = form.data('toast-position'); 688 var recaptcha_el = $('#' + recaptcha_id); // Recaptcha validation 689 690 if (recaptcha_el.length && recaptchaLoaded) { 691 var recaptcha_widget_id = recaptchaWidgets[recaptcha_id]; 692 693 if (grecaptcha.getResponse(recaptcha_widget_id) == '') { 694 recaptcha_el.addClass('recaptcha-error'); 695 $theme.scrollToID(recaptcha_id); 696 notyf.error(locale["invalid-captcha"]); 697 return; 698 } 699 } 700 701 var submit = form.find(':submit'); 702 $theme.showInfiniteLoader(true); 703 submit.attr('disabled', true); 704 submit.addClass('btn-loading'); 705 $.ajax({ 706 url: action, 707 type: method, 708 processData: false, 709 contentType: false, 710 data: data, 711 async: true 712 }).done(function (response) { 713 var finished = $theme.callFunction(success_callback, [response, form]); // if explicitly returned TRUE we assume callback wants to handle the response completely on it's own 714 715 if (finished === true) { 716 return; 717 } 718 719 if (response.type) { 720 var type = response.type; 721 var duration = notyfOpts.duration; // Increase the time for longer texts 722 723 if (response.message.length > 100) { 724 duration = 4000; 725 } 726 727 var toast = { 728 type: type, 729 message: response.message, 730 duration: duration 731 }; 732 733 if ($theme.isObject(notyf_position)) { 734 toast.position = notyf_position; 735 } 736 notyf.open(toast); 737 } 738 739 if (response.redirect) { 740 return $theme.httpRedirect(response.redirect); 741 } 742 743 if (response.dash) { 744 $theme.ajaxContentLoad(response.dash, 'GET', {}); 745 return; 746 } 747 748 if (response.title) { 749 document.title = response.title; 750 } 751 752 if (response.body_class) { 753 $('body').attr('class', response.body_class); 754 } 755 756 if (recaptchaLoaded && recaptcha_el.length) { 757 grecaptcha.reset(); 758 } 759 760 if (form.parsley && response.type && response.type == 'success') { 761 form.find('.input-has-success, .input-has-error').each(function (index, el) { 762 $(this).removeClass('input-has-success').removeClass('input-has-error'); 763 }); 764 form.parsley().reset(); 765 } 766 767 if (reset && response.type && response.type == 'success') { 768 form.trigger('reset'); 769 form.find('.custom-file-label').text(window.locale["choose-file"]); 770 } 771 772 if (response.url && response.form_change_url) { 773 window.history.pushState({ 774 path: response.url 775 }, '', response.url); 776 } // content that needs to be replaced 777 778 779 if (response.sections) { 780 for (var element in response.sections) { 781 $('#' + element).html(response.sections[element]); 782 } 783 } // content that needs to be appended 784 785 786 if (response.append) { 787 for (var element in response.append) { 788 $('#' + element).append(response.append[element]); 789 } 790 } // content that needs to be prepended 791 792 793 if (response.prepend) { 794 for (var element in response.prepend) { 795 $('#' + element).append(response.prepend[element]); 796 } 797 } 798 799 if (response.attr) { 800 for (var selector in response.attr) { 801 var element = response.attr[selector]; 802 803 for (var attr in element) { 804 var value = element[attr]; 805 $(selector).each(function () { 806 $(this).attr(attr, value); 807 }); 808 } 809 } 810 } 811 812 $theme.refreshMarkup(); 813 }).fail(function (response) { 814 var finished = $theme.callFunction(error_callback, [response, form]); // if explicitly returned TRUE we assume callback wants to handle the response completely on it's own 815 816 if (finished === true) { 817 return; 818 } 819 820 notyf.error(response.status + ' ' + response.statusText); 821 }).always(function (response) { 822 $theme.hideInfiniteLoader(); 823 submit.removeClass('btn-loading'); 824 submit.attr('disabled', false); 825 }); 826}; 827/** 828 * Format AJAX response for form submission 829 * 830 * @param {Mixed} response 831 * @return {String} 832 */ 833 834 835$theme.formatAjaxResponse = function (response) { 836 if ($theme.isString(response)) { 837 return response; 838 } 839 840 if (!response.message) { 841 return false;
842 } 843 844 var dismissable = false; 845 846 if (response.dismissable) { 847 dismissable = true; 848 } 849 850 return $theme.buildAlert(response.message, response.type, dismissable, window.app.alertIcons); 851}; 852 853$theme.escapeHTML = function (text) { 854 var map = { 855 '&': '&', 856 '<': '<', 857 '>': '>', 858 '"': '"', 859 "'": ''' 860 }; 861 return text.replace(/[&<>"']/g, function (m) { 862 return map[m]; 863 }); 864}; 865 866var eye_svg_icon = $theme.svgIcon('eye'); 867var eye_svg_icon_off = $theme.svgIcon('eye-off'); 868/** 869 * Recaptcha callback 870 * 871 */ 872 873function refreshRecaptcha() { 874 $theme.refreshRecaptcha(); 875} // Load parsley because it doesn't work inside document ready 876 877 878$theme.loadParsley(); 879$(document).ready(function () { 880 // Instant Fetch 881 if (window.app.enableAjaxNavigation) { 882 $(document).on('click', "a.sp-link", function (e) { 883 var link = $(this); // Ignore links that wants to open in a new window 884 885 if (link.attr('target') == '_blank') { 886 return; 887 } 888 889 var target = link.attr('href'); // No need for identifers or javascript urls 890 891 if (target.startsWith('#') == true || target.startsWith('javascript:')) { 892 return; 893 } 894 895 var abs_target = link.prop('href'); 896 var target_host = new URL(abs_target).hostname; 897 898 if (target_host != window.location.hostname) { 899 return; 900 } 901 902 var method = link.data('method') || 'get'; 903 904 if (method.toLowerCase() == 'post') { 905 method = 'post'; 906 } 907 908 var parameters = {}; 909 var params = link.data('params'); 910 911 if ($theme.isObject(params)) { 912 parameters = params; 913 } 914 915 e.preventDefault(); 916 var scrolltop = true; 917 918 if (link.data('scrolltop') === false) { 919 scrolltop = false; 920 } 921 922 var options = { 923 pre_callback: link.data('pre-callback'), 924 success_callback: link.data('success-callback'), 925 error_callback: link.data('error-callback'), 926 post_callback: link.data('post-callback'), 927 scrolltop: scrolltop, 928 el: link 929 }; 930 $('body').addClass('ajax-started'); 931 $theme.ajaxContentLoad(abs_target, method, parameters, options); 932 }); 933 } // Dynamic components for custom content replacement 934 935 936 $(document).on('click', "[data-instant-ajax]", function (e) { 937 e.preventDefault(); 938 var link = $(this); 939 var target = link.attr('data-instant-ajax'); 940 var abs_target = target; 941 var method = link.data('method') || 'get'; 942 943 if (method.toLowerCase() == 'post') { 944 method = 'post'; 945 } 946 947 var parameters = {}; 948 var params = link.data('params'); 949 950 if ($theme.isObject(params)) { 951 parameters = params; 952 } 953 954 var options = { 955 pre_callback: link.data('pre-callback'), 956 success_callback: link.data('success-callback'), 957 error_callback: link.data('error-callback'), 958 post_callback: link.data('post-callback'), 959 scrolltop: false, 960 instantFetch: true, 961 el: link 962 }; 963 $theme.ajaxContentLoad(abs_target, method, parameters, options); 964 }); // Handle back button press 965 966 if (window.app.enableAjaxNavigation) { 967 $(window).on('popstate', function (e) { 968 if (location.href.includes('#')) { 969 return false; 970 } // In case we were on a page where the content doesn't exist 971 972 973 if ($('#content').length) { 974 $theme.ajaxContentLoad(location.href, 'GET', {}); 975 } else { 976 $theme.httpRedirect(location.href); 977 } 978 }); 979 } 980 981 if (window.app.enableAjaxNavigation) { 982 $(document).on('submit', 'form[data-ajax-form]', function (e) { 983 var form = $(this); 984 var before_callback = form.data('before-callback'); 985 var finished = $theme.callFunction(before_callback, [e]); 986 987 if (finished === true) { 988 return; 989 } 990 991 e.preventDefault(); 992 $theme.ajaxFormHandler(form); 993 }); 994 } // Custom file inputs 995 996 997 $(document).on('change', '.custom-file-input', function (e) { 998 var $el = $(this), 999 files = $el[0].files; 1000 var label_el = $el.next('.custom-file-label'); 1001 1002 if (files.length < 1) { 1003 label_el.html(window.locale["choose-file"]); 1004 return; 1005 } 1006 1007 var label = files[0].name; 1008 1009 if (files.length > 1) { 1010 label = label + " and " + String(files.length - 1) + " more files"; 1011 } 1012 1013 label_el.html(label); 1014 }); // Simple attribute based toggling 1015 1016 $(document).on('click', '[data-toggle="class"]', function (event) { 1017 event.preventDefault(); 1018 var target = $(this).data('target'); 1019 var targetEl; 1020 1021 if (!target) { 1022 targetEl = $(this); 1023 } else if (target == 'parent') { 1024 targetEl = $(this).parent(); 1025 } else { 1026 targetEl = $(target); 1027 } 1028 1029 var classes = $(this).data('classes');
1030 targetEl.toggleClass(classes); 1031 return false; 1032 }); 1033 $(document).on('click', '[data-focus]', function (event) { 1034 event.preventDefault(); 1035 var target = $($(this).data('focus')); 1036 target.focus(); 1037 }); 1038}); // Executed on DOM load and also when the dom is loaded via ajax 1039 1040 1041let searchSuggetions = []; 1042 1043function dynamicThemeEvents() { 1044 // Start floating labels 1045 $('.floating-label .custom-select, .floating-label .form-control').floatinglabel(); 1046 var body = $('body'); 1047 1048 if (window.app.settings.search_autocomplete) { 1049 $('[data-autocomplete]').autocomplete({ 1050 autosuggest: false, 1051 appendMethod: 'replace', 1052 openOnFocus: true, 1053 closeOnBlur: true, 1054 minLength: 3, 1055 accents: false, 1056 valid: function valid(value, query) { 1057 return true; 1058 }, 1059 source: [function (q, add) { 1060 if (q.toString().length < 2) { 1061 return; 1062 } 1063 1064 $.ajax({ 1065 url: 'https://suggestqueries.google.com/complete/search?json&client=firefox&hl=en&lr=en&ds=n&num=20&q=' + encodeURIComponent(q), 1066 jsonp: "callback", 1067 dataType: "jsonp", 1068 success: function success(response) { 1069 var suggestions = response[1]; // Remove the query itself 1070 1071 var index = suggestions.indexOf(q); 1072 1073 if (index !== -1) { 1074 suggestions.splice(index, 1); 1075 } 1076 searchSuggetions = suggestions; 1077 add(suggestions); 1078 } 1079 }); 1080 }] 1081 }).on("open.xdsoft", function () { 1082 $(this).closest('.searchbox-group').addClass('suggestions-active'); 1083 }).on("close.xdsoft", function () { 1084 $(this).closest('.searchbox-group').removeClass('suggestions-active'); 1085 }).on('selected.xdsoft', function (e, data) { 1086 $("#searchForm").submit(); 1087 }); 1088 $(['data-autocomplete']).autocomplete('update'); 1089 } 1090} 1091 1092function getParameterByName(name, url) { 1093 name = name.replace(/[\[\]]/g, '\\$&'); 1094 var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), 1095 results = regex.exec(url); 1096 if (!results) return null; 1097 if (!results[2]) return ''; 1098 var val = decodeURIComponent(results[2].replace(/\+/g, ' ')); 1099 return htmlEntities(val); 1100} 1101 1102function htmlEntities(str) { 1103 return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); 1104} 1105 1106function preventEmptySubmit(e) { 1107 if (!$('input[name=q]').val().length) { 1108 e.preventDefault(); 1109 return true; 1110 } 1111 1112 return false; 1113} 1114 1115var accordsMarkup = '<div class="card my-4"><div class="card-body py-2"><div class="list-group bg-transparent list-group-flush" id="answers-accordion"><h3 class="card-title m-0">People also search for:</h3></div></div>'; 1116var accords = ''; 1117var hasAccord = false; 1118jQuery(document).ready(function ($) { 1119 $('body').tooltip({ 1120 selector: '[data-toggle="tooltip"], [data-tooltip]', 1121 boundary: 'window' 1122 }); 1123 $(document).on('click', '.gs-spelling a', function (e) { 1124 var query = $(this).text(); 1125 $('input[data-search-input]').val(query); 1126 }); 1127 $(document).on('focus', '#home-search-input', function (e) { 1128 $('#dimmer').addClass('dimmer-active'); 1129 }); 1130 $(document).on('click', function (event) { 1131 var $trigger = $("#home-search-group"); 1132 1133 if ($trigger !== event.target && !$trigger.has(event.target).length) { 1134 $('#dimmer').removeClass('dimmer-active'); 1135 } 1136 }); 1137 $(document).on('click', '.gs-previewLink', function (e) { 1138 var src = $(this).find('img').attr('src'); 1139 1140 if (src) { 1141 e.preventDefault(); 1142 window.open(src, '_blank'); 1143 } 1144 }); 1145 1146 if (!document.getElementById('myallsearch-search-nav-theme')) { 1147 $('<style id="myallsearch-search-nav-theme">' + 1148 '.gsc-webResult.gsc-result:has(.web-result) { margin-bottom:0 !important; padding-bottom:0 !important; }' + 1149 '.gsc-webResult.gsc-result:has(.web-result) .gs-webResult.gs-result { margin-bottom:0 !important; padding-bottom:0 !important; }' + 1150 '.find-elsewhere-button:hover, .myallsearch-page-nav:hover {' + 1151 'background:#f1f1f1 !important;' + 1152 'border-color:#dedede !important;' + 1153 'color:#333 !important;' + 1154 '}' + 1155 'body.dark-mode .find-elsewhere-button, body.dark-mode .myallsearch-page-nav,' + 1156 'html.dark-mode .find-elsewhere-button, html.dark-mode .myallsearch-page-nav,' + 1157 'body.theme-dark .find-elsewhere-button, body.theme-dark .myallsearch-page-nav {' + 1158 'background:#2b2b2b !important;' + 1159 'color:#d7d7d7 !important;' + 1160 'border-color:#505050 !important;' + 1161 '}' + 1162 '.find-elsewhere-button { position:relative; }' + 1163 '.find-elsewhere-privacy-warning {' + 1164 'display:none;position:absolute;
1164left:50%;bottom:calc(100% + 10px);transform:translateX(-50%);' + 1165 'width:230px;padding:10px 12px;border:1px solid #e1e4e8;border-radius:8px;' + 1166 'background:#fff;color:#4f5358;box-shadow:0 4px 14px rgba(0,0,0,.12);' + 1167 'font-size:12px;font-weight:400;line-height:1.4;text-align:left;white-space:normal;z-index:9999;' + 1168 '}' + 1169 '.find-elsewhere-button:hover .find-elsewhere-privacy-warning, .find-elsewhere-button:focus .find-elsewhere-privacy-warning {display:block;}' + 1170 'body.dark-mode .find-elsewhere-privacy-warning, html.dark-mode .find-elsewhere-privacy-warning, body.theme-dark .find-elsewhere-privacy-warning {' + 1171 'background:#2b2b2b;color:#d7d7d7;border-color:#505050;box-shadow:0 4px 14px rgba(0,0,0,.35);' + 1172 '}' + 1173 'body.dark-mode .find-elsewhere-button:hover, body.dark-mode .myallsearch-page-nav:hover,' + 1174 'html.dark-mode .find-elsewhere-button:hover, html.dark-mode .myallsearch-page-nav:hover,' + 1175 'body.theme-dark .find-elsewhere-button:hover, body.theme-dark .myallsearch-page-nav:hover {' + 1176 'background:#353535 !important;' + 1177 'border-color:#666 !important;' + 1178 '}' + 1179 '</style>').appendTo('head'); 1180 } 1181 1182 // Allow long DuckDuckGo summary/fact values to wrap instead of being clipped. 1183 if (!document.getElementById('myallsearch-instant-answer-wrap-fix')) { 1184 $('<style id="myallsearch-instant-answer-wrap-fix">' + 1185 '#instant-answer, #instant-answer * {' + 1186 'overflow-wrap:anywhere;' + 1187 'word-break:normal;' + 1188 '}' + 1189 '#instant-answer .text-truncate {' + 1190 'white-space:normal !important;' + 1191 'overflow:visible !important;' + 1192 'text-overflow:clip !important;' + 1193 '}' + 1194 '</style>').appendTo('head'); 1195 } 1196 1197 var updateImageSidebarHeading = function updateImageSidebarHeading() { 1198 var query = $('input[name="q"]').first().val() || getParameterByName('q', window.location.href) || ''; 1199 query = $('<textarea/>').html(query).text().trim(); 1200 1201 if (!query) return; 1202 1203 var headingText = query + ' Images'; 1204 var $imageArea = $('#search-images-cse').closest('.card'); 1205 1206 if (!$imageArea.length) { 1207 $imageArea = $('#search-images-cse').parent(); 1208 } 1209 1210 $imageArea.find('.card-title, h1, h2, h3, h4, h5, h6').filter(function () { 1211 return $.trim($(this).text()) === 'Images' || $(this).attr('data-myallsearch-images-heading') === 'true'; 1212 }).first().attr('data-myallsearch-images-heading', 'true').text(headingText); 1213 }; 1214 1215 var addFindElsewhereToGoogleResults = function addFindElsewhereToGoogleResults() { 1216 $('.find-elsewhere').remove(); 1217 1218 var $googleResults = $('.gsc-webResult.gsc-result').filter(function () { 1219 return $(this).find('.web-result').length > 0; 1220 }); 1221 1222 if (!$googleResults.length) { 1223 return; 1224 } 1225 1226 var query = $('input[name="q"]').first().val() || getParameterByName('q', window.location.href) || ''; 1227 query = $('<textarea/>').html(query).text(); 1228 1229 if (!query) { 1230 return; 1231 } 1232 1233 var encodedQuery = encodeURIComponent(query); 1234 var searchIcon = '<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" style="display:block;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;"><circle cx="11" cy="11" r="7"></circle><path d="M20 20l-4-4"></path></svg>'; 1235 1236 var html = '<div class="find-elsewhere" style="display:flex;align-items:center;flex-wrap:wrap;column-gap:10px;row-gap:10px;margin:24px 0 14px;font-family:inherit;">' + 1237 '<div style="display:inline-flex;align-items:center;gap:10px;margin-right:8px;color:#4f5358;font-size:0.9rem;font-weight:500;line-height:1.5;white-space:nowrap;">' + 1238 searchIcon + '<span>Find elsewhere</span>' + 1239 '</div>' + 1240 '<a class="find-elsewhere-button" href="https://www.google.com/search?q=' + encodedQuery + '" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;justify-content:center;height:44px;min-width:60px;padding:0 12px;border:1px solid #e1e4e8;border-radius:999px;background:#fff;color:#4f5358;text-decoration:none;font-size:0.9rem;font-weight:400;">Google<span class="find-elsewhere-privacy-warning"><strong>Privacy notice:</strong> Searches made using this link are handled by Google and may be tracked according to their privacy policy.</span></a>' + 1241 '<a class="find-elsewhere-button" href="https://www.bing.com/search?q=' + encodedQuery + '" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;justify-content:center;height:44px;min-width:60px;padding:0 12px;border:1px solid #e1e4e8;border-radius:999px;background:#fff;color:#4f5358;text-decoration:none;font-size:0.9rem;font-weight:400;">Bing<span class="find-elsewhere-privacy-warning"><strong>Privacy notice:</strong> Searches made using this link are handled by Bing and may be tracked according to their privacy policy.</span></a>' + 1242 '<a class="find-elsewhere-button" href="https://www.mojeek.com/search?q=' + encodedQuery + '" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;justify-content:center;height:44px;min-width:60px;padding:0 12px;border:1px solid #e1e4e8;border-radius:999px;background:#fff;color:#4f5358;text-decoration:none;font-size:0.9rem;font-weight:400;">Mojeek<span class="find-elsewhere-privacy-warning"><strong>Privacy notice:</strong> Searches made using this link are handled by Mojeek and may be subject to their privacy policy.</span></a>' + 1243 '</div>'; 1244 1245 $googleResults.last().after(html); 1246 }; 1247 1248 var setupNextSearchPageButton = function setupNextSearchPageButton() { 1249 var $cursor = $('.gsc-cursor').first(); 1250 if (!$cursor.length) return; 1251 1252 var $current = $cursor.find('.gsc-cursor-current-page').first(); 1253 var $prev = $current.prev('.gsc-cursor-page'); 1254 var $next = $current.next('.gsc-cursor-page'); 1255 1256 $cursor.find('.myallsearch-page-nav, .myallsearch-page-number').remove(); 1257 1258 // Keep Google's real page controls in the DOM, but hide the numbers. 1259 $cursor.find('.gsc-cursor-page').each(function () { 1260 this.style.setProperty('display', 'none', 'important'); 1261 }); 1262 1263 var makeNavButton = function (label, target) { 1264 var $button = $('<button type="button" class="myallsearch-page-nav">' + label + '</button>'); 1265 1266 $button.css({ 1267 display: 'inline-flex', 1268 alignItems: 'center', 1269 justifyContent: 'center', 1270 gap: '8px', 1271 minWidth: '112px', 1272 height: '44px', 1273 padding: '0 24px', 1274 border: '1px solid rgba(0,0,0,.16)', 1275 borderRadius: '999px', 1276 background: '#fff', 1277 color: '#333', 1278 fontFamily: 'inherit', 1279 fontSize: '15px', 1280 fontWeight: '500', 1281 cursor: 'pointer' 1282 }); 1283 1284 $button.on('click', function () { 1285 target[0].click(); 1286 }); 1287 1288 return $button; 1289 }; 1290 1291 $cursor.css({ 1292 display: 'flex', 1293 alignItems: 'center', 1294 justifyContent: 'center', 1295 gap: '12px', 1296 textAlign: 'center', 1297 padding: '8px 0 14px' 1298 }); 1299 1300 if ($prev.length) { 1301 $cursor.append(makeNavButton('â Prev', $prev)); 1302 1303 var currentPage = $.trim($current.text()); 1304 if (currentPage) { 1305 var $pageNumber = $('<span class="myallsearch-page-number">Page ' + currentPage + '</span>'); 1306 $pageNumber.css({ 1307 display: 'inline-flex', 1308 alignItems: 'center', 1309 justifyContent: 'center', 1310 fontFamily: 'inherit', 1311 fontSize: '15px', 1312 fontWeight: '500', 1313 color: 'inherit', 1314 whiteSpace: 'nowrap' 1315 }); 1316 $cursor.append($pageNumber); 1317 } 1318 } 1319 1320 if ($next.length) { 1321 $cursor.append(makeNavButton('Next â', $next)); 1322 } 1323 }; 1324 1325 var cleanGoogleSearchHash = function cleanGoogleSearchHash() { 1326 if (window.location.hash.indexOf('#gsc.') === 0) { 1327 window.history.replaceState( 1328 window.history.state, 1329 document.title, 1330 window.location.pathname + window.location.search 1331 ); 1332 } 1333 }; 1334 1335 var resultsRenderedCallback = function resultsRenderedCallback() { 1336 $('#search-loader').hide(); 1337 1338 window.setTimeout(updateImageSidebarHeading, 0); 1339 window.setTimeout(updateImageSidebarHeading, 100); 1340 window.setTimeout(updateImageSidebarHeading, 300); 1341 $('#instant-answer').show(); 1342 if (hasAccord) { 1343 $('#web-result-1').before(accords); 1344 } 1345 1346 // Google may finish attaching its final gsc-result wrappers just after this callback. 1347 window.setTimeout(addFindElsewhereToGoogleResults, 0); 1348 window.setTimeout(addFindElsewhereToGoogleResults, 100); 1349 window.setTimeout(addFindElsewhereToGoogleResults, 300); 1350 1351 window.setTimeout(setupNextSearchPageButton, 0); 1352 window.setTimeout(setupNextSearchPageButton, 100); 1353 window.setTimeout(setupNextSearchPageButton, 300); 1354 window.setTimeout(setupNextSearchPageButton, 700); 1355 1356 // Google CSE adds #gsc.tab / #gsc.q / #gsc.page to the address. 1357 // Remove that fragment after CSE has finished using it. 1358 window.setTimeout(cleanGoogleSearchHash, 100); 1359 window.setTimeout(cleanGoogleSearchHash, 400); 1360 window.setTimeout(cleanGoogleSearchHash, 800); 1361 }; 1362 1363 var resultsReadyCallback = function resultsReadyCallback(name, q, promos, results, resultsDiv) { 1364 var items = ''; 1365 1366 var makePromoParts = function makePromoParts(promo, id) { 1367 var thumb = ''; 1368 1369 if (promo.image) { 1370 thumb = "<img src=\"".concat(promo.image.url, "\" class=\"web-thumb-img\" style=\"height:").concat(promo.image.height, "px;width:").concat(promo.image.width, "px\">"); 1371 } 1372 1373 var target = ''; 1374 1375 if (window.app.settings.new_window) { 1376 target = '_blank'; 1377 } 1378 1379 var promoURL = getParameterByName('q', promo['url']); 1380 var promoDomain = promoURL.replace(/(^\w+:|^)\/\//, '').replace(/\/$/, ""); 1381 var anchor = "<div class=\"web-result promo-result\" id=\"promo-".concat(id, "\">\n <a class=\"web-result-title\" href=\"").concat(promoURL, "\" target=\"").concat(target, "\" rel=\"nofollow nopener noreferer\"><h3 class=\"web-result-title-heading\">\n ").concat(promo['title'], "</h3></a>\n <div class=\"web-result-domain\"><img src=\"https://www.google.com/s2/favicons?domain=").concat(promo['visibleUrl'], "\" class=\"web-result-favicon\">").concat(promoDomain, "</div> ").concat(thumb, "\n <p class=\"web-result-desc\">").concat(promo['content'], "</p>\n\n </div>"); 1382 return anchor; 1383 }; 1384 1385 if (_typeof(promos) === 'object') { 1386 items += '<section class="promoted-results">'; 1387 1388 for (var i in promos) { 1389 items += makePromoParts(promos[i], i); 1390 } 1391 1392 items += '</section>'; 1393 } 1394 1395 1396var makeResultParts = function makeResultParts(result, id) { 1397 var thumb = ''; 1398 1399 if (result.thumbnailImage && engine.showThumbnails) { 1400 thumb = "<img src=\"".concat(result.thumbnailImage.url, "\" class=\"web-thumb-img\">"); 1401 } 1402 1403 1404 1405 var target = ''; 1406 1407 if (window.app.settings.new_window) { 1408 target = '_blank'; 1409 } 1410 1411 q = htmlEntities(q); 1412 var resURL = getParameterByName('q', result['url']); 1413 var resDomain = resURL.replace(/(^\w+:|^)\/\//, '').replace(/\/$/, ""); 1414 var anchor = "<div class=\"web-result\" id=\"web-result-".concat(id, "\">\n <a class=\"web-result-title\" href=\"").concat(resURL, "\" target=\"").concat(target, "\" rel=\"nofollow nopener noreferer\"><h3 class=\"web-result-title-heading\">\n ").concat(result['title'], "</h3></a>\n <div class=\"web-result-domain\"><img src=\"https://www.google.com/s2/favicons?domain=").concat(result['visibleUrl'], "\" class=\"web-result-favicon\">").concat(resDomain, "</div> ").concat(thumb, "\n <p class=\"web-result-desc\">").concat(result['content'], "</p>\n\n </div>"); 1415 return anchor; 1416 }; 1417 1418 var extQuestions = []; 1419 if (results.length > 1) { 1420 accords = accordsMarkup; 1421 var i = 0; 1422 var answerDone = false;
1423 var i2 = 0; 1424 var _iterator = _createForOfIteratorHelper(results), 1425 _step; 1426 try { 1427 for (_iterator.s(); !(_step = _iterator.n()).done;) { 1428 var result = _step.value; 1429 if (result.richSnippet) { 1430 if (result.richSnippet.answer && result.richSnippet.answer[0] && result.richSnippet.question[0]) { 1431 if (i == 7) { 1432 break; 1433 } 1434 1435 i++; 1436 var expanded = 'false'; 1437 var collapsed = 'collapsed'; 1438 var collapse = 'collapse'; 1439 var show = ''; 1440 1441 if (i == 1) { 1442 expanded = 'true'; 1443 show = 'show'; 1444 collapse = 'collapse show'; 1445 } 1446 1447 1448 1449 if(extQuestions.indexOf(result.richSnippet.question[0].name) == -1){ 1450 accords += "\n <div class=\"expansion-panel list-group-item bg-transparent ".concat(show, "\">\n <a aria-controls=\"collapse-").concat(i, "\" aria-expanded=\"").concat(expanded, "\" class=\"font-weight-bold expansion-panel-toggler ").concat(collapsed, "\" data-toggle=\"collapse\" href=\"#collapse-").concat(i, "\" id=\"collapse-heading-").concat(i, "\">\n ").concat(result.richSnippet.question[0].name, "\n <div class=\"expansion-panel-icon ml-3 text-black-secondary\">\n <i class=\"collapsed-show\">").concat($theme.svgIcon('keyboard-arrow-down'), "</i>\n <i class=\"collapsed-hide\">").concat($theme.svgIcon('keyboard-arrow-up'), "</i>\n </div>\n </a>\n <div aria-labelledby=\"collapse-heading-").concat(i, "\" class=\"").concat(collapse, "\" data-parent=\"#answers-accordion\" id=\"collapse-").concat(i, "\">\n <div class=\"expansion-panel-body\"><p class=\"card-text\">").concat(result.richSnippet.answer[0].text, "</p>\n </div>\n </div>\n </div>"); 1451 } 1452 extQuestions.push(result.richSnippet.question[0].name); 1453 } 1454 } 1455 i2++; 1456 items += makeResultParts(result, i2); 1457 } 1458 } catch (err) { 1459 _iterator.e(err); 1460 } finally { 1461 _iterator.f(); 1462 } 1463 1464 accords += '</div></div>'; 1465 if(searchSuggetions?.length > 0){ 1466 let sidebarSuggestions = searchSuggetions.slice(0, 4); 1467 let suggestionHtml = ''; 1468 1469 sidebarSuggestions.forEach(function(suggestion) { 1470 let suggestionUrl = '/search?q=' + encodeURIComponent(suggestion) + '&engine=1'; 1471 suggestionHtml += `<a href="${suggestionUrl}" class="related-search-link"><svg class="svg-icon svg-sm"><use xlink:href="#search"></use></svg>${suggestion}</a>`; 1472 }); 1473 1474 $('#related-searches-sidebar').html(suggestionHtml); 1475 $('#related-searches-card').show(); 1476 } else { 1477 $('#related-searches-sidebar').empty(); 1478 $('#related-searches-card').hide(); 1479 } 1480 1481 1482 $(resultsDiv).html(items); 1483 1484 if (i > 3) { 1485 hasAccord = true; 1486 } else { 1487 hasAccord = false; 1488 } 1489 } else { 1490 q = htmlEntities(q); 1491 var plainQuery = $('<textarea/>').html(q).text(); 1492 var encodedQuery = encodeURIComponent(plainQuery); 1493 var nothing = '<div class="no-results" style="padding:18px 0 10px;">' + 1494 '<p style="font-size:1.1rem;margin-bottom:6px;"><strong>No results found for “' + q + '”</strong></p>' + 1495 '<p style="margin-bottom:16px;">Try checking the spelling, using fewer words, or searching for something more general.</p>' + 1496 '<p style="margin-bottom:10px;font-weight:500;">Try this search elsewhere:</p>' + 1497 '<div style="display:flex;flex-wrap:wrap;gap:10px;">' + 1498 '<a href="https://www.google.com/search?q=' + encodedQuery + '" target="_blank" rel="noopener noreferrer" style="display:inline-block;padding:9px 15px;border:1px solid #ddd;border-radius:999px;text-decoration:none;color:inherit;">Google</a>' + 1499 '<a href="https://www.bing.com/search?q=' + encodedQuery + '" target="_blank" rel="noopener noreferrer" style="display:inline-block;padding:9px 15px;border:1px solid #ddd;border-radius:999px;text-decoration:none;color:inherit;">Bing</a>' + 1500 '<a href="https://www.mojeek.com/search?q=' + encodedQuery + '" target="_blank" rel="noopener noreferrer" style="display:inline-block;padding:9px 15px;border:1px solid #ddd;border-radius:999px;text-decoration:none;color:inherit;">Mojeek</a>' + 1501 '</div>' + 1502 '</div>'; 1503 $(resultsDiv).html(nothing); 1504 1505 // No web results: hide the image sidebar card for this empty search. 1506 $('.search-images-card').hide(); 1507 } 1508 1509 return true; 1510 }; 1511 1512 window.__gcse || (window.__gcse = {}); 1513 window.__gcse.searchCallbacks = { 1514 web: { 1515 ready: resultsReadyCallback, 1516 rendered: resultsRenderedCallback 1517 }, 1518 image: { 1519 rendered: resultsRenderedCallback 1520 } 1521 }; 1522 dynamicThemeEvents(); 1523 $(window).on('scroll load', function (event) { 1524 var scroll = $(window).scrollTop(); 1525 if (scroll > 56) { 1526 $('body').addClass('navbar-active'); 1527 $('#site-navbar').fadeIn(500).addClass("navbar-scrolled"); 1528 } else { 1529 $('#site-navbar').fadeIn(500).removeClass("navbar-scrolled"); 1530 $('body').removeClass('navbar-active'); 1531 } 1532 }); 1533 $(document).on('click', '[data-engine-id]', function (event) { 1534 event.preventDefault(); 1535 var el = $(this); 1536 var id = el.data('engine-id'); 1537 var target = el.data('target'); 1538 var textTarget = el.data('text-target'); 1539 1540 if (target) { 1541 $(target).val(id); 1542 } 1543 1544 if (textTarget) { 1545 $(textTarget).text(el.text()); 1546 } 1547 1548 $('[data-engine-active="true"]').removeClass('active').attr('data-engine-active', false); 1549 $('[data-engine-id="' + id + '"]').attr('data-engine-active', true).addClass('active'); 1550 }); // Enable Popovers 1551 1552 $('body').popover({ 1553 selector: '[data-toggle="popover"]', 1554 html: true 1555 }); 1556 $(document).on('change', '[data-file-preview]', function (event) { 1557 var input = this; 1558 var target = $($(this).data('file-preview')); 1559 1560 if (input.files && input.files[0]) { 1561 var reader = new FileReader(); 1562 1563 reader.onload = function (e) { 1564 target.css('background-image', 'url(' + e.target.result + ')'); 1565 }; 1566
1567 reader.readAsDataURL(input.files[0]); 1568 } 1569 }); 1570});
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.