PageSourceSearch

https://www.hotelmanagement-network.com/wp-content/themes/goodlife…/modules/fancyTreeFrontEnd.js?ver=1.43935

js hotelmanagement-network.com collected 2026-10-02 04:29:26 UTC 34,147 bytes, 1,041 lines download raw bytes

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.