1((document, Joomla) => { 2 "use strict"; 3 4 class MinitekLiveSearch { 5 constructor(params, formId, searchUrl, autosuggestUrl) { 6 const self = this; 7 const options = Joomla.getOptions("com_miniteklivesearch"); 8 this.site_path = options.site_path; 9 this.orOperator = options.orOperator; 10 this.andOperator = options.andOperator; 11 this.notOperator = options.notOperator; 12 13 this.params = params; 14 this.formId = parseInt(formId, 10); 15 this.searchUrl = 16 this.site_path + searchUrl + "&form=" + this.formId; 17 this.autosuggestUrl = autosuggestUrl; 18 19 this.timer; 20 this.mls_ajax_request; 21 this.suggest_ajax_request; 22 this.showAutosuggest = parseInt(this.params.show_autosuggest); 23 this.appendToUrl = parseInt(this.params.append_to_url); 24 this.highlight = parseInt(this.params.mls_highlight_terms); 25 this.sidebar = this.params.filters_position == "sidebar"; 26 this.openFilters = parseInt(this.params.open_filters); 27 this.form = document.querySelector("#mls-" + this.formId); 28 this.mls_clear_input = this.form.querySelector(".mls-clear-input"); 29 this.mls_query_link = this.form.querySelector(".mls-query-link"); 30 this.mls_input_tools = this.form.querySelector(".mls-input-tools"); 31 this.mls_suggest_panel = 32 this.form.querySelector(".mls-suggest-panel"); 33 this.mls_popular = this.form.querySelector(".mls-popular"); 34 this.mls_suggestions = this.form.querySelector(".mls-suggestions"); 35 this.keepFiltersOpen = this.sidebar && this.openFilters; 36 this.scrollToTop = this.params.mls_scroll_to_top ?? 1; 37 this.scrollToTop = parseInt(this.scrollToTop); 38 39 this.togglePanels(); 40 this.submitForm(); 41 42 // Show clear input button and input link on page load 43 if (this.form.querySelector(".mls-input").value) { 44 this.form.querySelector( 45 ".mls-input-bar .input-group-addon i.fa-search" 46 ).style.display = "none"; 47 this.mls_clear_input.style.display = "block"; 48 if (this.mls_query_link) 49 this.mls_query_link.style.display = "flex"; 50 } 51 52 this.mls_clear_input.addEventListener("click", function (e) { 53 e.preventDefault(); 54 self.clearInput(); 55 }); 56 57 this.bindSearchInput(); 58 this.initSuggestions(); 59 this.openSuggestions(); 60 this.saveSearch(); 61 this.initMapFilters(); 62 this.initDateFilters(); 63 this.dropdownLayout(); 64 this.clickEvents(); 65 } 66 67 togglePanels() { 68 const self = this; 69 const mls_toggle = self.form.querySelectorAll(".mls-toggle"); 70 71 if (mls_toggle) { 72 mls_toggle.forEach((item) => { 73 item.addEventListener("click", function (e) { 74 e.preventDefault(); 75 const panel = item.getAttribute("data-panel"); 76 self.mls_input_tools 77 .querySelector( 78 ".mls-toggle[data-panel='" + panel + "']" 79 ) 80 .classList.toggle("mls-btn-open"); 81 82 if ( 83 self.mls_input_tools 84 .querySelector( 85 ".mls-toggle[data-panel='" + panel + "']" 86 ) 87 .classList.contains("mls-btn-open") 88 ) 89 self.form.querySelector( 90 ".mls-" + panel + "-panel" 91 ).style.display = "block"; 92 else 93 self.form.querySelector( 94 ".mls-" + panel + "-panel" 95 ).style.display = "none"; 96 }); 97 }); 98 } 99 } 100
101 submitForm() { 102 const self = this; 103 const q = self.form.querySelector(".mls-input"); 104 var _value = q.value; 105 106 // Check if there is a pending ajax request 107 if (typeof self.mls_ajax_request !== "undefined") 108 self.mls_ajax_request.abort(); 109 110 if (_value.trim().length > 0) { 111 if (self.form.querySelector(".mls-save-search")) 112 self.form 113 .querySelector(".mls-save-search") 114 .removeAttribute("disabled"); 115 116 self.form.querySelector(".mls-loader").style.display = "flex"; 117 self.form.querySelector( 118 ".mls-input-bar .input-group-addon i.fa-search" 119 ).style.display = "none"; 120 self.mls_clear_input.style.display = "none"; 121 if (self.mls_query_link) 122 self.mls_query_link.style.display = "none"; 123 124 // Serialize form data 125 var data = new FormData(self.form); 126 var _data = ""; 127 var _dates = ""; 128 var d1_exists = false; 129 var d2_exists = false; 130 131 for (var p of data) { 132 if (p[0] == "mls-search-title") continue; 133 134 if ( 135 p[0] == "w1" || 136 p[0] == "d1" || 137 p[0] == "w2" || 138 p[0] == "d2" 139 ) 140 _dates += "&" + p[0] + "=" + p[1]; 141 else _data += "&" + p[0] + "=" + p[1]; 142 143 if (p[0] == "d1" && p[1]) d1_exists = true; 144 if (p[0] == "d2" && p[1]) d2_exists = true; 145 } 146 147 // Add dates if they exist 148 if (d1_exists || d2_exists) _data += _dates; 149 150 // Execute new ajax request 151 self.mls_ajax_request = Joomla.request({ 152 url: self.searchUrl, 153 method: "POST", 154 data: _data, 155 onSuccess: (response, xhr) => { 156 let q_now = self.form 157 .querySelector(".mls-input") 158 .value.trim(); 159 160 // Abort if input is empty by the time this request has finished 161 if (q_now.length == 0) { 162 self.clearInput(); 163 return; 164 } 165 166 let action = self.form.action; 167 168 self.form.querySelector(".mls-results").innerHTML = 169 response; 170 self.form.querySelector(".mls-loader").style.display = 171 "none"; 172 self.mls_clear_input.style.display = "block"; 173 if (self.mls_query_link) { 174 self.updateQueryLink(_data, action); 175 self.mls_query_link.style.display = "flex"; 176 } 177 178 if ( 179 self.form.querySelector( 180 ".mls-dropdown .mls-results" 181 ) 182 ) 183 self.form.querySelector( 184 ".mls-dropdown .mls-results" 185 ).style.display = "block"; 186 187 if (self.appendToUrl) self.appendUrl(_data, action); 188 189 // Highlight terms 190 if (self.highlight) { 191 let terms = _value.trim(); 192 new Mark( 193 self.form.querySelector(".js-highlight") 194 ).mark(terms, { 195 exclude: [ 196 ".bg-primary", 197 ".bg-secondary", 198 ".mls-tag", 199 ".result-url a", 200 ], 201 }); 202 } 203 }, 204 onError: (xhr) => { 205 if (xhr.status > 0) 206 Joomla.renderMessages( 207 Joomla.ajaxErrorsMessages(xhr) 208 ); 209 }, 210 }); 211 } 212 } 213 214 clearInput(focus = true) { 215 const self = this; 216 217 if (self.form.querySelector(".mls-save-search")) 218 self.form 219 .querySelector(".mls-save-search") 220 .setAttribute("disabled", ""); 221 222 self.form.querySelector( 223 ".mls-input-bar .input-group-addon i.fa-search" 224 ).style.display = "block"; 225 self.mls_clear_input.style.display = "none"; 226 if (self.mls_query_link) self.mls_query_link.style.display = "none"; 227 self.form.querySelector(".mls-loader").style.display = "none"; 228 self.form.querySelector(".mls-input").value = ""; 229 230 if (focus) self.form.querySelector(".mls-input").focus(); 231 232 if (self.mls_suggest_panel) { 233 if (self.mls_popular) self.mls_popular.classList.add("visible"); 234 235 if (self.mls_suggestions) { 236 self.mls_suggestions.classList.remove("visible"); 237 self.mls_suggestions.querySelector("ul").innerHTML = ""; 238 } 239 240 self.mls_suggest_panel 241 .querySelectorAll("ul li") 242 .forEach((a) => { 243 a.classList.remove("selected"); 244 }); 245 } 246 247 if (self.form.querySelector(".mls-empty-search")) 248 self.form.querySelector(".mls-empty-search").style.display = 249 "block"; 250 251 if (self.form.querySelector(".mls-results-inner")) 252 self.form.querySelector(".mls-results-inner").remove(); 253 254 self.clearUrl(); 255 } 256 257 // Update query link 258 updateQueryLink(data, action, start = 0) { 259 const self = this; 260 let url = self.getNewUrl(data, action, (start = 0)); 261 self.mls_query_link.href = url; 262 } 263 264 // Append search params to url 265 appendUrl(data, action, start = 0) { 266 const self = this; 267 let url = self.getNewUrl(data, action, (start = 0)); 268 window.history.pushState({}, "", url); 269 } 270 271 // Clear search params from url 272 clearUrl() { 273 let url = new URL(window.location) + "?"; 274 url = url.substring(0, url.indexOf("?")); 275 window.history.pushState({}, "", url); 276 } 277 278 // Construct new url 279 getNewUrl(data, action, start = 0) { 280 let url_data = data; 281 var ajax_regex = /([&]ajax=[0-9])/g; 282 url_data = url_data.replace(ajax_regex, ""); 283 var module_regex = /([&]moduleid=[0-9])/g; 284 url_data = url_data.replace(module_regex, ""); 285 url_data = url_data.substring(1); 286 if (start > 0) url_data += "&start=" + start; 287 288 return action + "?" + url_data; 289 } 290 291 bindSearchInput() { 292 const self = this; 293 294 self.form.addEventListener("input", function (e) { 295 const mls_input = self.form.querySelector(".mls-input"); 296 let trim_q = mls_input.value.trimStart(); 297 trim_q = trim_q.replace(/\s\s+/g, " "); 298 mls_input.value = trim_q; 299 300 // Close popular searches 301 if (self.mls_suggest_panel) { 302 if (trim_q) { 303 if (self.mls_popular) 304 self.mls_popular.classList.remove("visible"); 305 306 self.mls_suggest_panel 307 .querySelectorAll("ul li") 308 .forEach((a) => { 309 a.classList.remove("selected"); 310 }); 311 } 312 } 313 314 if (!trim_q) self.clearInput(false); 315 316 // Wait for 500ms before submitting form 317 window.clearTimeout(self.timer); 318 self.timer = window.setTimeout(function () {
319 self.submitForm(); 320 }, 500); 321 322 return false; 323 }); 324 } 325 326 // Open suggestions when clicking input 327 openSuggestions() { 328 const self = this; 329 330 ["click", "ontouchstart"].forEach((evt) => 331 self.form 332 .querySelector(".mls-input") 333 .addEventListener(evt, function (e) { 334 const mls_input = self.form.querySelector(".mls-input"); 335 let trim_q = mls_input.value.trimStart(); 336 trim_q = trim_q.replace(/\s\s+/g, " "); 337 338 if (self.mls_suggest_panel) { 339 if ( 340 (!trim_q && 341 self.mls_popular && 342 self.mls_popular.querySelectorAll("ul li") 343 .length > 0) || 344 (self.mls_suggestions && 345 self.mls_suggestions.querySelectorAll( 346 "ul li" 347 ).length > 0) 348 ) { 349 self.mls_suggest_panel.style.display = "block"; 350 } 351 352 if (!trim_q) { 353 if (self.mls_suggestions) 354 self.mls_suggestions.querySelector( 355 "ul" 356 ).innerHTML = ""; 357 358 if (self.mls_popular) 359 self.mls_popular.classList.add("visible"); 360 } else { 361 if ( 362 self.mls_suggestions.querySelectorAll( 363 "ul li" 364 ).length 365 ) 366 self.mls_suggestions.classList.add( 367 "visible" 368 ); 369 } 370 } 371 }) 372 ); 373 } 374 375 saveSearch() { 376 const self = this; 377 378 if (self.form.querySelector(".mls-search-title")) { 379 self.form 380 .querySelector(".mls-save-search") 381 .addEventListener("click", function (e) { 382 if (self.form.querySelectorAll(".mls-message")) 383 self.form 384 .querySelectorAll(".mls-message") 385 .forEach((a) => { 386 a.style.display = "none"; 387 }); 388 389 let title = 390 self.form.querySelector(".mls-search-title").value; 391 392 if (title.length == 0) { 393 self.form 394 .querySelector(".mls-search-title") 395 .classList.add("invalid"); 396 397 if (self.form.querySelector(".mls-title-empty")) 398 self.form.querySelector( 399 ".mls-title-empty" 400 ).style.display = "inline-block"; 401 else 402 Joomla.renderMessages({ 403 error: [ 404 Joomla.Text._( 405 "COM_MINITEKLIVESEARCH_MESSAGE_ENTER_SEARCH_TITLE" 406 ), 407 ], 408 }); 409 } else { 410 self.form 411 .querySelector(".mls-search-title") 412 .classList.remove("invalid"); 413 414 let form_id = self.form.getAttribute("data-form"); 415 let Itemid = self.form.getAttribute("data-itemid"); 416 417 let url = new URL(window.location); 418 let search_params = new URLSearchParams( 419 url.search 420 ).toString(); 421 422 let data = { 423 title: title, 424 form_id: parseInt(form_id, 10), 425 Itemid: parseInt(Itemid, 10), 426 search_params: search_params, 427 }; 428 429 // Show loader
430 self.form.querySelector( 431 ".mls-save-loader" 432 ).style.display = "inline-block"; 433 434 // Ajax request 435 Joomla.request({ 436 url: 437 self.site_path + 438 "index.php?option=com_miniteklivesearch&task=saveSearch", 439 method: "POST", 440 data: JSON.stringify(data), 441 onSuccess: (response, xhr) => { 442 response = JSON.parse(response); 443 444 // Error 445 if ( 446 response.status === -1 || 447 response.status === 0 448 ) { 449 if ( 450 self.form.querySelector( 451 ".mls-save-error" 452 ) 453 ) 454 self.form.querySelector( 455 ".mls-save-error" 456 ).style.display = "inline-block"; 457 else 458 Joomla.renderMessages({ 459 error: [response.message], 460 }); 461 } 462 // Success 463 else if (response.status === 1) { 464 if ( 465 self.form.querySelector( 466 ".mls-save-success" 467 ) 468 ) 469 self.form.querySelector( 470 ".mls-save-success" 471 ).style.display = "inline-block"; 472 else 473 Joomla.renderMessages({ 474 message: [response.message], 475 }); 476 } 477 478 // Hide loader 479 self.form.querySelector( 480 ".mls-save-loader" 481 ).style.display = "none"; 482 }, 483 onError: (xhr) => { 484 if (xhr.status > 0) 485 Joomla.renderMessages( 486 Joomla.ajaxErrorsMessages(xhr) 487 ); 488 }, 489 }); 490 } 491 }); 492 } 493 } 494 495 // Pagination 496 submitPage(link_href) { 497 const self = this; 498 499 if (typeof self.mls_ajax_request !== "undefined") 500 self.mls_ajax_request.abort(); 501 502 self.form.querySelector(".mls-loader").style.display = "flex"; 503 self.form.querySelector(".mls-page-loader-wrap").style.display = 504 "block"; 505 self.form.querySelector( 506 ".mls-input-bar .input-group-addon i.fa-search" 507 ).style.display = "none"; 508 self.mls_clear_input.style.display = "none"; 509 if (self.mls_query_link) self.mls_query_link.style.display = "none"; 510 511 // Serialize form data 512 var data = new FormData(self.form); 513 var _data = ""; 514 var _dates = ""; 515 var d1_exists = false; 516 var d2_exists = false; 517 518 for (var p of data) { 519 if (p[0] == "mls-search-title") continue; 520 521 if ( 522 p[0] == "w1" || 523 p[0] == "d1" || 524 p[0] == "w2" || 525 p[0] == "d2" 526 ) 527 _dates += "&" + p[0] + "=" + p[1]; 528 else _data += "&" + p[0] + "=" + p[1]; 529 530 if (p[0] == "d1" && p[1]) d1_exists = true; 531 if (p[0] == "d2" && p[1]) d2_exists = true; 532 } 533 534 // Add dates if they exist 535 if (d1_exists || d2_exists) _data += _dates; 536 537 // Get page 538 var start = parseInt(link_href.split("&start=").pop(), 10); 539 540 self.mls_ajax_request = Joomla.request({ 541 url: link_href + "&form=" + self.formId, 542 method: "POST", 543 data: _data, 544 onSuccess: (response, xhr) => { 545 self.form.querySelector(".mls-results").innerHTML = 546 response; 547 self.form.querySelector(".mls-loader").style.display = 548 "none"; 549 self.mls_clear_input.style.display = "block"; 550 551 let action = self.form.action; 552 553 if (self.mls_query_link) { 554 self.updateQueryLink(_data, action); 555 self.mls_query_link.style.display = "flex"; 556 } 557 558 if (this.scrollToTop) self.form.scrollIntoView(); 559 560 // Append search params to url 561 if (self.appendToUrl) self.appendUrl(_data, action, start); 562 563 // Highlight terms 564 if (self.highlight) { 565 const _value = 566 self.form.querySelector(".mls-input").value; 567 let terms = _value.trim();
568 new Mark(self.form.querySelector(".js-highlight")).mark( 569 terms, 570 { 571 exclude: [ 572 ".bg-primary", 573 ".bg-secondary", 574 ".mls-tag", 575 ".result-url a", 576 ], 577 } 578 ); 579 } 580 }, 581 onError: (xhr) => { 582 if (xhr.status > 0) 583 Joomla.renderMessages(Joomla.ajaxErrorsMessages(xhr)); 584 }, 585 }); 586 } 587 588 onInputChange(target, url) { 589 const self = this; 590 591 if (typeof self.suggest_ajax_request !== "undefined") 592 self.suggest_ajax_request.abort(); 593 594 const ul = self.mls_suggestions.querySelector("ul"); 595 596 if (target.value.length == 1) { 597 self.mls_suggest_panel.style.display = "none"; 598 } else if (target.value.length > 1) { 599 Joomla.request({ 600 url: "".concat(url, "&q=").concat(target.value), 601 method: "GET", 602 data: { 603 q: target.value, 604 }, 605 perform: true, 606 headers: { 607 "Content-Type": "application/x-www-form-urlencoded", 608 }, 609 onSuccess: (resp) => { 610 const response = JSON.parse(resp); 611 612 if ( 613 Object.prototype.toString.call( 614 response.suggestions 615 ) === "[object Array]" && 616 response.suggestions.length 617 ) { 618 ul.innerHTML = ""; 619 620 response.suggestions.forEach((suggestion) => { 621 const li = document.createElement("li"); 622 const a = document.createElement("a"); 623 a.href = "#"; 624 a.innerText = suggestion; 625 li.appendChild(a); 626 ul.appendChild(li); 627 a.addEventListener( 628 "click", 629 self.onSuggestionClick.bind(self) 630 ); 631 }); 632 633 self.mls_suggestions.classList.add("visible"); 634 self.mls_suggest_panel.style.display = "block"; 635 } else { 636 self.mls_suggest_panel.style.display = "none"; 637 } 638 }, 639 onError: (xhr) => { 640 if (xhr.status > 0) { 641 Joomla.renderMessages( 642 Joomla.ajaxErrorsMessages(xhr) 643 ); 644 } 645 }, 646 }); 647 } 648 } 649 650 onSuggestionClick(e) { 651 const self = this; 652 653 e.preventDefault(); 654 self.form.querySelector(".mls-input").value = e.target.innerText; 655 self.mls_suggest_panel.style.display = "none"; 656 657 if (self.mls_popular) self.mls_popular.classList.remove("visible"); 658 if (self.mls_suggestions) 659 self.mls_suggestions.classList.remove("visible"); 660 661 self.submitForm(); 662 } 663 664 initSuggestions() { 665 const self = this; 666 667 if (self.mls_suggest_panel) { 668 // Close on click outside 669 document.addEventListener( 670 "mouseup", 671 self.closeSuggestions.bind(self), 672 false 673 ); 674 document.addEventListener( 675 "touchend", 676 self.closeSuggestions.bind(self), 677 false 678 ); 679 680 // Click suggestions 681 if (self.mls_suggest_panel) { 682 if (self.mls_suggest_panel.querySelectorAll("ul")) { 683 self.mls_suggest_panel 684 .querySelectorAll("ul") 685 .forEach((ul) => { 686 ul.querySelectorAll("li a").forEach((a) => { 687 a.addEventListener( 688 "click", 689 self.onSuggestionClick.bind(self) 690 ); 691 }); 692 }); 693 } 694 } 695 696 const searchInputs = [].slice.call( 697 self.form.querySelectorAll(".mls-input") 698 ); 699
700 searchInputs.forEach((searchInput) => { 701 // Suggestions on input 702 if (self.showAutosuggest > 0) { 703 searchInput.addEventListener("input", function () { 704 var lastChar = searchInput.value.substring( 705 searchInput.value.length - 1 706 ); 707 708 if (lastChar != " ") 709 self.onInputChange( 710 searchInput, 711 self.autosuggestUrl 712 ); 713 }); 714 } 715 716 // Navigate suggestions 717 var liSelected; 718 var index = -1; 719 720 searchInput.addEventListener("keydown", function (e) { 721 if ( 722 e.isComposing || 723 e.key === "Process" || 724 e.keyCode === 229 725 ) 726 return; 727 else if ( 728 e.keyCode === 40 || 729 e.keyCode === 38 || 730 e.keyCode === 13 731 ) { 732 var ul = self.form.querySelector( 733 ".mls-panel-items.visible ul" 734 ); 735 736 if (!ul) return; 737 738 var len = ul.getElementsByTagName("li").length - 1; 739 740 if (len < 0) return; 741 742 liSelected = ul.querySelector("li.selected"); 743 } else return; 744 745 if (e.keyCode === 40) { 746 // Down 747 index++; 748 749 if (liSelected) { 750 liSelected.classList.remove("selected"); 751 let next = ul.getElementsByTagName("li")[index]; 752 753 if (typeof next !== undefined && index <= len) { 754 liSelected = next; 755 } else { 756 index = 0; 757 liSelected = 758 ul.getElementsByTagName("li")[0]; 759 } 760 761 liSelected.classList.add("selected"); 762 } else { 763 index = 0; 764 liSelected = ul.getElementsByTagName("li")[0]; 765 liSelected.classList.add("selected"); 766 } 767 } else if (e.keyCode === 38) { 768 // Up 769 if (liSelected) { 770 liSelected.classList.remove("selected"); 771 index--; 772 let next = ul.getElementsByTagName("li")[index]; 773 774 if (typeof next !== undefined && index >= 0) { 775 liSelected = next; 776 } else { 777 index = len; 778 liSelected = 779 ul.getElementsByTagName("li")[len]; 780 } 781 782 liSelected.classList.add("selected"); 783 } else { 784 index = 0; 785 liSelected = ul.getElementsByTagName("li")[len]; 786 liSelected.classList.add("selected"); 787 } 788 } else if (e.keyCode === 13) { 789 // Enter 790 e.preventDefault(); 791 792 if (liSelected) 793 self.form.querySelector(".mls-input").value = 794 liSelected.innerText; 795 796 self.mls_suggest_panel.style.display = "none"; 797 798 if (self.mls_popular) 799 self.mls_popular.classList.remove("visible"); 800 if (self.mls_suggestions) 801 self.mls_suggestions.classList.remove( 802 "visible" 803 ); 804
805 self.submitForm(); 806 807 return false; 808 } 809 }); 810 }); 811 } 812 } 813 814 closeSuggestions(e) { 815 const self = this; 816 817 if ( 818 !e.target.closest(".mls-suggest-panel") && 819 !e.target.closest(".mls-input") 820 ) { 821 if (self.mls_popular) 822 self.mls_popular.classList.remove("visible"); 823 if (self.mls_suggestions) 824 self.mls_suggestions.classList.remove("visible"); 825 826 if (self.mls_suggest_panel) { 827 self.mls_suggest_panel.style.display = "none"; 828 self.mls_suggest_panel 829 .querySelectorAll("ul li") 830 .forEach((a) => { 831 a.classList.remove("selected"); 832 }); 833 } 834 } 835 } 836 837 initMapFilters() { 838 const self = this; 839 840 if (self.form.querySelector(".mls-maps-panel")) { 841 self.countFilters(); 842 843 // Calculate filters width 844 if ( 845 !self.sidebar && 846 self.form.querySelectorAll(".mls-filters-type") 847 ) { 848 var mls_types_width = 0; 849 self.form 850 .querySelectorAll(".mls-filters-type") 851 .forEach((a) => { 852 mls_types_width += a.offsetWidth; 853 }); 854 855 if (self.form.querySelector(".mls-filters-types")) { 856 self.form.querySelector( 857 ".mls-filters-types" 858 ).style.width = mls_types_width + 1 + "px"; 859 } 860 } 861 862 // Select filter 863 const mls_type = self.form.querySelectorAll(".mls-type"); 864 865 if (mls_type) { 866 mls_type.forEach((filter) => { 867 // Check if filter is checked on page load 868 if (filter.checked) { 869 filter 870 .closest(".controls") 871 .classList.add("mls-checked"); 872 } 873 874 // Toggle filters box if keep filters open is disabled 875 if (self.sidebar && !self.keepFiltersOpen) { 876 var filter_id = filter.getAttribute("data-id"); 877 878 filter 879 .closest("label") 880 .addEventListener("click", function (e) { 881 self.form 882 .querySelectorAll( 883 '.mls-filters-group-box[data-filter="' + 884 filter_id + 885 '"]' 886 ) 887 .forEach((a) => { 888 if ( 889 !a.classList.contains( 890 "mls-filters-hidden" 891 ) 892 ) { 893 e.preventDefault(); 894 895 if ( 896 !a.style.display || 897 a.style.display == "none" 898 ) 899 a.style.display = "block"; 900 else a.style.display = "none"; 901 } 902 }); 903 }); 904 } 905 906 filter.addEventListener("change", function (e) { 907 var filter_id = filter.getAttribute("data-id"); 908 909 if (!self.keepFiltersOpen) 910 self.form 911 .querySelectorAll(".mls-filters-group-box") 912 .forEach((a) => { 913 a.style.display = "none"; 914 }); 915 916 if (filter.checked) { 917 filter 918 .closest(".controls") 919 .classList.add("mls-checked"); 920 mls_type.forEach((a) => { 921 a.closest(".controls").classList.remove( 922 "mls-active" 923 ); 924 }); 925 } else { 926 if ( 927 filter 928 .closest(".controls") 929 .classList.contains("mls-active") 930 ) { 931 mls_type.forEach((a) => { 932 a.closest(".controls").classList.remove( 933 "mls-active" 934 ); 935 }); 936 } else { 937 mls_type.forEach((a) => { 938 a.closest(".controls").classList.remove( 939 "mls-active" 940 ); 941 }); 942 943 if (!self.sidebar || !self.keepFiltersOpen) 944 filter 945 .closest(".controls") 946 .classList.add("mls-active"); 947 948 self.form 949 .querySelectorAll( 950 '.mls-filters-group-box[data-filter="' + 951 filter_id + 952 '"]' 953 ) 954 .forEach((a) => { 955 if ( 956 !a.classList.contains( 957 "mls-filters-hidden" 958 ) 959 ) 960 a.style.display = "block"; 961 }); 962 } 963 964 filter.checked = true; 965 } 966 967 // Check all children if none is selected 968 let children_selected = self.form.querySelectorAll( 969 '.filter-node:checked[data-filter="' + 970 filter_id + 971 '"]' 972 ).length; 973 974 if (!children_selected) { 975 self.form 976 .querySelectorAll( 977 '.filter-node[data-filter="' + 978 filter_id + 979 '"]' 980 ) 981 .forEach((a) => { 982 a.checked = true; 983 }); 984 } 985 986 self.countFilters(); 987 }); 988 }); 989 } 990 991 // Show more children nodes 992 const mls_more_items = self.form.querySelectorAll(".mls-more"); 993
994 mls_more_items.forEach((selector) => { 995 selector.addEventListener("click", function (e) { 996 selector 997 .closest(".mls-filters-column") 998 .querySelectorAll(".control-group") 999 .forEach((a) => { 1000 a.classList.remove("mls-hidden"); 1001 }); 1002 1003 selector.style.display = "none"; 1004 }); 1005 }); 1006 1007 // Expand filter 1008 const mls_select_type = 1009 self.form.querySelectorAll(".mls-select-type"); 1010 1011 mls_select_type.forEach((selector) => { 1012 selector.addEventListener("click", function (e) { 1013 var filter_id = selector 1014 .closest(".controls") 1015 .querySelector(".mls-type") 1016 .getAttribute("data-id"); 1017 1018 if (!self.keepFiltersOpen) 1019 self.form 1020 .querySelectorAll(".mls-filters-group-box") 1021 .forEach((a) => { 1022 a.style.display = "none"; 1023 }); 1024 1025 self.form 1026 .querySelectorAll( 1027 '.mls-filters-group-box[data-filter="' + 1028 filter_id + 1029 '"]' 1030 ) 1031 .forEach((a) => { 1032 if (!a.classList.contains("mls-filters-hidden")) 1033 a.style.display = "block"; 1034 }); 1035 self.form.querySelectorAll(".mls-type").forEach((a) => { 1036 a.closest(".controls").classList.remove( 1037 "mls-active" 1038 ); 1039 }); 1040 1041 selector 1042 .closest(".controls") 1043 .classList.add("mls-checked"); 1044 1045 if (!self.sidebar || !self.keepFiltersOpen) 1046 selector 1047 .closest(".controls") 1048 .classList.add("mls-active"); 1049 1050 selector 1051 .closest(".controls") 1052 .querySelector(".mls-type").checked = true; 1053 1054 // Check all children 1055 self.form 1056 .querySelectorAll( 1057 '.filter-node[data-filter="' + filter_id + '"]' 1058 ) 1059 .forEach((a) => { 1060 a.checked = true; 1061 }); 1062 1063 self.countFilters(); 1064 self.submitForm(); 1065 }); 1066 }); 1067 1068 // Remove filter 1069 const mls_uncheck_type = 1070 self.form.querySelectorAll(".mls-uncheck-type"); 1071 1072 mls_uncheck_type.forEach((selector) => { 1073 selector.addEventListener("click", function (e) { 1074 var filter_id = selector.getAttribute("data-filter"); 1075 selector 1076 .closest(".controls") 1077 .querySelector(".mls-type").checked = false; 1078 selector 1079 .closest(".controls") 1080 .classList.remove("mls-checked"); 1081 self.form 1082 .querySelectorAll(".mls-filters-type .controls") 1083 .forEach((a) => { 1084 a.classList.remove("mls-active"); 1085 }); 1086 1087 if (!self.keepFiltersOpen) 1088 self.form 1089 .querySelectorAll(".mls-filters-group-box") 1090 .forEach((a) => { 1091 a.style.display = "none"; 1092 }); 1093 1094 // Uncheck all children 1095 self.form 1096 .querySelectorAll( 1097 '.filter-node[data-filter="' + filter_id + '"]' 1098 ) 1099 .forEach((a) => { 1100 a.checked = false; 1101 }); 1102 1103 self.countFilters();
1104 self.submitForm(); 1105 }); 1106 }); 1107 1108 // Select all children nodes 1109 const select_all = 1110 self.form.querySelectorAll(".mls-select-all"); 1111 1112 select_all.forEach((selector) => { 1113 selector.addEventListener("click", function (e) { 1114 const filter_id = selector.getAttribute("data-filter"); 1115 1116 selector 1117 .closest(".mls-group-box") 1118 .querySelectorAll(".filter-node") 1119 .forEach((a) => { 1120 a.checked = true; 1121 }); 1122 1123 if (filter_id) { 1124 // Select type 1125 self.form.querySelector( 1126 '.mls-type[data-id="' + filter_id + '"]' 1127 ).checked = true; 1128 1129 if (!self.sidebar || !self.keepFiltersOpen) 1130 self.form 1131 .querySelector( 1132 '.mls-type[data-id="' + filter_id + '"]' 1133 ) 1134 .closest(".controls") 1135 .classList.add("mls-active"); 1136 1137 self.form 1138 .querySelector( 1139 '.mls-type[data-id="' + filter_id + '"]' 1140 ) 1141 .closest(".controls") 1142 .classList.add("mls-checked"); 1143 } 1144 1145 self.countFilters(); 1146 self.submitForm(); 1147 }); 1148 }); 1149 1150 // Deselect all children nodes 1151 const select_none = 1152 self.form.querySelectorAll(".mls-select-none"); 1153 1154 select_none.forEach((selector) => { 1155 selector.addEventListener("click", function (e) { 1156 const filter_id = selector.getAttribute("data-filter"); 1157 1158 selector 1159 .closest(".mls-group-box") 1160 .querySelectorAll(".filter-node") 1161 .forEach((a) => { 1162 a.checked = false; 1163 }); 1164 1165 if (filter_id) { 1166 // Deselect type 1167 self.form.querySelector( 1168 '.mls-type[data-id="' + filter_id + '"]' 1169 ).checked = false; 1170 self.form 1171 .querySelector( 1172 '.mls-type[data-id="' + filter_id + '"]' 1173 ) 1174 .closest(".controls") 1175 .classList.remove("mls-checked", "mls-active"); 1176 } 1177 1178 self.countFilters(); 1179 self.submitForm(); 1180 }); 1181 }); 1182 1183 // Close filters dropdown 1184 const mls_fg_close = 1185 self.form.querySelectorAll(".mls-fg-close"); 1186 1187 mls_fg_close.forEach((selector) => { 1188 selector.addEventListener("click", function (e) { 1189 e.preventDefault(); 1190 1191 if (!self.keepFiltersOpen) 1192 self.form 1193 .querySelectorAll(".mls-filters-group-box") 1194 .forEach((a) => { 1195 a.style.display = "none"; 1196 }); 1197 1198 self.form 1199 .querySelectorAll(".mls-filters-type .mls-checked") 1200 .forEach((a) => { 1201 a.classList.remove("mls-active"); 1202 }); 1203 }); 1204 }); 1205 1206 // Close filters dropdown on click outside 1207 var filtersContainer = 1208 self.form.querySelector(".mls-filters-box"); 1209 1210 if ( 1211 filtersContainer && 1212 (!self.sidebar || !self.keepFiltersOpen) 1213 ) { 1214 document.addEventListener( 1215 "mouseup", 1216 self.closeFilters.bind(self), 1217 false 1218 ); 1219 document.addEventListener( 1220 "touchend", 1221 self.closeFilters.bind(self), 1222 false 1223 ); 1224 } 1225 1226 // 'Clear All' button 1227 var clear_maps = self.form.querySelector(".mls-clear-maps"); 1228 1229 if (clear_maps) { 1230 clear_maps.addEventListener("click", function (e) { 1231 self.form 1232 .querySelectorAll(".filter-node") 1233 .forEach((a) => { 1234 a.checked = false; 1235 }); 1236 self.form.querySelectorAll(".mls-type").forEach((a) => { 1237 a.checked = false; 1238 }); 1239 self.form 1240 .querySelectorAll(".mls-filters-type .controls") 1241 .forEach((a) => { 1242 a.classList.remove("mls-checked", "mls-active"); 1243 }); 1244 1245 if (!self.keepFiltersOpen) 1246 self.form 1247 .querySelectorAll(".mls-filters-group-box") 1248 .forEach((a) => { 1249 a.style.display = "none"; 1250 }); 1251 1252 self.countFilters();
1253 self.submitForm(); 1254 }); 1255 } 1256 1257 // Select a node 1258 const filter_node = self.form.querySelectorAll(".filter-node"); 1259 1260 if ( 1261 filter_node && 1262 self.form.querySelectorAll(".mls-type").length 1263 ) { 1264 filter_node.forEach((node) => { 1265 node.addEventListener("click", function (e) { 1266 var filter_id = node.getAttribute("data-filter"); 1267 1268 // Select type only if sum of all children > 0 1269 var checked_sum = self.form.querySelectorAll( 1270 '.filter-node[data-filter="' + 1271 filter_id + 1272 '"]:checked' 1273 ).length; 1274 1275 if ( 1276 self.form.querySelector( 1277 '.mls-type[data-id="' + filter_id + '"]' 1278 ) 1279 ) { 1280 if (checked_sum > 0) { 1281 // Select type 1282 self.form.querySelector( 1283 '.mls-type[data-id="' + filter_id + '"]' 1284 ).checked = true; 1285 1286 self.form 1287 .querySelector( 1288 '.mls-type[data-id="' + 1289 filter_id + 1290 '"]' 1291 ) 1292 .closest(".controls") 1293 .classList.add("mls-checked"); 1294 1295 if (!self.sidebar || !self.keepFiltersOpen) 1296 self.form 1297 .querySelector( 1298 '.mls-type[data-id="' + 1299 filter_id + 1300 '"]' 1301 ) 1302 .closest(".controls") 1303 .classList.add("mls-active"); 1304 } else { 1305 // Deselect type 1306 self.form.querySelector( 1307 '.mls-type[data-id="' + filter_id + '"]' 1308 ).checked = false; 1309 self.form 1310 .querySelector( 1311 '.mls-type[data-id="' + 1312 filter_id + 1313 '"]' 1314 ) 1315 .closest(".controls") 1316 .classList.remove("mls-checked"); 1317 } 1318 } 1319 1320 self.countFilters(); 1321 }); 1322 }); 1323 } 1324 } 1325 } 1326 1327 closeFilters(e) { 1328 const self = this; 1329 1330 if (!e.target.closest(".mls-filters-box")) { 1331 self.form 1332 .querySelectorAll(".mls-filters-group-box") 1333 .forEach((a) => { 1334 a.style.display = "none"; 1335 }); 1336 self.form.querySelectorAll(".mls-checked").forEach((a) => { 1337 a.classList.remove("mls-active"); 1338 }); 1339 } 1340 } 1341 1342 // Count active filters 1343 countFilters() { 1344 const self = this; 1345 1346 if (self.sidebar) return; 1347 1348 var total_types = 1349 self.form.querySelectorAll(".mls-type:checked").length; 1350 var total_children = self.form.querySelectorAll( 1351 ".mls-filters-group-box:not(.mls-filters-hidden) .filter-node:checked" 1352 ).length; 1353 var total_orphans = self.form.querySelectorAll( 1354 ".mls-orphan-group-box:not(.mls-filters-hidden) .filter-node:checked" 1355 ).length; 1356 var total_filters = total_types + total_children + total_orphans; 1357 1358 if (self.form.querySelector(".mls-filters-count")) 1359 self.form.querySelector(".mls-filters-count").innerHTML = 1360 total_filters; 1361 } 1362 1363 initDateFilters() { 1364 const self = this; 1365 1366 if (self.form.querySelector(".mls-dates-panel")) { 1367 // 'Clear All' button 1368 var clear_dates = self.form.querySelector(".mls-clear-dates"); 1369 1370 if (clear_dates) { 1371 clear_dates.addEventListener("click", function (e) { 1372 self.form.querySelector( 1373 "#filter_date1_" + self.formId 1374 ).value = ""; 1375 self.form.querySelector( 1376 "#filter_date2_" + self.formId 1377 ).value = ""; 1378 1379 self.countFilters();
1380 self.submitForm(); 1381 }); 1382 1383 const date1 = self.form.querySelector( 1384 "#filter_date1_" + self.formId 1385 ); 1386 1387 if (date1) 1388 date1.addEventListener("change", function () { 1389 self.submitForm(); 1390 }); 1391 1392 const date2 = self.form.querySelector( 1393 "#filter_date2_" + self.formId 1394 ); 1395 1396 if (date2) 1397 date2.addEventListener("change", function () { 1398 self.submitForm(); 1399 }); 1400 } 1401 } 1402 } 1403 1404 dropdownLayout() { 1405 const self = this; 1406 1407 if (self.form.closest(".mls").classList.contains("mls-dropdown")) { 1408 // Close results on click outside 1409 document.addEventListener( 1410 "mouseup", 1411 self.closeDropdownResults.bind(self), 1412 false 1413 ); 1414 document.addEventListener( 1415 "touchend", 1416 self.closeDropdownResults.bind(self), 1417 false 1418 ); 1419 1420 // Open results on input 1421 document.addEventListener( 1422 "mouseup", 1423 self.openDropdownResults.bind(self), 1424 false 1425 ); 1426 document.addEventListener( 1427 "touchend", 1428 self.openDropdownResults.bind(self), 1429 false 1430 ); 1431 } 1432 } 1433 1434 closeDropdownResults(e) { 1435 const self = this; 1436 1437 if (!e.target.closest(".mls-dropdown")) { 1438 self.form.querySelector( 1439 ".mls-dropdown .mls-results" 1440 ).style.display = "none"; 1441 } 1442 } 1443 1444 openDropdownResults(e) { 1445 const self = this; 1446 1447 if ( 1448 e.target.closest(".mls-dropdown") && 1449 e.target.classList.contains("mls-input") 1450 ) { 1451 self.form.querySelector( 1452 ".mls-dropdown .mls-results" 1453 ).style.display = "block"; 1454 } 1455 } 1456 1457 // Click events inside .mls-results 1458 clickEvents() { 1459 const self = this; 1460 1461 self.form 1462 .querySelector(".mls-results") 1463 .addEventListener("click", function (e) { 1464 // 'Did you mean' link 1465 if (e.target.classList.contains("suggested-search")) { 1466 var this_suggestion = 1467 e.target.getAttribute("data-suggested"); 1468 self.form.querySelector(".mls-input").value = 1469 this_suggestion; 1470 1471 if (self.mls_suggest_panel) { 1472 self.mls_suggest_panel.style.display = "none"; 1473 1474 if (self.mls_popular) 1475 self.mls_popular.classList.remove("visible"); 1476 if (self.mls_suggestions) 1477 self.mls_suggestions.classList.remove( 1478 "visible" 1479 ); 1480 1481 self.mls_suggest_panel 1482 .querySelectorAll("ul li") 1483 .forEach((a) => { 1484 a.classList.remove("selected"); 1485 }); 1486 } 1487 1488 self.submitForm(); 1489 } 1490 1491 // Remove search terms 1492 if ( 1493 e.target && 1494 (e.target.classList.contains("mls-remove-term") || 1495 e.target.closest(".mls-remove-term")) 1496 ) { 1497 if (e.target.classList.contains("mls-remove-term")) 1498 var _this = e.target; 1499 else var _this = e.target.parentNode; 1500 1501 const q = self.form.querySelector(".mls-input"); 1502 var this_term = _this 1503 .closest(".mls-query-item") 1504 .querySelector(".mls-term-inner").innerText; 1505 1506 var orRegex = new RegExp( 1507 "(?:^|\\s)" + 1508 self.orOperator + 1509 '\\s["]?' + 1510 this_term + 1511 '["]?($|\\s)', 1512 "gi" 1513 ); 1514 var andRegex = new RegExp( 1515 "(?:^|\\s)" + 1516 self.andOperator + 1517 '\\s["]?' + 1518 this_term + 1519 '["]?($|\\s)', 1520 "gi" 1521 ); 1522 var notRegex = new RegExp( 1523 "(?:^|\\s)" + 1524 self.notOperator + 1525 '\\s["]?' + 1526 this_term + 1527 '["]?($|\\s)', 1528 "gi" 1529 ); 1530 var simpleRegex = new RegExp( 1531 '(?:^|\\s)["]?' + this_term + '["]?($|\\s)', 1532 "gi" 1533 ); 1534 1535 // Remove term 1536 _this.closest(".mls-query-item").remove(); 1537 q.value = q.value.replace(orRegex, " "); 1538 q.value = q.value.replace(andRegex, " "); 1539 q.value = q.value.replace(notRegex, " "); 1540 q.value = q.value.replace(simpleRegex, " "); 1541 1542 if (self.mls_suggestions) { 1543 self.mls_suggestions.classList.remove("visible"); 1544 self.mls_suggest_panel 1545 .querySelectorAll("ul li") 1546 .forEach((a) => { 1547 a.classList.remove("selected"); 1548 }); 1549 } 1550 1551 // Trim trailing operators 1552 var q_value = q.value; 1553 q_value = q_value.trim(); 1554 var splitQ = q_value.split(" "); 1555 var firstElement = splitQ[0]; 1556 1557 if ( 1558 firstElement === self.orOperator || 1559 firstElement === self.andOperator || 1560 firstElement === self.notOperator 1561 ) 1562 q_value = q_value.substring( 1563 q_value.indexOf(" ") + 1 1564 ); 1565 1566 q.value = q_value; 1567 1568 if (typeof self.mls_ajax_request !== "undefined") 1569 self.mls_ajax_request.abort(); 1570
1571 if (q_value) self.submitForm(); 1572 else self.clearInput(); 1573 } 1574 1575 // Remove date filters 1576 if ( 1577 e.target.classList.contains("mls-remove-date1") || 1578 e.target.closest(".mls-remove-date1") 1579 ) { 1580 e.preventDefault(); 1581 self.form.querySelector( 1582 "#filter_date1_" + self.formId 1583 ).value = ""; 1584 self.submitForm(); 1585 } 1586 1587 if ( 1588 e.target.classList.contains("mls-remove-date2") || 1589 e.target.closest(".mls-remove-date2") 1590 ) { 1591 e.preventDefault(); 1592 self.form.querySelector( 1593 "#filter_date2_" + self.formId 1594 ).value = ""; 1595 self.submitForm(); 1596 } 1597 1598 // Remove map filters 1599 if ( 1600 e.target.classList.contains("mls-remove-map") || 1601 e.target.closest(".mls-remove-map") 1602 ) { 1603 e.preventDefault(); 1604 1605 if (e.target.classList.contains("mls-remove-map")) 1606 var _this = e.target; 1607 else var _this = e.target.parentNode; 1608 1609 var filter_id = _this.getAttribute("data-filter"); 1610 var node_id = _this.getAttribute("data-node"); 1611 1612 // It's a child item 1613 if (node_id) { 1614 var checked_sum = self.form.querySelectorAll( 1615 '.filter-node[data-filter="filter-' + 1616 filter_id + 1617 '"]:checked' 1618 ).length; 1619 1620 // Remove parent only if sum of children == 1 1621 if ( 1622 checked_sum === 1 && 1623 self.form.querySelectorAll(".mls-type").length 1624 ) { 1625 // Deselect parent 1626 self.form.querySelector( 1627 '.mls-type[data-id="filter-' + 1628 filter_id + 1629 '"]' 1630 ).checked = false; 1631 self.form 1632 .querySelector( 1633 '.mls-type[data-id="filter-' + 1634 filter_id + 1635 '"]' 1636 ) 1637 .closest(".controls") 1638 .classList.remove( 1639 "mls-checked", 1640 "mls-active" 1641 ); 1642 1643 if (!self.keepFiltersOpen) 1644 self.form 1645 .querySelectorAll( 1646 ".mls-filters-group-box" 1647 ) 1648 .forEach((a) => { 1649 a.style.display = "none"; 1650 }); 1651 } 1652 1653 // Deselect this node 1654 self.form.querySelector( 1655 '.filter-node[value="' + 1656 node_id + 1657 '"][data-filter="filter-' + 1658 filter_id + 1659 '"]' 1660 ).checked = false; 1661 } 1662 // It's a parent item 1663 else { 1664 // Remove all children 1665 self.form 1666 .querySelectorAll( 1667 '.mls-query-item[data-filter="filter-' + 1668 filter_id + 1669 '"]' 1670 ) 1671 .forEach((a) => { 1672 a.remove(); 1673 }); 1674 1675 // Deselect children nodes 1676 self.form 1677 .querySelectorAll( 1678 '.filter-node[data-filter="filter-' + 1679 filter_id + 1680 '"]' 1681 ) 1682 .forEach((a) => { 1683 a.checked = false; 1684 }); 1685 1686 if (!self.keepFiltersOpen) 1687 self.form 1688 .querySelectorAll(".mls-filters-group-box") 1689 .forEach((a) => { 1690 a.style.display = "none"; 1691 }); 1692 1693 // Deselect parent 1694 self.form.querySelector( 1695 '.mls-type[data-id="filter-' + filter_id + '"]' 1696 ).checked = false; 1697 1698 self.form 1699 .querySelector( 1700 '.mls-type[data-id="filter-' + 1701 filter_id + 1702 '"]' 1703 ) 1704 .closest(".controls") 1705 .classList.remove("mls-checked", "mls-active"); 1706 1707 if (!self.keepFiltersOpen) 1708 self.form 1709 .querySelectorAll(".mls-filters-group-box") 1710 .forEach((a) => { 1711 a.style.display = "none"; 1712 }); 1713 } 1714 1715 _this.closest(".mls-query-item").remove(); 1716 self.countFilters();
1717 self.submitForm(); 1718 } 1719 1720 // Pagination links 1721 if ( 1722 e.target.classList.contains("mls-page-link") || 1723 e.target.closest(".mls-page-link") 1724 ) { 1725 e.preventDefault(); 1726 1727 if (e.target.classList.contains("mls-page-link")) 1728 var _this = e.target; 1729 else var _this = e.target.closest("li"); 1730 1731 if (_this.classList.contains("disabled")) return; 1732 1733 var link_href = _this 1734 .querySelector("a") 1735 .getAttribute("href"); 1736 self.submitPage(link_href); 1737 } 1738 }); 1739 } 1740 } 1741 1742 window.MinitekLiveSearch = { 1743 initialise: (params, formId, searchUrl, autosuggestUrl) => 1744 new MinitekLiveSearch(params, formId, searchUrl, autosuggestUrl), 1745 }; 1746})(document, Joomla);
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.