PageSourceSearch

https://mountaincabinsutah.com/wp-content/plugins/homerunner/assets/js/explorer-ai-search.js?ver=2.6.7

js mountaincabinsutah.com collected 2026-10-10 02:01:37 UTC 8,717 bytes, 311 lines download raw bytes

1(function ($) {
2  $.fn.homelocalExplorerAiSearch = function (rootApp, options, settings) {
3    const debounce = (func, timeout = 300) => {
4      let timer;
5      return (...args) => {
6        clearTimeout(timer);
7        timer = setTimeout(() => {
8          func.apply(this, args);
9        }, timeout);
10      };
11    };
12
13    const $container = $(this);
14
15    const selector = {
16      form: ".hfse-ai-form",
17      input: ".ai_search_input",
18      searchId: ".ai_search_id",
19      recentSearchContainer: ".ai_recent_search",
20      recentSearchItem: ".ai_recent_search_item",
21      clearHistoryBtn: ".ai_search_clear_history_btn",
22      loaderModal: "#hfse-ai-search-loader-modal",
23    };
24
25    let showingRecentSearches = false;
26    let aiRequest;
27
28    const app = {
29      init: () => {
30        // ai search
31        $container.on("submit", selector.form, app.onAiSearchFormSubmit);
32
33        $container.on(
34          "input",
35          selector.input,
36          debounce(app.onAiSearchInputChange, 300),
37        );
38
39        // hfse-ai-search-abort-btn
40        $(selector.loaderModal).on(
41          "click",
42          ".hfse-ai-search-abort-btn",
43          app.abortyAiSearch,
44        );
45
46        if (options.hide_ai_search_history !== "1") {
47          app.initSearchHistory();
48        }
49
50        app.onAiSearchInputChange();
51
52        $container.on("reset", function () {
53          $container.find(selector.input).val("");
54          $container.find(selector.searchId).val("");
55          app.onAiSearchInputChange();
56        });
57      },
58
59      initSearchHistory: () => {
60        $container.on("focus", selector.input, app.showRecentSearches);
61
62        // $container.on("blur", selector.input, app.hideRecentSearches);
63        $(document).on("click", function (event) {
64          if (
65            showingRecentSearches &&
66            !$(event.target).closest($container.find(selector.input)).length &&
67            !$(event.target).closest(
68              $container.find(selector.recentSearchContainer),
69            ).length
70          ) {
71            app.hideRecentSearches();
72          }
73        });
74
75        $container.on(
76          "click",
77          selector.recentSearchItem,
78          app.onAiSearchLinkClick,
79        );
80
81        $container.on(
82          "click",
83          selector.clearHistoryBtn,
84          app.clearRecentSearches,
85        );
86      },
87
88      clearRecentSearches: (e) => {
89        e.preventDefault();
90
91        localStorage.removeItem("recentAiSearches");
92
93        app.hideRecentSearches();
94
95        return false;
96      },
97
98      getRecentSearches: () => {
99        let recentSearches = localStorage.getItem("recentAiSearches");
100        if (recentSearches) {
101          recentSearches = JSON.parse(recentSearches);
102          return recentSearches;
103        }
104
105        return [];
106      },
107
108      addRecentSearch: (query, query_hash) => {
109        let recentSearches = app.getRecentSearches();
110
111        // check if query already exists
112        for (let i = 0; i < recentSearches.length; i++) {
113          if (recentSearches[i].query === query) {
114            return;
115          }
116        }
117
118        recentSearches.unshift({
119          query: query,
120          query_hash: query_hash,
121        });
122
123        localStorage.setItem(
124          "recentAiSearches",
125          JSON.stringify(recentSearches),
126        );
127      },
128
129      showRecentSearches: () => {
130        const $target = $container.find(selector.recentSearchContainer);
131        const recentSearches = app.getRecentSearches();
132
133        $target.empty();
134
135        if (recentSearches.length > 0) {
136          const itemsLimit = Math.min(
137            recentSearches.length,
138            parseInt(settings.ai_search_history_limit, 10),
139          );
140
141          showingRecentSearches = true;
142
143          $target.append(`
144						<div class="ai_recent_search_items"></div>
145						<div class="hk-margin-small hk-flex hk-flex-right">
146							<button class="uk-button uk-button-link hk-button hk-button-link hk-button-small ai_search_clear_history_btn">${settings.ai_search_clear_history}</button>
147						</div>
148					`);
149
150          const $items = $target.find(".ai_recent_search_items");
151
152          for (let i = 0; i < itemsLimit; i++) {
153            const $item = $(`
154							<button class="ai_recent_search_item" data-query="${recentSearches[i].query}" data-query-hash="${recentSearches[i].query_hash}">${recentSearches[i].query}</button>
155					`);
156
157            $items.append($item);
158          }
159
160          $target.show();
161        }
162      },
163
164      hideRecentSearches: () => {
165        showingRecentSearches = false;
166        $container.find(selector.recentSearchContainer).hide();
167      },
168
169      onAiSearchLinkClick: (e) => {
170        e.preventDefault();
171
172        $container.find(selector.recentSearchContainer).hide();
173
174        const query = $(e.target).data("query");
175        const query_hash = $(e.target).data("query-hash");
176
177        $container.find(selector.input).val(query);
178        $container.find(selector.searchId).val(query_hash);
179
180        app.onAiSearchInputChange();
181
182        app.onAiSearchFormSubmit();
183      },
184
185      onAiSearchInputChange: () => {
186        const $btn = $container.find(".ai_search_submit");
187        const value = $container.find(selector.input).val();
188
189        if (value.length > 0) {
190          $btn.prop("disabled", false);
191        } else {
192          $btn.prop("disabled", true);
193        }
194      },
195
196      abortyAiSearch: () => {
197        if (aiRequest && aiRequest.readyState !== 4) {
198          aiRequest.abort();
199          // $container.removeClass("loading");
200          hkUIkit.modal(selector.loaderModal).hide();
201        }
202      },
203
204      buildSearchParams: (res, query_hash) => {
205        let params = {
206          ai_search_id: query_hash,
207          pets: res.pets ? "yes" : "no",
208        };
209
210        if (res.checkin && res.checkout) {
211          params.checkin = res.checkin;
212          params.checkout = res.checkout;
213        }
214
215        // Simple assignments
216        ["bedrooms", "bathrooms", "adults", "children", "title__like"].forEach(
217          (key) => {
218            if (res[key]) params[key] = res[key];
219          },
220        );
221
222        // Array-to-string assignments
223        const arrayFields = {
224          amenities: "amenity",
225          groups: "group",
226          states: "state",
227          cities: "city",
228          types: "type",
229          locations: "location",
230          ctid: "ctid",
231        };
232
233        for (const [field, paramKey] of Object.entries(arrayFields)) {
234          if (res[field]) params[paramKey] = res[field].join(",");
235        }
236
237        return params;
238      },
239
240      onAiSearchFormSubmit: () => {
241        // $container.addClass("loading");
242
243        hkUIkit.modal(selector.loaderModal).show();
244
245        // $container
246        //   .find(".hfse-properties .property-item")
247        //   .not(".property-loading")
248        //   .remove();
249
250        // add property loading skeleton.
251        // for (let i = 0; i < options.per_page; i++) {
252        //   $container
253        //     .find(".hfse-properties")
254        //     .append(rootApp.getPropertyLoadingHtml());
255        // }
256
257        aiRequest = $.ajax({
258          type: "POST",
259          url: settings.api_url + "homelocal/v1/ai-search/process-query",
260          data: {
261            query: $container.find(selector.input).val(),
262          },
263        })
264          .done((response) => {
265            if (response.query_hash) {
266              app.addRecentSearch(response.query, response.query_hash);
267
268              $container.find(selector.searchId).val(response.query_hash);
269
270              if (options.grid === "0") {
271                const params = app.buildSearchParams(
272                  response.response,
273                  response.query_hash,
274                );
275
276                const redirectUrl =
277                  $container.find(selector.form).attr("action") +
278                  "?" +
279                  $.param(params);
280
281                window.location.href = redirectUrl;
282                return false;
283              }
284
285              rootApp.setExplorerParams(response.response);
286              rootApp.clearFilterChangeTimer();
287              rootApp.performFilter();
288
289              return false;
290            }
291          })
292          .fail((response) => {
293            if (response.statusText === "abort") {
294            } else if (response.responseJSON && response.responseJSON.message) {
295              alert(response.responseJSON.message);
296            } else {
297              alert(settings.ai_search_failed_message);
298            }
299          })
300          .always(() => {
301            // $container.removeClass("loading");
302            hkUIkit.modal(selector.loaderModal).hide();
303          });
304
305        return false;
306      },
307    };
308
309    return app;
310  };
311})(jQuery);

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.