1import { store, getElement } from "@wordpress/interactivity"; 2import { scrollToAnchor } from "../../src/js/utils/scroll-to-anchor.js"; 3import { sortBy } from "../../src/js/utils/sort-obj-array-by.js"; 4import { isObject } from "../../src/js/utils/is.js"; 5 6const svgPath = mgThemeVars ? mgThemeVars.svgPath : null; 7const orgPlaceholderImage = svgPath ? `${svgPath}/org_placeholder.png` : null; 8 9function parseHtmlText(text = "") { 10 const parser = new DOMParser(); 11 const decoded = parser.parseFromString(text, "text/html").documentElement 12 .textContent; 13 return decoded; 14} 15 16function validateWebsite(websiteValue) { 17 // Handle empty or whitespace-only values 18 if (!websiteValue || websiteValue.trim() === "") { 19 return null; 20 } 21 22 // Split by pipe character to handle multiple URLs 23 const urls = websiteValue 24 .split("|") 25 .map((url) => url.trim()) 26 .filter((url) => url !== ""); 27 28 if (urls.length === 0) { 29 return null; 30 } 31 32 // Take the first valid URL 33 let validUrl = urls[0]; 34 35 // Add protocol if missing 36 if (!validUrl.match(/^https?:\/\//i)) { 37 validUrl = "https://" + validUrl; 38 } 39 40 // Basic URL validation 41 try { 42 new URL(validUrl); 43 return validUrl; 44 } catch (e) { 45 // If URL is still invalid, return null 46 return null; 47 } 48} 49 50const searchUrl = mgThemeVars.restUrl + "directory"; 51const facetsUrl = mgThemeVars.restUrl + "directory-facets"; 52const searchCollectionId = 1; 53const tabClasses = 54 "uppercase text-sm lg:text-base border-b border-dividers bg-quaternary text-white px-6 lg:px-12 py-6 text-center font-black"; 55const currentTabClasses = 56 "uppercase text-sm lg:text-base border border-dividers border-b-white px-6 lg:px-12 py-6 text-center font-black bg-white text-font-main"; 57 58store("mg-store-directory", { 59 state: { 60 page: 1, 61 lastPage: 1, // last page of the directory 62 initialized: false, 63 posts: [], 64 mgPaginationListenerAdded: false, 65 loading: false, 66 mode: "company", 67 zip: "", 68 radius: "", 69 params: { 70 record_type: "", 71 record_number: "", 72 title: "", 73 categories: [] 74 }, 75 options: { 76 categories: [], 77 radius: [ 78 { 79 name: "5 miles", 80 code: 5 81 }, 82 { 83 name: "10 miles", 84 code: 10 85 }, 86 { 87 name: "15 miles", 88 code: 15 89 }, 90 { 91 name: "25 miles", 92 code: 25 93 }, 94 { 95 name: "50 miles", 96 code: 50 97 }, 98 { 99 name: "100 miles", 100 code: 100 101 } 102 ] 103 } 104 }, 105 106 actions: { 107 updateName(e) { 108 const { state } = store("mg-store-directory"); 109 state.params.title = e.target.value; 110 }, 111 112 scrollToResults() { 113 const resultsAnchor = document.getElementById( 114 "mg-block-directory-feed" 115 ); 116 scrollToAnchor(resultsAnchor); 117 }, 118 119 prevPage() { 120 const { state, callbacks } = store("mg-store-directory"); 121 state.page -= 1; 122 123 // fetchDirectory 124 callbacks.fetchDirectory(); 125 }, 126 127 nextPage() { 128 const { state, callbacks } = store("mg-store-directory"); 129 state.page += 1; 130 131 // fetchDirectory 132 callbacks.fetchDirectory(); 133 }, 134 135 addPaginationNumber(pageNumber, container) { 136 const template = document.getElementById( 137 "directory-pagination-number" 138 ); 139 const clone = template.content.cloneNode(true); 140 const button = clone.querySelector("button"); 141 button.textContent = pageNumber; 142 button.dataset.pageNumber = pageNumber; 143 container.appendChild(clone); 144 }, 145
146 addPaginationCurrent(pageNumber, container) { 147 const template = document.getElementById( 148 "directory-pagination-current" 149 ); 150 const clone = template.content.cloneNode(true); 151 const span = clone.querySelector("span"); 152 span.textContent = pageNumber; 153 container.appendChild(clone); 154 }, 155 156 addPaginationDots(container) { 157 const template = document.getElementById( 158 "directory-pagination-dots" 159 ); 160 const clone = template.content.cloneNode(true); 161 container.appendChild(clone); 162 }, 163 164 setupPaginationDelegation() { 165 const { state, callbacks } = store("mg-store-directory"); 166 const paginationNumbers = document.querySelector( 167 ".mg-directory-pagination-container" 168 ); 169 170 // Use event delegation - single listener for all pagination buttons 171 // Only add the listener once to prevent duplicates 172 if (state.mgPaginationListenerAdded || !paginationNumbers) return; 173 174 paginationNumbers.addEventListener("click", (e) => { 175 if (e.target.matches("button.mg-directory-pagination-number")) { 176 const pageNumber = parseInt(e.target.dataset.pageNumber); 177 state.page = pageNumber; 178 callbacks.fetchDirectory(); 179 } 180 }); 181 182 state.mgPaginationListenerAdded = true; 183 }, 184 185 switchToCompany() { 186 const { state, actions, callbacks } = store("mg-store-directory"); 187 state.mode = "company"; 188 189 // Update URL parameters 190 actions.updateUrlParams(); 191 192 callbacks.handleModeSwitch(); 193 }, 194 195 switchToIndividual() { 196 const { state, actions, callbacks } = store("mg-store-directory"); 197 state.mode = "individual"; 198 199 // Update URL parameters 200 actions.updateUrlParams(); 201 202 callbacks.handleModeSwitch(); 203 }, 204 205 updateMode(event) { 206 const { state, actions, callbacks } = store("mg-store-directory"); 207 state.mode = event.target.value; 208 209 // Update URL parameters 210 actions.updateUrlParams(); 211 212 // Don't fetch here - wait for form submit 213 callbacks.getAndSetFacets(); 214 }, 215 216 updateName(event) { 217 const { state, actions } = store("mg-store-directory"); 218 state.params.title = event.target.value; 219 actions.updateUrlParams(); 220 // Don't fetch here - wait for form submit 221 }, 222 223 updateZip(event) { 224 const { state, actions } = store("mg-store-directory"); 225 state.zip = event.target.value; 226 actions.updateUrlParams(); 227 // Don't fetch here - wait for form submit 228 }, 229 230 updateRadius(event) { 231 const { state, actions } = store("mg-store-directory"); 232 state.radius = event.target.value; 233 actions.updateUrlParams(); 234 // Don't fetch here - wait for form submit 235 }, 236 237 updateCategory(event) { 238 const { state, actions } = store("mg-store-directory"); 239 state.params.categories = [event.target.value]; 240 actions.updateUrlParams(); 241 // Don't fetch here - wait for form submit 242 }, 243 244 handleFormSubmit(event) { 245 event.preventDefault(); 246 const { state, actions, callbacks } = store("mg-store-directory"); 247 state.page = 1; // Reset to first page 248 callbacks.fetchDirectory(); 249 actions.updateTabClasses(); 250 }, 251 252 clearForm() { 253 const { state, actions } = store("mg-store-directory"); 254 255 // NOTE: dont change `state.mode` here 256 state.params = { 257 record_type: "", 258 record_number: "", 259 title: "", 260 categories: [] 261 }; 262 state.zip = ""; 263 state.radius = ""; 264 state.page = 1; 265 266 // Clear URL parameters 267 actions.updateUrlParams(); 268 }, 269 270 updateTabClasses() { 271 const { state } = store("mg-store-directory"); 272 const tabs = document.querySelectorAll(".mg-directory-tabs button"); 273 const companyTab = document.querySelector( 274 '[data-wp-on--click="actions.switchToCompany"]' 275 ); 276 const individualTab = document.querySelector( 277 '[data-wp-on--click="actions.switchToIndividual"]' 278 ); 279
280 tabs.forEach((tab) => { 281 tab.className = tabClasses; 282 }); 283 284 if (state.mode == "company") { 285 companyTab.className = currentTabClasses; 286 } else if (state.mode == "individual") { 287 individualTab.className = currentTabClasses; 288 } 289 }, 290 291 updateRadioButtons() { 292 const { state } = store("mg-store-directory"); 293 const filterForm = document.querySelector( 294 ".mg-directory-filter-form" 295 ); 296 const companyRadio = filterForm 297 ? filterForm.querySelector( 298 'input[name="type"][value="company"]' 299 ) 300 : null; 301 const individualRadio = filterForm 302 ? filterForm.querySelector( 303 'input[name="type"][value="individual"]' 304 ) 305 : null; 306 307 if (companyRadio && individualRadio) { 308 if (state.mode === "company") { 309 companyRadio.checked = true; 310 individualRadio.checked = false; 311 } else if (state.mode === "individual") { 312 companyRadio.checked = false; 313 individualRadio.checked = true; 314 } 315 } 316 }, 317 318 updateUrlParams() { 319 const { state } = store("mg-store-directory"); 320 const url = new URL(window.location); 321 322 // Clear existing search params 323 url.searchParams.delete("title"); 324 url.searchParams.delete("category"); 325 url.searchParams.delete("zip"); 326 url.searchParams.delete("radius"); 327 url.searchParams.delete("mode"); 328 329 // Add current filter values to URL 330 if (state.params.title) { 331 url.searchParams.set("title", state.params.title); 332 } 333 if (state.params.categories && state.params.categories.length > 0) { 334 url.searchParams.set("category", state.params.categories[0]); 335 } 336 if (state.zip) { 337 url.searchParams.set("zip", state.zip); 338 } 339 if (state.radius) { 340 url.searchParams.set("radius", state.radius); 341 } 342 if (state.mode && state.mode !== "company") { 343 url.searchParams.set("mode", state.mode); 344 } 345 346 // Update browser URL without page reload 347 window.history.replaceState({}, "", url); 348 } 349 }, 350 351 callbacks: { 352 init() { 353 const { state, actions, callbacks } = store("mg-store-directory"); 354 console.log("MG STORE, page:", state.page); 355 356 // Only initialize once 357 if (state.initialized) { 358 console.log("MG STORE, already initialized"); 359 return; 360 } 361 362 // init UI 363 actions.setupPaginationDelegation(); 364 365 // fetch data 366 callbacks.getAndSetFacets(); 367 368 // Update data based on search parameters from URL GET params 369 const urlParams = new URLSearchParams(window.location.search); 370 const searchTitle = urlParams.get("title") || ""; 371 const searchCategory = urlParams.get("category") || ""; 372 const searchZip = urlParams.get("zip") || ""; 373 const searchRadius = urlParams.get("radius") || ""; 374 const searchMode = urlParams.get("mode") || "company"; // Default to company 375 let hasInitUrlParams = false; 376 377 // Set initial search parameters if they exist 378 if (searchTitle) { 379 state.params.title = searchTitle; 380 hasInitUrlParams = true; 381 } 382 if (searchCategory) { 383 state.params.categories = [searchCategory]; 384 hasInitUrlParams = true; 385 386 const catSelect = document.querySelector( 387 ".mg-directory-filter-category" 388 ); 389 if (catSelect) { 390 // wait on second and update dom node 391 // not sure how to properly do this with interactivity API 392 setTimeout(() => { 393 catSelect.value = searchCategory; 394 }, 1000); 395 } 396 } 397 if (searchZip) { 398 state.zip = searchZip; 399 hasInitUrlParams = true; 400 } 401 if (searchRadius) {
402 state.radius = searchRadius; 403 hasInitUrlParams = true; 404 } 405 if (searchMode) { 406 state.mode = searchMode; 407 } 408 409 // Update radio buttons after setting mode from URL 410 actions.updateRadioButtons(); 411 412 // Update tab classes after setting mode from URL 413 actions.updateTabClasses(); 414 415 // then, fetch directory 416 callbacks.fetchDirectory(hasInitUrlParams); 417 418 // this is last, set initialized to true 419 state.initialized = true; 420 }, 421 422 handlePostsUpdate() { 423 const { state } = store("mg-store-directory"); 424 const docFragment = document.createDocumentFragment(); 425 const feedGridContainer = document.getElementById( 426 "mg-block-directory-feed" 427 ); 428 const prevButton = document.querySelector( 429 ".mg-directory-pagination-prev-button" 430 ); 431 const nextButton = document.querySelector( 432 ".mg-directory-pagination-next-button" 433 ); 434 const isIndividual = state.mode === "individual"; 435 436 // console.log("Posts update triggered. Posts are now:", state.posts); 437 438 if (!feedGridContainer) return; 439 440 /** 441 * Not sure why but I could not get wp-bind to work here, so I'm doing it manually. 442 */ 443 if (prevButton && state.page === 1) { 444 prevButton.classList.add("hidden"); 445 } else { 446 prevButton.classList.remove("hidden"); 447 } 448 449 if (nextButton && state.page === state.lastPage) { 450 nextButton.classList.add("hidden"); 451 } else { 452 nextButton.classList.remove("hidden"); 453 } 454 455 /** 456 * Warning, avoid event listeners on template items. 457 * If used, remove them when the item is removed from the DOM. 458 */ 459 state.posts.forEach((post) => { 460 const postItem = document 461 .getElementById("directory-item") 462 .content.cloneNode(true); 463 464 // Populate the directory item with data 465 const contentElm = postItem.querySelector( 466 ".mg-directory-item-content" 467 ); 468 const heading = postItem.querySelector("h3"); 469 const category = postItem.querySelector(".category"); 470 const address = postItem.querySelector(".address"); 471 const website = postItem.querySelector(".website a"); 472 const websiteText = postItem.querySelector(".website b"); 473 const phone = postItem.querySelector(".phone"); 474 const ogImage = postItem.querySelector(".og-image"); 475 const recordNumber = postItem.querySelector(".record-number"); 476 const organization = postItem.querySelector(".organization"); 477 const jobTitle = postItem.querySelector(".job-title"); 478 const hasImage = 479 !post["og:image"] || 480 (post["og:image"] && 481 !post["og:image"].includes("no-ind-image-available") && 482 !post["og:image"].includes("no-org-image-available")); 483 484 // Set the title 485 heading.insertAdjacentHTML("beforeend", post.title); 486 487 // set custom placeholder image 488 if (isIndividual && !hasImage) { 489 post["og:image"] = ""; 490 } else if (!hasImage) { 491 post["og:image"] = orgPlaceholderImage; 492 } 493 494 if (isIndividual) { 495 ogImage.setAttribute( 496 "class", 497 "og-image relative overflow-hidden shadow bg-center bg-cover bg-no-repeat bg-primary rounded-3xl w-[150px] h-[200px] mx-auto transform translate-y-[-10%]" 498 ); 499 } else { 500 // border-t and pt-12 501 contentElm.classList.add("pt-12", "border-t"); 502 ogImage.style.backgroundSize = "85%"; 503 } 504 505 // Set category 506 if (!isIndividual && category && post.categories) { 507 category.textContent = post.categories 508 .split("|") 509 .join(", "); 510 category.classList.remove("hidden"); 511 } 512 513 // Build address from components 514 if (address) { 515 const addressLines = []; 516 const line1Parts = []; 517 const line2Parts = []; 518 let line2PartsString = ""; 519 520 // First line: address_line1 and address_line2 521 if (post.address_line1) line1Parts.push(post.address_line1); 522 if (post.address_line2) line1Parts.push(post.address_line2); 523 524 // Second line: city, state, zip 525 if (post.address_city) line2Parts.push(post.address_city); 526 if (post.address_state) line2Parts.push(post.address_state); 527 528 // Build the address string 529 if (line1Parts.length > 0) { 530 addressLines.push(line1Parts.join(", ")); 531 } 532 if (line2Parts.length > 0) { 533 line2PartsString = line2Parts.join(", "); 534 if (post.address_zip) { 535 line2PartsString += ` ${post.address_zip}`; 536 } 537 addressLines.push(line2PartsString); 538 } 539 540 if (addressLines.length > 0) { 541 address.innerHTML = addressLines.join("<br>"); 542 address.classList.remove("hidden"); 543 } 544 } 545 546 // Set website 547 if (website && websiteText && post.website) { 548 const websiteUrl = validateWebsite(post.website); 549 if (websiteUrl) { 550 website.href = websiteUrl; 551 websiteText.textContent = websiteUrl; 552 website.parentElement.classList.remove("hidden"); 553 } 554 } 555 556 // Set phone 557 if (phone && post.phone) { 558 phone.textContent = post.phone; 559 phone.classList.remove("hidden"); 560 } 561 562 // Set og:image as background 563 if (isIndividual && !hasImage) { 564 // show nested img placeholder 565 ogImage.querySelector("img").classList.remove("hidden"); 566 } else if (ogImage && post["og:image"]) { 567 ogImage.style.backgroundImage = `url(${post["og:image"]})`; 568 } 569 570 // Set record number 571 if (recordNumber && post.record_number) { 572 recordNumber.textContent = post.record_number; 573 recordNumber.classList.remove("hidden"); 574 } 575 576 // Set organization (trusted HTML) 577 if (organization && post.organization) { 578 organization.insertAdjacentHTML( 579 "beforeend", 580 post.organization 581 ); 582 organization.classList.remove("hidden"); 583 } 584 585 // Set job title 586 if (jobTitle && post.job_title) { 587 jobTitle.textContent = post.job_title; 588 jobTitle.classList.remove("hidden"); 589 } 590
591 docFragment.appendChild(postItem); 592 }); 593 594 document 595 .getElementById("mg-block-directory-feed") 596 .replaceChildren(docFragment); 597 }, 598 599 watchPagination() { 600 const { state, actions } = store("mg-store-directory"); 601 const paginationNumbers = document.querySelector( 602 ".mg-directory-pagination-container" 603 ); 604 605 if (!paginationNumbers) return; 606 607 console.log( 608 "Pagination watcher triggered. Page:", 609 state.page, 610 "Last page:", 611 state.lastPage 612 ); 613 614 // Clear existing pagination numbers 615 paginationNumbers.innerHTML = ""; 616 617 const currentPage = state.page; 618 const lastPage = state.lastPage; 619 const maxVisiblePages = 5; 620 621 // Calculate which pages to show 622 let startPage = Math.max( 623 1, 624 currentPage - Math.floor(maxVisiblePages / 2) 625 ); 626 let endPage = Math.min(lastPage, startPage + maxVisiblePages - 1); 627 628 // Adjust start page if we're near the end 629 if (endPage - startPage < maxVisiblePages - 1) { 630 startPage = Math.max(1, endPage - maxVisiblePages + 1); 631 } 632 633 // Add first page and dots if needed 634 if (startPage > 1) { 635 actions.addPaginationNumber(1, paginationNumbers); 636 if (startPage > 2) { 637 actions.addPaginationDots(paginationNumbers); 638 } 639 } 640 641 // Add visible page numbers 642 for (let i = startPage; i <= endPage; i++) { 643 if (i === currentPage) { 644 actions.addPaginationCurrent(i, paginationNumbers); 645 } else { 646 actions.addPaginationNumber(i, paginationNumbers); 647 } 648 } 649 650 // Add last page and dots if needed 651 if (endPage < lastPage) { 652 if (endPage < lastPage - 1) { 653 actions.addPaginationDots(paginationNumbers); 654 } 655 actions.addPaginationNumber(lastPage, paginationNumbers); 656 } 657 }, 658 659 handleModeSwitch() { 660 const { state, actions, callbacks } = store("mg-store-directory"); 661 const feedGridContainer = document.getElementById( 662 "mg-block-directory-feed" 663 ); 664 const isIndividual = state.mode === "individual"; 665 666 state.page = 1; // Reset to first page 667 668 // fetch data 669 callbacks.fetchDirectory(); 670 callbacks.getAndSetFacets(); 671 672 // Update UI 673 if (isIndividual) { 674 feedGridContainer.classList.remove("gap-y-12"); 675 feedGridContainer.classList.add("gap-y-24", "mt-24"); 676 } else { 677 feedGridContainer.classList.add("gap-y-12"); 678 feedGridContainer.classList.remove("gap-y-24", "mt-24"); 679 } 680 681 actions.updateTabClasses(); 682 actions.updateRadioButtons(); 683 }, 684 685 fetchDirectory(scroll = true) { 686 const { state, actions, callbacks } = store("mg-store-directory"); 687 const loader = document.querySelector( 688 ".mg-block-directory .pulsating-circle" 689 ); 690 const feedContainer = document.getElementById( 691 "mg-block-directory-feed" 692 ); 693 694 // log state name 695 console.log("State title params:", state.params.title); 696 697 // set state to loading 698 state.loading = true; 699 700 if (loader) { 701 loader.classList.remove("hidden"); 702 } 703 704 if (feedContainer) { 705 feedContainer.classList.add("hidden"); 706 } 707 708 // Add name search if provided 709 if (state.params.title && state.params.title.trim() !== "") { 710 state.params.title = state.params.title.trim(); 711 } 712 713 const url = callbacks.buildRequestUrl(); 714 wp.apiFetch({ 715 url: url, 716 parse: false 717 }) 718 .then((response) => { 719 return response.json(); 720 }) 721 .then((data) => { 722 const totalPages = data.lastPage; 723 if (totalPages) { 724 state.lastPage = parseInt(totalPages); 725 } 726 727 state.posts = data.result; 728 }) 729 .catch((error) => { 730 state.posts = []; 731 console.error("Error fetching directory:", error); 732 }) 733 .finally(() => { 734 state.loading = false;
735 736 if (loader) { 737 loader.classList.add("hidden"); 738 } 739 740 if (feedContainer) { 741 feedContainer.classList.remove("hidden"); 742 } 743 744 callbacks.handlePostsUpdate(); 745 callbacks.handleNoResults(); 746 747 if (scroll) { 748 actions.scrollToResults(); 749 } 750 }); 751 }, 752 753 setOptions(payload = []) { 754 const { state } = store("mg-store-directory"); 755 756 if (!Array.isArray(payload)) return; 757 758 for (const item of payload) { 759 if (!isObject(item) || !Array.isArray(item.facet)) continue; 760 const facet = item.facet.length ? item.facet[0] : ""; 761 if (!isObject(facet)) continue; 762 if (facet.name in state.options) { 763 const facetOptions = facet.int; 764 765 if (Array.isArray(facetOptions)) { 766 const filteredOptions = facetOptions.filter( 767 (f_option) => { 768 const optionName = f_option?.name; 769 return ( 770 "string" === typeof optionName && 771 "none" !== optionName.toLowerCase() 772 ); 773 } 774 ); 775 776 state.options[facet.name] = sortBy( 777 filteredOptions, 778 "name" 779 ); 780 } 781 } 782 } 783 }, 784 785 buildRequestUrl() { 786 console.log("Building request url..."); 787 788 const { state, actions } = store("mg-store-directory"); 789 const urlObj = new URL(searchUrl); 790 const query = []; 791 const quoteValues = [ 792 "address_country", 793 "address_state", 794 "categories", 795 "address_city" 796 ]; 797 let zip = state.zip; 798 const radius = state.radius; 799 const page = state.page; 800 const mode = state.mode; 801 802 query.push(`record_type:"${mode}"`); 803 804 for (const key in state.params) { 805 const isLastName = "last_name" === key; 806 const isFirstName = "first_name" === key; 807 const isTitle = "title" === key; 808 let value = state.params[key]; 809 810 if (isLastName && value) { 811 value = value.replace(/-/g, " "); 812 } 813 814 if ((isLastName || isFirstName || isTitle) && value) { 815 //if lastname contains ' (like o'malley) then wrap it in double quotes 816 if ( 817 value.includes("'") && 818 !(value.startsWith("'") && value.endsWith("'")) 819 ) { 820 value = `"${value}"`; 821 } 822 } 823 824 if (Array.isArray(value) && value.length) { 825 const valueEncoded = value.map((v) => { 826 return `"${v}"`; 827 }); 828 query.push(`${key}:(${valueEncoded.join(" OR ")})`); 829 continue; 830 } 831 832 let valueEncoded = encodeURIComponent(value); 833 const maybeNeedsAdjustment = [ 834 "first_name", 835 "last_name", 836 "organization", 837 "title" 838 ]; 839 840 if (maybeNeedsAdjustment.includes(key)) { 841 const valueIsQuoted = 842 "string" == typeof value && 843 ((value.startsWith(`"`) && value.endsWith(`"`)) || 844 (value.startsWith(`'`) && value.endsWith(`'`))); 845 846 if (value && !value.endsWith("*") && !valueIsQuoted) { 847 valueEncoded += "*"; 848 } 849 } 850 851 if (value && quoteValues.includes(key)) { 852 query.push(`${key}:"${valueEncoded}"`); 853 } else if (value) { 854 query.push(`${key}:${valueEncoded}`); 855 } 856 } 857 858 if (zip) { 859 zip = zip.substring(0, 5); 860 861 // if (!radius) { 862 // query.push(`address_zip:${zip}`); 863 // } 864 865 urlObj.searchParams.append("zip", zip); 866 } 867 868 urlObj.searchParams.append("col", searchCollectionId); 869 urlObj.searchParams.append("pagesize", 9); 870 urlObj.searchParams.append("page", page); 871 urlObj.searchParams.append("filter", query.join(" AND ")); 872 873 if (zip && radius) { 874 urlObj.searchParams.append("radius", radius); 875 } 876
877 const request = new Request(urlObj); 878 return request.url; 879 }, 880 881 getAndSetFacets() { 882 console.log("Building request url for facets..."); 883 const { state, callbacks } = store("mg-store-directory"); 884 const urlObj = new URL(facetsUrl); 885 const query = []; 886 const mode = state.mode; 887 888 query.push(`record_type:"${mode}"`); 889 urlObj.searchParams.append("filter", query.join(" AND ")); 890 891 const request = new Request(urlObj); 892 const url = request.url; 893 894 wp.apiFetch({ 895 url: url, 896 parse: false 897 }) 898 .then((response) => { 899 return response.json(); 900 }) 901 .then((data) => { 902 if (data.facets) { 903 const facets = data.facets; 904 callbacks.setOptions(facets); 905 } 906 }) 907 .catch((error) => { 908 console.error("Error fetching facets:", error); 909 }) 910 .finally(() => { 911 // log state options 912 // console.log("State options:", state.options); 913 }); 914 }, 915 916 handleNoResults() { 917 const { state } = store("mg-store-directory"); 918 const pagination = document.querySelector( 919 ".wp-block-query-pagination" 920 ); 921 const noResultsMessage = document.querySelector( 922 ".mg-directory-no-results-message" 923 ); 924 925 // Check if we have results 926 const hasResults = 927 Array.isArray(state.posts) && state.posts.length > 0; 928 929 if (hasResults) { 930 // Show pagination, hide no results message 931 if (pagination) { 932 pagination.classList.remove("hidden"); 933 } 934 if (noResultsMessage) { 935 noResultsMessage.classList.add("hidden"); 936 } 937 } else { 938 // Hide pagination, show no results message 939 if (pagination) { 940 pagination.classList.add("hidden"); 941 } 942 if (noResultsMessage) { 943 noResultsMessage.classList.remove("hidden"); 944 } 945 } 946 }, 947 948 parseMyText: () => { 949 const element = getElement(); 950 const domNode = element.ref; 951 const html = parseHtmlText(domNode.textContent); 952 domNode.textContent = html; 953 } 954 } 955});
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.