PageSourceSearch

https://www.houstonfirst.com/static/js/lib/swiftype/jquery.swiftype.search.js

js houstonfirst.com collected 2026-10-03 20:48:41 UTC 10,489 bytes, 311 lines download raw bytes

1(function ($) {
2  var queryParser = function (a) {
3      var i, p, b = {};
4      if (a === "") {
5        return {};
6      }
7      for (i = 0; i < a.length; i += 1) {
8        p = a[i].split('=');
9        if (p.length === 2) {
10          b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
11        }
12      }
13      return b;
14    };
15  $.queryParams = function () {
16    return queryParser(window.location.search.substr(1).split('&'));
17  };
18  $.hashParams = function () {
19    return queryParser(window.location.hash.substr(1).split('&'));
20  };
21
22
23  window.Swiftype = window.Swiftype || {};
24  Swiftype.root_url = Swiftype.root_url || 'https://api.swiftype.com';
25  Swiftype.pingUrl = function(endpoint, callback) {
26    var to = setTimeout(callback, 350);
27    var img = new Image();
28    img.onload = img.onerror = function() {
29      clearTimeout(to);
30      callback();
31    };
32    img.src = endpoint;
33    return false;
34  };
35  Swiftype.pingSearchResultClick = function (engineKey, docId, callback) {
36    var params = {
37      t: new Date().getTime(),
38      engine_key: engineKey,
39      doc_id: docId,
40      q: Swiftype.currentQuery
41    };
42    var url = Swiftype.root_url + '/api/v1/public/analytics/pc?' + $.param(params);
43    Swiftype.pingUrl(url, callback);
44  };
45
46  $.fn.swiftypeSearch = function (options) {
47    var options = $.extend({}, $.fn.swiftypeSearch.defaults, options);
48
49    return this.each(function () {
50      var $this = $(this);
51      var config = $.meta ? $.extend({}, options, $this.data()) : options;
52      $this.data('swiftype-config-search', config);
53
54      $this.selectedCallback = function (data) {
55        return function (e) {
56          var $el = $(this);
57          e.preventDefault();
58          Swiftype.pingSearchResultClick(config.engineKey, data['id'], function() {
59            config.onComplete($el);
60          });
61        };
62      };
63
64      $this.registerResult = function ($element, data) {
65        $element.data('swiftype-item', data);
66        $('a', $element).click($this.selectedCallback(data));
67      };
68
69      $this.getContentCache = function () {
70        return $('#' + contentCacheId);
71      };
72
73      var $resultContainer = $(config.resultContainingElement),
74        initialContentOfResultContainer = $resultContainer.html(),
75        contentCacheId = 'st-content-cache',
76        $contentCache = $this.getContentCache();
77
78      var setSearchHash = function (query, page) {
79          location.hash = "stq=" + encodeURIComponent(query) + "&stp=" + page;
80        };
81
82      var submitSearch = function (query, options) {
83          options = $.extend({
84            page: 1
85          }, options);
86          var params = {};
87
88          if (!$contentCache.length) {
89            $resultContainer.after("<div id='" + contentCacheId + "' style='display: none;'></div>");
90            $contentCache.html(initialContentOfResultContainer).hide();
91          }
92          config.loadingFunction(query, $resultContainer);
93
94          Swiftype.currentQuery = query;
95          params['q'] = query;
96          params['engine_key'] = config.engineKey;
97          params['page'] = options.page;
98
99          function handleFunctionParam(field) {
100            if (field !== undefined) {
101              var evald = field;
102              if (typeof evald === 'function') {
103                evald = evald.call();
104              }
105              return evald;
106            }
107            return undefined;
108          }
109
110          params['per_page'] = handleFunctionParam(config.perPage);
111          params['search_fields'] = handleFunctionParam(config.searchFields);
112          params['fetch_fields'] = handleFunctionParam(config.fetchFields);
113          params['facets'] = handleFunctionParam(config.facets);
114          params['filters'] = handleFunctionParam(config.filters);
115          params['document_types'] = handleFunctionParam(config.documentTypes);
116          params['functional_boosts'] = handleFunctionParam(config.functionalBoosts);
117          params['sort_field'] = handleFunctionParam(config.sortField);
118          params['sort_direction'] = handleFunctionParam(config.sortDirection);
119          params['spelling'] = handleFunctionParam(config.spelling);
120          params['highlight_fields'] = handleFunctionParam(config.highlightFields);
121
122          $.ajax({
123            dataType: "json",
124            url: Swiftype.root_url + "/api/v1/public/engines/search.json?callback=?",
125            data: params,
126            xhrFields: { withCredentials: true },
127            success: renderSearchResults
128          });
129        };
130
131      function handleHashchange () {
132        var params = $.hashParams();
133        if (params.stq) {
134          submitSearch(params.stq, {
135            page: params.stp
136          });
137        } else {
138          var $contentCache = $this.getContentCache();
139          if ($contentCache.length) {
140            $resultContainer.html($contentCache.html());
141            $contentCache.remove();
142          }
143        }
144      }
145
146      // Expose this globally for users to force a refresh of search results.
147      Swiftype.reloadResults = handleHashchange;
148
149      $(window).on("hashchange", handleHashchange);
150
151      var $containingForm = $this.parents('form');
152      if ($containingForm) {
153        $containingForm.bind('submit', function (e) {
154          e.preventDefault();
155          var searchQuery = $this.val();
156          setSearchHash(searchQuery, 1);
157        });
158      }
159
160      $(document).on('click', '[data-hash][data-page]', function (e) {
161        e.preventDefault();
162        var $this = $(this);
163        setSearchHash($.hashParams().stq, $this.data('page'));
164      });
165
166      $(document).on('click', '[data-hash][data-spelling-suggestion]', function (e) {
167        e.preventDefault();
168        var $this = $(this);
169        setSearchHash($this.data('spelling-suggestion'), 1);
170      });
171
172      var renderSearchResults = function (data) {
173        if (typeof config.preRenderFunction === 'function') {
174          config.preRenderFunction.call($this, data);
175        }
176
177        config.renderResultsFunction($this.getContext(), data);
178
179        if (typeof config.postRenderFunction === 'function') {
180          config.postRenderFunction.call($this, data);
181        }
182      };
183
184      $this.getContext = function () {
185        return {
186          config: config,
187          resultContainer: $resultContainer,
188          registerResult: $this.registerResult
189        };
190      };
191
192      handleHashchange(); // if the swiftype query hash is present onload (maybe the user is pressing the back button), submit a query onload
193    });
194  };
195
196  var renderPagination = function (ctx, resultInfo) {
197    var maxPagesType, maxPages = -1,
198      config = ctx.config;
199    $.each(resultInfo, function(documentType, typeInfo) {
200      if (typeInfo.num_pages > maxPages) {
201        maxPagesType = documentType;
202        maxPages = typeInfo.num_pages;
203      }
204    });
205    var currentPage = resultInfo[maxPagesType].current_page,
206      totalPages = resultInfo[maxPagesType].num_pages;
207
208    $(config.renderPaginationForType(maxPagesType, currentPage, totalPages)).appendTo(ctx.resultContainer);
209  };
210
211
212  var normalize = function (str) {
213      return $.trim(str).toLowerCase();
214    };
215
216  function htmlEscape(str) {
217    return String(str).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
218  }
219
220  var defaultRenderResultsFunction = function (ctx, data) {
221    var $resultContainer = ctx.resultContainer,
222      config = ctx.config;
223
224    $resultContainer.html('');
225
226    $.each(data.records, function (documentType, items) {
227      $.each(items, function (idx, item) {
228        ctx.registerResult($(config.renderFunction(documentType, item)).appendTo($resultContainer), item);
229      });
230    });
231
232    renderPagination(ctx, data.info);
233  };
234
235  var defaultRenderFunction = function (document_type, item) {
236      return '<div class="st-result"><h3 class="title"><a href="' + item['url'] + '" class="st-search-result-link">' + htmlEscape(item['title']) + '</a></h3>' +
237          '<p>'+ htmlEscape(item['description']) + '</p></div>';
238    };
239
240  var defaultLoadingFunction = function(query, $resultContainer) {
241      $resultContainer.html('<p class="st-loading-message">loading...</p>');
242    };
243
244  var defaultOnComplete = function(elem) {
245    window.location = elem.attr('href');
246  };
247
248  var defaultPostRenderFunction = function(data) {
249    var totalResultCount = 0;
250    var $resultContainer = this.getContext().resultContainer;
251    var spellingSuggestion = null;
252
253    if (data['info']) {
254      $.each(data['info'], function(index, value) {
255        totalResultCount += value['total_result_count'];
256        if ( value['spelling_suggestion'] ) {
257          spellingSuggestion = value['spelling_suggestion']['text'];
258        }
259
260      });
261    }
262
263    if (totalResultCount === 0) {
264      $resultContainer.html("<div id='st-no-results' class='st-no-results'>No results found.</div>");
265    }
266
267    if (spellingSuggestion !== null) {
268      $resultContainer.append('<div class="st-spelling-suggestion">Did you mean <a href="#" data-hash="true" data-spelling-suggestion="' + spellingSuggestion + '">' + spellingSuggestion + '</a>?</div>');
269    }
270  };
271
272
273  var defaultRenderPaginationForType = function (type, currentPage, totalPages) {
274      var pages = '<div class="st-page">',
275        previousPage, nextPage;
276      if (currentPage != 1) {
277        previousPage = currentPage - 1;
278        pages = pages + '<a href="#" class="st-prev" data-hash="true" data-page="' + previousPage + '">&laquo; previous</a>';
279      }
280      if (currentPage < totalPages) {
281        nextPage = currentPage + 1;
282        pages = pages + '<a href="#" class="st-next" data-hash="true" data-page="' + nextPage + '">next &raquo;</a>';
283      }
284      pages += '</div>';
285      return pages;
286    };
287
288
289  $.fn.swiftypeSearch.defaults = {
290    attachTo: undefined,
291    documentTypes: undefined,
292    facets: undefined,
293    filters: undefined,
294    engineKey: undefined,
295    searchFields: undefined,
296    functionalBoosts: undefined,
297    sortField: undefined,
298    sortDirection: undefined,
299    fetchFields: undefined,
300    highlightFields: undefined,
301    preRenderFunction: undefined,
302    postRenderFunction: defaultPostRenderFunction,
303    loadingFunction: defaultLoadingFunction,
304    renderResultsFunction: defaultRenderResultsFunction,
305    renderFunction: defaultRenderFunction,
306    renderPaginationForType: defaultRenderPaginationForType,
307    onComplete: defaultOnComplete,
308    perPage: 10,
309    spelling: 'strict'
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.