PageSourceSearch

https://sharkssuites.com/wp-content/themes/responsive/search/search-autocomplete.js?ver=12

js sharkssuites.com collected 2026-10-06 14:10:35 UTC 14,903 bytes, 311 lines download raw bytes

1(function ($) {
2
3const isLocal = window.location.hostname.startsWith("local.");
4const algoliaCfg = {
5    appId: isLocal ? '7YBEXRNILY' : 'ONZ5UYNDHI',
6    apiKey: isLocal ? '6b24cc8afd36cb371486fdfccad96169' : 'cd9958d5a487a75fc6eda5566a75e1b8',
7};
8// console.log('AG', algoliaCfg);
9
10function debouncePromise(fn, time) {
11    let timerId = undefined;
12    return function debounced(...args) {
13        if (timerId) {
14        clearTimeout(timerId);
15        }
16
17        return new Promise((resolve) => {
18            timerId = setTimeout(() => resolve(fn(...args)), time);
19        });
20    };
21}
22
23const debounced = debouncePromise((items) => Promise.resolve(items), 100);
24
25function onResultClick(itemUrl) {
26    window.location.href = itemUrl;
27}
28
29window.setupAutocomplete = function(templatePath, isAlgoliaEnabled) {
30    $(document).ready(function () {
31
32        if (isAlgoliaEnabled) {
33            const searchClient = algoliasearch(algoliaCfg.appId, algoliaCfg.apiKey);
34            const algolia = window['@algolia/autocomplete-js'];
35            const minEventStartDate = Math.floor(Date.now() / 1000);
36            // const minEventStartDate = new Date('2024-09-01').getTime() / 1000;
37
38            window.algoliaAutocomplete = algolia.autocomplete({
39                container: '#algolia-search',
40                detachedMediaQuery: 'none', // Disable detached mode
41                placeholder: 'Search for an Event, Team/Artist, or Venue...',
42                classNames: {
43                    submitButton: 'far fa-magnifying-glass',
44                },
45                getSources({ query }) {
46                    let optionalWords = "";
47
48                    // We have a way to customize ranking using index Rules configured in Algolia dashboard (see
49                    // https://dashboard.algolia.com/apps/ONZ5UYNDHI/rules/teams_v1). That said, the code-based approach
50                    // shown below gives us much more control; leaving it here in case we need to use it in the future.
51
52                    /*
53                    // If user searches for "dallas" (and not "dallas cowboys") we want to artificially boost the 
54                    // ranking of the Dallas Cowboys.
55                    const dallasRegex = /(?:^|\s+)dallas(?:$|\s+)/i;
56                    if (dallasRegex.test(query) && !/dallas cowboys/i.test(query)) {
57                        // Pretend user typed in "dallas cowboys" instead of just "dallas"
58                        query = query.replace(dallasRegex, "dallas cowboys ");
59                        // Make "cowboys" optional so that we still get results if user only types "dallas"
60                        optionalWords += " cowboys";
61                    }
62                    */
63
64                    return query.trim().length < 2 ? [] : debounced([{
65                        sourceId: 'teams',
66                        getItemUrl({ item }) {
67                            return `/all-suites/${item.permalink}?ref=search`;
68                        },
69                        getItems() {
70                            return algolia.getAlgoliaResults({
71                                searchClient,
72                                queries: [
73                                    {
74                                        indexName: 'teams_v1',
75                                        query,
76                                        params: {
77                                            optionalWords: optionalWords,
78                                            hitsPerPage: 5,
79                                        },
80                                    },
81                                ],
82                            });
83                        },
84                        templates: {
85                            header() {
86                                return 'Teams';
87                            },
88                            item({ item, components, html }) {
89                                return html`
90                                    <div
91                                        class="search-result-row"
92                                        onClick=${(event) => {
93                                            onResultClick(`/all-suites/${item.permalink}?ref=search`);
94                                        }}
95                                    >
96                                        <span class="stadium_icon_sm stadium_icon_${item.league_id}"></span>
97                                        <a href="/all-suites/${item.permalink}?ref=search" class="search-result">
98                                            ${item.name}
99                                        </a>
100                                    </div>
101                                `;
102                            },
103                        },
104                    }, {
105                        sourceId: "stadiums",
106                        getItemUrl({ item }) {
107                            return `/all-suites/${item.permalink}?ref=search`;
108                        },
109                        getItems() {
110                            return algolia.getAlgoliaResults({
111                                searchClient,
112                                queries: [
113                                    {
114                                        indexName: 'stadiums_v1',
115                                        query,
116                                        params: {
117                                            hitsPerPage: 5
118                                        },
119                                    },
120                                ],
121                            });
122                        },
123                        templates: {
124                            // See https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/templates/#returning-html
125                            header() {
126                                return 'Venue';
127                            },
128                            item({ item, components, html }) {
129                                return html`
130                                    <div
131                                        class="search-result-row"
132                                        onClick=${(event) => {
133                                            onResultClick(`/all-suites/${item.permalink}?ref=search`);
134                                        }}
135                                    >
136                                        <span class="stadium_icon_sm stadium_icon_0"></span>
137                                        <a href="/all-suites/${item.permalink}?ref=search" class="search-result">
138                                            ${item.name} (${item.location})
139                                        </a>
140                                    </div>
141                                `;
142                            },
143                        },
144                    }, {
145                        sourceId: "events",
146                        getItemUrl({ item }) {
147                            return `${item.marketplace_v1_url}?ref=search`;
148                        },
149                        getItems() {
150                            return algolia.getAlgoliaResults({
151                                searchClient,
152                                queries: [
153                                    {
154                                        indexName: 'events_v1',
155                                        query,
156                                        params: {
157                                            hitsPerPage: 5,
158                                            // Ensure that filter out events that have already happened
159                                            filters: `date_unix_sec > ${minEventStartDate}`,
160                                        },
161                                    },
162                                ],
163                            });
164                        },
165                        templates: {
166                            // See https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/templates/#returning-html
167                            header() {
168                                return 'Events';
169                            },
170                            item({ item, components, html }) {
171                                // console.log({ item, components });
172                                return html`
173                                    <div
174                                        class="search-result-row"
175                                        onClick=${(event) => {
176                                            onResultClick(`${item.marketplace_v1_url}?ref=search`);
177                                        }}
178                                    >
179                                        <span class="stadium_icon_sm event_icon"></span>
180                                        <a href="${item.marketplace_v1_url}?ref=search" class="search-result">
181                                            ${item.event_name} / ${item.venue} / ${item.date_formatted}
182                                        </a>
183                                    </div>
184                                `;
185                            },
186                        },
187                    }, {
188                        // The autocomplete library doesn't give us an easy way to add a footer that is shown
189                        // independent of specific sources. If you add a footer for a specific source, then that footer
190                        // is only visible if search results exist (or if you also display a "no results" message). The
191                        // work-around we'll use is to create a source that always returns no results, and then add a
192                        // footer to that source.
193                        sourceId: "footer-only-no-results",
194                        getItems() {
195                            return Promise.resolve([]);
196                        },
197                        templates: {
198                            noResults({ html }) {
199                                return html`<div style="display: none;"></div>`;
200                            },
201                            footer({ html, state }) {
202                                return html`
203                                    <div class="non-result see-all ui-menu-item">
204                                        <a
205                                            class="ui-menu-item-wrapper"
206                                            href="/search-results/?term=${encodeURIComponent(state.query)}"
207                                        >
208                                            See All Results
209                                        </a>
210                                    </div>
211                                `;
212                            },
213                        },
214                    }]);
215                },
216                // debug: true, // ⚠️ Only for local dev: keep search results pane open after blur
217                onSubmit({ state }) {
218                    window.location.href = `/search-results/?term=${encodeURIComponent(state.query)}`;
219                },
220            });
221
222            // focus on search input on element load
223            $("#algolia-search input").focus();
224        } else {
225            var searchApiPath = templatePath + "/search/search.php";
226            var minLength     = 2;
227            var searchInput   = $("#all_suites_search");
228
229            // focus on search input on element load
230            searchInput.focus();
231
232            // on input hide the empty message if the chars in the field are less than minLength
233            searchInput.on("input", function (event) {
234                if ($(this).val().length < minLength) {
235                    $("#search-empty-message").hide();
236                    $(".loading_small").css("display", "none");
237                } else {
238                    $(".loading_small").css("display", "block");
239                }
240            });
241
242            // autocomplete api
243            var autocompleteInstance = searchInput.autocomplete({
244                source: searchApiPath,
245                appendTo: ".seg-search__input-wrapper",
246                focus: function (event, ui) {
247                    $(event.currentTarget).find("li").css("background", "none")
248                    $(event.currentTarget).find("a.ui-state-focus").parent().css("background", "#f1f7ff")
249                },
250                select: function (event, ui) {
251                    var toElement = event.originalEvent.toElement;
252                    var elClicked = toElement ? toElement.localName : false;
253
254                    if (ui.item.url.length && (event.key === "Enter" || elClicked === "li")) {
255                        window.location = ui.item.url;
256                    }
257                },
258                create: function () {
259                    $(this).data("ui-autocomplete")._renderItem = function (ul, item) {
260                        var li = $("<li></li>");
261
262                        // if the item has a url, it is a result and gets an a tag inside
263                        // if not it does not and the item will not be selectable via keyboard
264                        if (item.url.length) {
265                            var icon  = "<span class='stadium_icon_sm stadium_icon_" + item.league + "'></span>";
266                            var a     = $("<a></a>");
267
268                            a.html(icon + item.label);
269                            a.attr("href", item.url);
270                            li.html(a);
271                        } else {
272                            li.html(item.label);
273                        }
274
275                        li.addClass(item.class);
276
277                        return li.appendTo(ul);
278                    };
279                },
280                response: function (event, ui) {
281                    // after search completed, before menu is shown, show empty results message if no results
282                    $(".loading_small").css("display", "none");
283                    if (ui.content.length === 0) {
284                        $("#search-empty-message").show();
285                    } else {
286                        $("#search-empty-message").hide();
287                    }
288                },
289                html: true,
290                minLength: minLength
291            });
292
293            // override the close method which closes the menu when clicking a link
294            autocompleteInstance.data("uiAutocomplete").close = function (event) {
295                if (event && event.type !== "menuselect") {
296                    $("ul.ui-autocomplete").hide();
297                }
298            };
299
300            // make sure search happens again upon refocusing on autocomplete
301            autocompleteInstance.focus(function () {
302                var val = $(this).val();
303                if (val.length >= minLength) {
304                    $(this).autocomplete("search", val);
305                }
306            });
307        }
308    });
309};
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.