1export default function fancyTreeFrontEnd() { 2 const pageHasFancyTree = 3 document.querySelector(".insights_custom_filters .insights_filters") || 4 document.querySelector('[id$="_fancy"]'); 5 6 if (!pageHasFancyTree) return; 7 if (window.__gdmFancyTreeFrontEndInitialized) return; 8 9 const getJQuery = () => window.jQuery; 10 const hasFancyTreePlugin = () => { 11 const $ = getJQuery(); 12 return Boolean($ && $.ui && $.ui.fancytree); 13 }; 14 15 const initialize = () => { 16 const $ = getJQuery(); 17 if (!$) return; 18 19 window.__gdmFancyTreeFrontEndInitialized = true; 20 21 function isLegacyStaticTree($treeDiv) { 22 if (!$treeDiv || !$treeDiv.length) return false; 23 return ( 24 !$treeDiv.data("ui-fancytree") && 25 $treeDiv.children("ul.ui-fancytree, ul.fancytree-container").length > 0 26 ); 27 } 28 29 function getFilterLabelText($li) { 30 if (!$li || !$li.length) return ""; 31 32 const dataLabel = ($li.attr("data-os-facet-data") || "").trim(); 33 if (dataLabel) return dataLabel; 34 35 const $title = $li.find(".fancytree-title").first().clone(); 36 if ($title.length) { 37 $title.find(".count").remove(); 38 return ($title.text() || "").trim(); 39 } 40 41 return ($li.text() || "").trim(); 42 } 43 44 function compareFilterLabels(a, b) { 45 return a.localeCompare(b, undefined, { 46 sensitivity: "base", 47 numeric: true, 48 }); 49 } 50 51 function normalizeListAlphabetical($list) { 52 if (!$list || !$list.length) return; 53 54 const items = $list.children("li").get(); 55 if (!items.length) return; 56 57 items.sort((a, b) => { 58 const labelA = getFilterLabelText($(a)); 59 const labelB = getFilterLabelText($(b)); 60 return compareFilterLabels(labelA, labelB); 61 }); 62 63 $list.append(items); 64 } 65 66 function normalizeTreeContainerAlphabetical($treeContainer) { 67 if (!$treeContainer || !$treeContainer.length) return; 68 69 normalizeListAlphabetical($treeContainer); 70 } 71 72 function insertFilterInAlphabeticalOrder($targetList, $li) { 73 if (!$targetList || !$targetList.length || !$li || !$li.length) return; 74 75 const currentLabel = getFilterLabelText($li); 76 let inserted = false; 77 78 $targetList.children("li").each(function () { 79 if (this === $li[0]) return; 80 81 const compareLabel = getFilterLabelText($(this)); 82 if (compareFilterLabels(currentLabel, compareLabel) < 0) { 83 $(this).before($li); 84 inserted = true; 85 return false; 86 } 87 }); 88 89 if (!inserted) { 90 $targetList.append($li); 91 } 92 } 93 94 function rememberLegacyOriginList($li, $treeDiv) { 95 if (!$li || !$li.length || !$treeDiv || !$treeDiv.length) return; 96 97 const $originList = $li.parent("ul"); 98 if (!$originList.length || $originList.hasClass("selected-filters")) 99 return; 100 101 $li.data("gdmLegacyOriginListEl", $originList.get(0)); 102 $li.data("gdmLegacyOriginTreeId", $treeDiv.attr("id") || ""); 103 } 104 105 function getLegacyRestoreList($li, $treeDiv, $treeContainer) { 106 if (!$li || !$li.length || !$treeDiv || !$treeDiv.length) { 107 return $treeContainer; 108 } 109 110 const originListEl = $li.data("gdmLegacyOriginListEl"); 111 const originTreeId = $li.data("gdmLegacyOriginTreeId"); 112 113 if ( 114 originListEl && 115 originTreeId === ($treeDiv.attr("id") || "") && 116 $.contains($treeDiv.get(0), originListEl) 117 ) { 118 return $(originListEl); 119 } 120 121 return $treeContainer; 122 } 123 124 function ensureLegacyNodeId($li) { 125 if (!$li || !$li.length) return ""; 126 127 let nodeId = $li.attr("data-gdm-legacy-node-id") || ""; 128 if (nodeId) return nodeId; 129 130 const counter = (window.__gdmLegacyNodeCounter || 0) + 1; 131 window.__gdmLegacyNodeCounter = counter; 132 nodeId = "legacy-node-" + counter; 133 $li.attr("data-gdm-legacy-node-id", nodeId); 134 135 return nodeId; 136 } 137 138 function getSelectedDuplicate($selectedList, sourceNodeId) { 139 if (!$selectedList || !$selectedList.length || !sourceNodeId) return $(); 140 return $selectedList 141 .children( 142 'li[data-gdm-legacy-duplicate="1"][data-gdm-legacy-source-id="' + 143 sourceNodeId + 144 '"]', 145 ) 146 .first(); 147 } 148 149 function cloneFilterListItem($sourceLi) { 150 if (!$sourceLi || !$sourceLi.length) return $(); 151 152 const $clone = $sourceLi.clone(false, false); 153 $clone.find("ul, li").remove(); 154 return $clone; 155 } 156 157 function addSelectedDuplicate($sourceLi, $selectedList) { 158 if ( 159 !$sourceLi || 160 !$sourceLi.length || 161 !$selectedList || 162 !$selectedList.length 163 ) 164 return; 165 166 const sourceNodeId = ensureLegacyNodeId($sourceLi); 167 if (!sourceNodeId) return; 168 169 let $duplicate = getSelectedDuplicate($selectedList, sourceNodeId); 170 if ($duplicate.length) { 171 $duplicate.attr("aria-selected", "true"); 172 $duplicate.find(".fancytree-node").addClass("fancytree-selected"); 173 return; 174 } 175 176 $duplicate = cloneFilterListItem($sourceLi); 177 $duplicate.attr("data-gdm-legacy-duplicate", "1"); 178 $duplicate.attr("data-gdm-legacy-source-id", sourceNodeId); 179 $duplicate.removeAttr("data-gdm-legacy-node-id"); 180 $duplicate.attr("aria-selected", "true"); 181 $duplicate.find(".fancytree-node").addClass("fancytree-selected"); 182 183 $selectedList.append($duplicate); 184 } 185 186 function removeSelectedDuplicate($selectedList, sourceNodeId) { 187 if (!$selectedList || !$selectedList.length || !sourceNodeId) return; 188 getSelectedDuplicate($selectedList, sourceNodeId).remove(); 189 } 190 191 function syncLegacyLocationState($treeDiv, $sourceLi, wasSelected) { 192 if (!$treeDiv.is("#country_str_fancy") || !$sourceLi.length) return; 193 194 const level = $sourceLi.attr("data-loc-level"); 195 if (wasSelected) { 196 $sourceLi 197 .parents("#country_str_fancy li[data-loc-level]") 198 .removeClass("loc-collapsed"); 199 $sourceLi.removeClass("loc-collapsed"); 200 } else if (level === "country" || level === "region") { 201 $sourceLi.removeClass("loc-collapsed"); 202 } 203 204 $treeDiv 205 .find("li.loc-ancestor-active") 206 .removeClass("loc-ancestor-active") 207 .children(".fancytree-node") 208 .removeClass("fancytree-ancestor-selected"); 209 $treeDiv 210 .find('li[data-loc-level="region"][aria-selected="true"]') 211 .each(function () { 212 $(this) 213 .parent() 214 .closest('li[data-loc-level="country"]') 215 .addClass("loc-ancestor-active") 216 .children(".fancytree-node") 217 .addClass("fancytree-ancestor-selected"); 218 }); 219 $treeDiv 220 .find('li[data-loc-level="city"][aria-selected="true"]') 221 .each(function () { 222 $(this) 223 .parent() 224 .closest('li[data-loc-level="region"]') 225 .addClass("loc-ancestor-active") 226 .children(".fancytree-node") 227 .addClass("fancytree-ancestor-selected"); 228 $(this) 229 .parents() 230 .closest('li[data-loc-level="country"]') 231 .addClass("loc-ancestor-active") 232 .children(".fancytree-node") 233 .addClass("fancytree-ancestor-selected"); 234 }); 235 236 const syncedCountries = []; 237 $treeDiv 238 .find('li[aria-selected="true"][data-loc-level="country"]') 239 .each(function () { 240 const country = $(this).attr("data-os-facet-data"); 241 if (country) syncedCountries.push(country); 242 }); 243 window.analyticsSelectedCountry = syncedCountries; 244 if (typeof window.renderAnalyticsLocationTree === "function") { 245 window.analyticsSelectedCountry = syncedCountries; 246 window.renderAnalyticsLocationTree(syncedCountries); 247 } 248 window.analyticsLoaded = false;
249 250 const hasSelected = $treeDiv.find('li[aria-selected="true"]').length > 0; 251 if (!hasSelected && typeof window.renderLocationTree === "function") { 252 window.renderLocationTree($treeDiv); 253 } 254 255 if ( 256 level === "region" && 257 !$sourceLi.hasClass("loc-collapsed") && 258 $sourceLi.children("ul.cities-placeholder").length && 259 typeof window.populateRegionCities === "function" 260 ) { 261 window.populateRegionCities( 262 $sourceLi.attr("data-country"), 263 $sourceLi.attr("data-os-facet-data"), 264 $sourceLi, 265 ); 266 } 267 } 268 269 function bindLegacySelectedFiltersOverride() { 270 if (window.__gdmLegacySelectedFiltersOverrideBound) return; 271 272 document.addEventListener( 273 "click", 274 (event) => { 275 const $targetLi = $(event.target).closest('[id$="_fancy"] li'); 276 if (!$targetLi.length) return; 277 278 // gd-jobs templates bind their own static tree click behavior 279 // (see search-view.php). Keep this guard so legacy interception 280 // never races/steals their clicks, or gd-jobs AJAX filtering and 281 // URL syncing silently stop firing. 282 if ($targetLi.closest('[data-gd-jobs-filters="true"]').length) return; 283 284 const $treeDiv = $targetLi.closest('[id$="_fancy"]'); 285 // Let an initialized FancyTree widget handle its own nodes. Static 286 // markup may still use FancyTree classes without being widget-owned. 287 if ($treeDiv.data("ui-fancytree")) return; 288 289 if (!isLegacyStaticTree($treeDiv)) return; 290 291 const $selected = $treeDiv.children("ul.selected-filters").first(); 292 const $treeContainer = $treeDiv 293 .children("ul.ui-fancytree, ul.fancytree-container") 294 .first(); 295 if (!$selected.length || !$treeContainer.length) return; 296 297 // Capture-phase interception avoids conflicting inline delegated handlers. 298 event.preventDefault(); 299 event.stopPropagation(); 300 if (typeof event.stopImmediatePropagation === "function") { 301 event.stopImmediatePropagation(); 302 } 303 304 const wasSelected = $targetLi.attr("aria-selected") === "true"; 305 const clickedInsideSelectedList = 306 $targetLi.closest("ul.selected-filters").length > 0; 307 const clickedDuplicate = 308 $targetLi.attr("data-gdm-legacy-duplicate") === "1"; 309 const facetType = 310 $targetLi.attr("data-os-industry_type") || 311 ($treeDiv.attr("id") || "").replace(/_fancy$/, ""); 312 const isSingleSelectFacet = 313 facetType === "posted_date_str" || 314 facetType === "remote_status_str"; 315 const $sourceLi = clickedDuplicate 316 ? $treeDiv 317 .find( 318 'li[data-gdm-legacy-node-id="' + 319 ($targetLi.attr("data-gdm-legacy-source-id") || "") + 320 '"]', 321 ) 322 .first() 323 : $targetLi; 324 325 if ( 326 !clickedInsideSelectedList && 327 !wasSelected && 328 isSingleSelectFacet 329 ) { 330 $selected.children('li[aria-selected="true"]').each(function () { 331 const $selectedItem = $(this); 332 $selectedItem 333 .attr("aria-selected", "false") 334 .find(".fancytree-node") 335 .removeClass("fancytree-selected"); 336 $treeContainer.append($selectedItem); 337 }); 338 } 339 340 if (clickedInsideSelectedList || wasSelected) { 341 if (clickedDuplicate) { 342 if ($sourceLi.length) { 343 $sourceLi.attr("aria-selected", "false"); 344 $sourceLi 345 .find(".fancytree-node") 346 .removeClass("fancytree-selected"); 347 } 348 $targetLi.remove(); 349 } else { 350 const sourceId = ensureLegacyNodeId($targetLi); 351 $targetLi.attr("aria-selected", "false"); 352 $targetLi 353 .find(".fancytree-node") 354 .removeClass("fancytree-selected"); 355 removeSelectedDuplicate($selected, sourceId); 356 357 const $restoreList = getLegacyRestoreList( 358 $targetLi, 359 $treeDiv, 360 $treeContainer, 361 ); 362 insertFilterInAlphabeticalOrder($restoreList, $targetLi); 363 normalizeListAlphabetical($restoreList); 364 normalizeTreeContainerAlphabetical($treeContainer); 365 } 366 } else {
367 rememberLegacyOriginList($targetLi, $treeDiv); 368 $targetLi.attr("aria-selected", "true"); 369 $targetLi.find(".fancytree-node").addClass("fancytree-selected"); 370 addSelectedDuplicate($targetLi, $selected); 371 } 372 373 syncLegacyLocationState( 374 $treeDiv, 375 $sourceLi, 376 clickedInsideSelectedList || wasSelected, 377 ); 378 379 const hasSelected = 380 $treeContainer.find('li[aria-selected="true"]').length > 0; 381 382 if (facetType) { 383 $(".clear_filters_" + facetType) 384 .toggleClass("show", hasSelected) 385 .css("display", ""); 386 $(".no_filters_" + facetType).toggle(!hasSelected); 387 refreshSearchResultsAfterClear(facetType); 388 } 389 390 $selected.toggleClass("show", $selected.children("li").length > 0); 391 $(document).trigger("fancytree-filter-change"); 392 }, 393 true, 394 ); 395 396 window.__gdmLegacySelectedFiltersOverrideBound = true; 397 } 398 399 function clearLegacyFilters(type) { 400 const treeId = type + "_fancy"; 401 const $treeDiv = $("#" + treeId); 402 if (!isLegacyStaticTree($treeDiv)) return; 403 404 const $selected = $treeDiv.children("ul.selected-filters").first(); 405 const $treeContainer = $treeDiv 406 .children("ul.ui-fancytree, ul.fancytree-container") 407 .first(); 408 if (!$selected.length || !$treeContainer.length) return; 409 410 $treeContainer.find('li[aria-selected="true"]').each(function () { 411 const $li = $(this); 412 const sourceId = ensureLegacyNodeId($li); 413 414 $li.attr("aria-selected", "false"); 415 $li.find(".fancytree-node").removeClass("fancytree-selected"); 416 removeSelectedDuplicate($selected, sourceId); 417 418 const $restoreList = getLegacyRestoreList( 419 $li, 420 $treeDiv, 421 $treeContainer, 422 ); 423 insertFilterInAlphabeticalOrder($restoreList, $li); 424 normalizeListAlphabetical($restoreList); 425 }); 426 normalizeTreeContainerAlphabetical($treeContainer); 427 428 $selected.children('li[data-gdm-legacy-duplicate="1"]').remove(); 429 430 $selected.toggleClass("show", false); 431 $(".no_filters_" + type).css("display", "block"); 432 $(".clear_filters_" + type).removeClass("show"); 433 434 $(document).trigger("fancytree-filter-change"); 435 } 436 437 function myInishtsFacetfilter() { 438 $(".insights_custom_filters .insights_filters").addClass("open-filters"); 439 } 440 441 function myInishtsFacetfilterClose() { 442 $(".insights_custom_filters .insights_filters").removeClass( 443 "open-filters", 444 ); 445 } 446 447 function initFilterGroupState() { 448 const $groups = $(".insights_filters .filter-group"); 449 450 $groups.each(function () { 451 const $group = $(this); 452 const $pane = $group.find(".dropdown-pane").first(); 453 const $header = $group.find(".fancytree-header").first(); 454 455 if (!$pane.length) return; 456 457 $pane.removeClass("open"); 458 $group.removeClass("open"); 459 $header.removeClass("open"); 460 }); 461 } 462 463 $(document).on( 464 "click", 465 ".insights_filters .filter-group .fancytree-header", 466 function (event) { 467 const $header = $(this); 468 const $group = $header.closest(".filter-group"); 469 const $pane = $group.find(".dropdown-pane").first(); 470 if (!$group.length || !$pane.length) return; 471 472 const $allGroups = $group 473 .closest(".insights_filters") 474 .find(".filter-group"); 475 const $allHeaders = $allGroups.find(".fancytree-header"); 476 477 $allGroups.not($group).removeClass("open"); 478 $allHeaders.not($header).removeClass("open"); 479 $allGroups.not($group).find(".dropdown-pane").removeClass("open"); 480 481 const isOpen = $group.hasClass("open"); 482 483 if (isOpen) { 484 $group.removeClass("open"); 485 $header.removeClass("open"); 486 $pane.removeClass("open"); 487 } else { 488 $group.addClass("open"); 489 $header.addClass("open"); 490 $pane.addClass("open"); 491 } 492 493 event.preventDefault(); 494 event.stopPropagation(); 495 }, 496 ); 497 498 function getTreeIfInitialized(treeId) { 499 const $treeDiv = $("#" + treeId); 500 if (!$treeDiv.length) return null; 501 if (!$treeDiv.data("ui-fancytree")) return null; 502
503 try { 504 return $treeDiv.fancytree("getTree") || null; 505 } catch (error) { 506 return null; 507 } 508 } 509 510 function ensureSelectedFiltersList(treeId) { 511 const $treeDiv = $("#" + treeId); 512 if (!$treeDiv.length) return $(); 513 514 const $treeContainer = $treeDiv.find("> ul.fancytree-container").first(); 515 if (!$treeContainer.length) return $(); 516 517 let $selected = $treeDiv.find("> ul.selected-filters").first(); 518 if (!$selected.length) { 519 $selected = $('<ul class="selected-filters"></ul>'); 520 $treeContainer.before($selected); 521 } 522 523 return $selected; 524 } 525 526 function getNodeSourceKey(node) { 527 if (!node) return ""; 528 529 const explicitKey = 530 node.key || node.data?.id || node.data?.value || node.data?.slug || ""; 531 if (explicitKey) return String(explicitKey); 532 533 const sourceText = (node.title || "").trim(); 534 if (sourceText) return sourceText; 535 536 const $li = node.li ? $(node.li) : $(); 537 return ($li.attr("data-os-facet-data") || $li.text() || "").trim(); 538 } 539 540 function buildSelectedFilterSummaryItem(node) { 541 const $sourceLi = node && node.li ? $(node.li) : $(); 542 if (!$sourceLi.length) return $(); 543 544 const sourceKey = getNodeSourceKey(node); 545 const $clone = cloneFilterListItem($sourceLi); 546 $clone.removeAttr( 547 "data-gdm-legacy-duplicate data-gdm-legacy-source-id data-gdm-legacy-node-id", 548 ); 549 $clone.attr({ 550 "data-gdm-selected-source-key": sourceKey, 551 "data-gdm-selected-summary": "1", 552 "aria-selected": "true", 553 }); 554 $clone.find(".fancytree-node").addClass("fancytree-selected"); 555 $clone.find(".fancytree-checkbox").attr("aria-checked", "true"); 556 557 return $clone; 558 } 559 560 // Non-selected copy of an ancestor node, used purely to group a selected 561 // child under it in ul.selected-filters (see buildNestedSelectedFilters). 562 function buildSelectedFilterWrapperItem(node) { 563 const $clone = buildSelectedFilterSummaryItem(node); 564 if (!$clone.length) return $clone; 565 $clone.removeAttr("data-gdm-selected-source-key"); 566 $clone 567 .attr("aria-selected", "false") 568 .find(".fancytree-node") 569 .removeClass("fancytree-selected"); 570 $clone.find(".fancytree-checkbox").attr("aria-checked", "false"); 571 return $clone; 572 } 573 574 // Build ul.selected-filters as a tree mirroring each selected node's 575 // ancestry, so a selected child is grouped under its (non-selected) 576 // parent instead of listed as a flat sibling of unrelated selections. 577 function buildNestedSelectedFilters(tree, $selected) { 578 const wrapperLists = new Map(); 579 580 function ensureWrapperList(ancestorNode) { 581 if (wrapperLists.has(ancestorNode.key)) { 582 return wrapperLists.get(ancestorNode.key); 583 } 584 const grandParents = ancestorNode.getParentList(false, false); 585 const $parentUl = grandParents.length 586 ? ensureWrapperList(grandParents[grandParents.length - 1]) 587 : $selected; 588 const $wrapper = buildSelectedFilterWrapperItem(ancestorNode); 589 const $ul = $("<ul></ul>"); 590 $wrapper.append($ul); 591 $parentUl.append($wrapper); 592 wrapperLists.set(ancestorNode.key, $ul); 593 return $ul; 594 } 595 596 tree.getSelectedNodes().forEach((node) => { 597 if (!node || !node.li) return; 598 const $clone = buildSelectedFilterSummaryItem(node); 599 if (!$clone.length) return; 600 601 $(node.li).attr("data-gdm-source-key", getNodeSourceKey(node)); 602 603 const parents = node.getParentList(false, false); 604 const $targetUl = parents.length 605 ? ensureWrapperList(parents[parents.length - 1]) 606 : $selected; 607 $targetUl.append($clone); 608 }); 609 } 610 611 function syncSelectedFiltersList(treeId) { 612 const $treeDiv = $("#" + treeId); 613 if (!$treeDiv.length) return; 614 615 const tree = getTreeIfInitialized(treeId); 616 if (!tree) return; 617 618 const $treeContainer = $treeDiv.find("> ul.fancytree-container").first(); 619 if (!$treeContainer.length) return; 620 621 const $selected = ensureSelectedFiltersList(treeId); 622 if (!$selected.length) return; 623 624 $selected.empty(); 625 buildNestedSelectedFilters(tree, $selected); 626 627 normalizeListAlphabetical($selected); 628 $selected.toggleClass("show", $selected.children("li").length > 0); 629 } 630 631 function bindSelectedFiltersDeselect(treeId) { 632 const $treeDiv = $("#" + treeId); 633 $treeDiv 634 .off("click.selectedFilters") 635 .on("click.selectedFilters", "ul.selected-filters li", function (e) { 636 e.preventDefault(); 637 e.stopPropagation(); 638 639 const $selectedItem = $(this); 640 const sourceKey = $selectedItem.attr("data-gdm-selected-source-key"); 641 if (!sourceKey) return; 642 643 const $sourceLi = $treeDiv 644 .find('li[data-gdm-source-key="' + sourceKey + '"]') 645 .first(); 646 const node = 647 $sourceLi.length && $.ui && $.ui.fancytree 648 ? $.ui.fancytree.getNode($sourceLi[0]) 649 : null; 650 651 if (node) { 652 node.setSelected(false); 653 } else if ($sourceLi.length) { 654 $sourceLi.attr("aria-selected", "false"); 655 $sourceLi.find(".fancytree-node").removeClass("fancytree-selected"); 656 } 657 658 $selectedItem.remove(); 659 syncSelectedFiltersList(treeId); 660 const type = treeId.replace(/_fancy$/, ""); 661 getRefCount(treeId); 662 refreshSearchResultsAfterClear(type); 663 }); 664 665 $treeDiv 666 .off("click.selectedFiltersCheckbox") 667 .on( 668 "click.selectedFiltersCheckbox", 669 "ul.selected-filters li .fancytree-checkbox", 670 function (e) { 671 e.preventDefault(); 672 const node = $.ui.fancytree.getNode(this); 673 if (node) { 674 node.setSelected(false); 675 syncSelectedFiltersList(treeId); 676 const type = treeId.replace(/_fancy$/, ""); 677 getRefCount(treeId); 678 refreshSearchResultsAfterClear(type); 679 } 680 }, 681 ); 682 } 683 684 function sortChildren(a, b) { 685 const aLabel = (a.title || "").trim(); 686 const bLabel = (b.title || "").trim(); 687 return compareFilterLabels(aLabel, bLabel); 688 } 689 690 function getRefCount(treeId) { 691 if (!treeId) return; 692 693 const treeClass = treeId.replace("_fancy", ""); 694 const treeContainerId = treeId.replace("_fancy", "_fancytree"); 695 const tree = getTreeIfInitialized(treeId); 696 if (!tree) return; 697 698 const totalSelectedCount = tree.getSelectedNodes().length; 699 const $container = $("#" + treeContainerId); 700 701 if (totalSelectedCount !== 0) { 702 $container 703 .find(".clear_filters_" + treeClass) 704 .removeClass("fecet_button_hide"); 705 $container.find(".no_filters_" + treeClass).css("display", "none"); 706 } else { 707 $container 708 .find(".clear_filters_" + treeClass) 709 .addClass("fecet_button_hide"); 710 $container.find(".no_filters_" + treeClass).css("display", "block"); 711 } 712 } 713 714 function refreshSearchResultsAfterClear(type) { 715 const isJobsFilterPage = 716 $(".insights_custom_filters[data-gd-jobs-filters='true']").length > 0; 717 718 if (isJobsFilterPage) { 719 return; 720 } 721 722 if (typeof window.data_refresh_after_ajax === "function") { 723 window.data_refresh_after_ajax(type, ""); 724 return; 725 } 726 727 // Jobs pages wire filtering through #search-form submit handlers. 728 const $searchForm = $("#search-form"); 729 if ($searchForm.length) { 730 $searchForm.trigger("submit"); 731 return; 732 } 733 734 // Fallback for pages exposing a direct filter refresh function. 735 if (typeof window.performFilter === "function") { 736 window.performFilter(); 737 } 738 } 739 740 function clear_filters(type) { 741 removeFilterGroupParamsFromUrl(type); 742 743 const treeId = type + "_fancy"; 744 const tree = getTreeIfInitialized(treeId); 745 if (!tree) { 746 clearLegacyFilters(type); 747 refreshSearchResultsAfterClear(type); 748 return; 749 } 750 751 tree.visit((node) => { 752 node.setSelected(false); 753 }); 754 755 setTimeout(() => { 756 syncSelectedFiltersList(treeId); 757 getRefCount(treeId); 758 }, 0); 759 760 refreshSearchResultsAfterClear(type); 761 762 $(".no_filters_" + type).css("display", "block"); 763 $(".clear_filters_" + type).removeClass("show"); 764 } 765 766 function getUrlParamKeysForFilterType(type) { 767 if (!type) return []; 768 769 const baseKey = type.replace(/_str$/, "");
770 const keys = new Set([type, baseKey]); 771 772 // Location filter tree can write multiple related params. 773 if (type === "country_str" || baseKey === "country") { 774 keys.add("country"); 775 keys.add("region"); 776 keys.add("city"); 777 } 778 779 return Array.from(keys); 780 } 781 782 function removeFilterGroupParamsFromUrl(type) { 783 if (!type || typeof window === "undefined") return; 784 785 const url = new URL(window.location.href); 786 const keys = getUrlParamKeysForFilterType(type); 787 if (!keys.length) return; 788 789 let changed = false; 790 keys.forEach((key) => { 791 if (url.searchParams.has(key)) { 792 url.searchParams.delete(key); 793 changed = true; 794 } 795 }); 796 797 if (!changed) return; 798 799 const nextUrl = url.pathname + (url.search ? url.search : "") + url.hash; 800 window.history.replaceState({}, "", nextUrl); 801 } 802 803 function initFacetTree(baseId, facetData) { 804 if (!hasFancyTreePlugin()) return; 805 if (typeof window.buildfancytreejson !== "function") return; 806 if (!Array.isArray(facetData) || !facetData.length) return; 807 808 const isSingleSelectFacet = 809 baseId === "posted_date_str" || baseId === "remote_status_str"; 810 811 const treeId = baseId + "_fancy"; 812 const $treeElement = $("#" + treeId); 813 if (!$treeElement.length) return; 814 if ($treeElement.data("ui-fancytree")) return; 815 816 $treeElement.fancytree({ 817 extensions: ["filter"], 818 quicksearch: true, 819 checkbox: true, 820 selectMode: isSingleSelectFacet ? 1 : 2, 821 source: window.buildfancytreejson(facetData), 822 filter: { 823 autoApply: true, 824 autoExpand: true, 825 counter: false, 826 fuzzy: false, 827 hideExpandedCounter: true, 828 hideExpanders: false, 829 highlight: true, 830 leavesOnly: false, 831 nodata: true, 832 mode: "hide", 833 }, 834 click(event, data) { 835 if ( 836 data.targetType === "title" || 837 data.targetType === "icon" || 838 data.targetType === "checkbox" 839 ) { 840 data.node.toggleSelected(); 841 842 if (data.node.isSelected()) { 843 // Only the exact node just picked stays selected within its 844 // branch: picking a child supersedes its parent's own selection 845 // and picking a parent supersedes any of its children. 846 data.node.getParentList(false, false).forEach((ancestor) => { 847 ancestor.setSelected(false); 848 }); 849 data.node.visit((child) => { 850 child.setSelected(false); 851 }); 852 } 853 854 // Run sync directly from click to avoid first-click timing issues 855 // where select callbacks can run before DOM movement is ready. 856 const currentTreeId = data.tree.$div.attr("id"); 857 setTimeout(() => { 858 syncSelectedFiltersList(currentTreeId); 859 getRefCount(currentTreeId); 860 }, 0); 861 862 // Trigger the results/URL refresh ourselves: returning false below 863 // stops the click from bubbling to gd-opensearch-public.js's own 864 // delegated ".fancytree-checkbox" listener, and data-os-facet-data 865 // is only set ~100ms after render, so read the facet name straight 866 // off the node data instead of relying on that DOM attribute. 867 if (typeof window.data_refresh_after_ajax === "function") { 868 const facetName = data.node.data.facetName || data.node.title; 869 window.data_refresh_after_ajax(baseId, facetName); 870 } 871 872 return false; 873 } 874 }, 875 select(event, data) { 876 const currentTreeId = data.tree.$div.attr("id"); 877 setTimeout(() => { 878 syncSelectedFiltersList(currentTreeId); 879 getRefCount(currentTreeId); 880 }, 0); 881 }, 882 renderTitle(event, data) { 883 const title = data.node.title; 884 const docCount = data.node.data.doc_count; 885 if (docCount) { 886 return ( 887 "<span class='fancytree-title'>" + 888 title + 889 "<span class='count'>(" + 890 docCount + 891 ")</span></span>" 892 ); 893 } 894 return "<span class='fancytree-title'>" + title + "</span>"; 895 }, 896 renderNode(event, data) { 897 setTimeout(() => { 898 const nodeTitle = data.node.title; 899 const safeSlug = 900 typeof window.slugify === "function" 901 ? window.slugify(nodeTitle) 902 : ""; 903 904 if (safeSlug) { 905 $(data.node.li).addClass(safeSlug); 906 } 907 908 $(data.node.li).attr({ 909 "data-os-facet-data": nodeTitle, 910 "data-os-industry_type": baseId, 911 "data-os-doc_count": data.node.data.doc_count, 912 }); 913 }, 100); 914 }, 915 }); 916 917 const rootNode = $treeElement.fancytree("getRootNode"); 918 rootNode.sortChildren(sortChildren, true); 919 bindSelectedFiltersDeselect(treeId); 920 syncSelectedFiltersList(treeId); 921 normalizeTreeContainerAlphabetical( 922 $treeElement.find("> ul.fancytree-container").first(), 923 ); 924 getRefCount(treeId); 925 } 926 927 function toggleNoDataAndLoadMore() { 928 const currentCount = parseInt( 929 $(".res_info strong:first").text().trim(), 930 10, 931 ); 932 const totalCount = parseInt( 933 $(".res_info strong:nth-child(2)").text().trim(), 934 10, 935 ); 936 937 if (Number.isNaN(currentCount) || Number.isNaN(totalCount)) return; 938 939 if (currentCount === 0 || totalCount === 0) {
940 $(".nodata-insights").css("display", "block"); 941 $(".nodata-insights").html("No Data Found"); 942 } 943 944 if (currentCount === totalCount) { 945 $("#load-more-archives, #load-more-insights") 946 .prop("disabled", true) 947 .attr("aria-disabled", "true"); 948 } else { 949 $("#load-more-archives, #load-more-insights") 950 .prop("disabled", false) 951 .removeAttr("aria-disabled"); 952 } 953 } 954 955 function runFancyTreeInit() { 956 toggleNoDataAndLoadMore(); 957 initFilterGroupState(); 958 959 const facetIds = [ 960 "themes_insights_str", 961 "project_stage_str", 962 "construction_type_str", 963 "building_size_str", 964 "services_str", 965 "security_str", 966 "amenities_str", 967 ]; 968 969 const facetConfigs = facetIds 970 .map((id) => ({ id, data: window[id + "_arr"] })) 971 .filter( 972 (config) => Array.isArray(config.data) && config.data.length > 0, 973 ); 974 975 facetConfigs.forEach((config) => { 976 initFacetTree(config.id, config.data); 977 }); 978 979 const ownerDefaults = [ 980 "Market Leaders", 981 "Enterprise & Compliance", 982 "Research Innovations", 983 ]; 984 const ownerTree = hasFancyTreePlugin() 985 ? getTreeIfInitialized("themes_insights_str_fancy") 986 : null; 987 988 if (ownerTree) { 989 ownerTree.getRootNode().visit((node) => { 990 if (ownerDefaults.indexOf(node.title) !== -1) { 991 node.setSelected(true); 992 } 993 }); 994 995 setTimeout(() => { 996 syncSelectedFiltersList("themes_insights_str_fancy"); 997 getRefCount("themes_insights_str_fancy"); 998 }, 0); 999 } 1000 } 1001 1002 window.myInishtsFacetfilter = myInishtsFacetfilter; 1003 window.myInishtsFacetfilterClose = myInishtsFacetfilterClose; 1004 if (typeof expandCollapse === "function") { 1005 window.expandCollapse = expandCollapse; 1006 } 1007 // gd-jobs pages define their own clear_filters (see search-view.php) that 1008 // knows how to reset AJAX filtering and the URL. Don't clobber it with 1009 // the generic legacy implementation. 1010 if (!$(".insights_custom_filters[data-gd-jobs-filters='true']").length) { 1011 window.clear_filters = clear_filters; 1012 } 1013 1014 // Bind static-tree handling before optional widget initialization so jobs 1015 // filter markup is interactive even when another tree fails to initialize. 1016 bindLegacySelectedFiltersOverride(); 1017 runFancyTreeInit(); 1018 }; 1019 1020 if (hasFancyTreePlugin()) { 1021 initialize(); 1022 return; 1023 } 1024 1025 let retries = 0; 1026 const maxRetries = 50; 1027 const intervalId = setInterval(() => { 1028 retries += 1; 1029 1030 if (hasFancyTreePlugin()) { 1031 clearInterval(intervalId); 1032 initialize(); 1033 return; 1034 } 1035 1036 if (retries >= maxRetries) { 1037 clearInterval(intervalId); 1038 initialize(); 1039 } 1040 }, 100); 1041}
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.