PageSourceSearch

https://swissolympic.ch/.resources/sow/js/lib/search~1980-01-31-23-00-00-000~cache.js

js swissolympic.ch collected 2026-09-24 09:25:50 UTC 36,929 bytes, 840 lines download raw bytes

1var SearchFilter = function (options) {
2    var self = this;
3    this.options = options;
4    this.isSearchPage = self.options.isSearchPage;
5    this.$searchResults = $("#search-results");
6    this.$wrapper = options.wrapper;
7    this.$searchInput = self.$wrapper.find(".search-term");
8    this.$searchButton = self.$wrapper.find(".search-button");
9    this.searchUI = new SearchUI(this, self.$wrapper, options.i18nMessages);
10    this.tagId = "";
11    this.name = "search";
12    this.globalSearchFilter = new GlobalSearchFilter(options);
13    this.lastModified = options.lastModified;
14    this.filters = [];
15    this.i18nMessages = options.i18nMessages;
16    this.autocompleteCategories = this.globalSearchFilter.autocompleteCategories;
17    this.athleteServicePath = options.contextPath + options.athleteSearch.athleteUrl;
18    this.athletePortraitLinkBase = options.athleteSearch.portraitLinkBase + "?athleteId=";
19    this.$athletePrevewTemplate = $("#athlete-preview-template").find(".athlete-preview-small");
20    this.$athleteForm = $("#athletes-form");
21    this.pageSize = options.pageSize;
22    this.showAllEnabled = options.showAllEnabled;
23    this.searchId = options.searchId;
24    this.contextPath = "";
25    this.$searchResultTemplate = $("#result-template").find(".result");
26    this.query = {};
27    this.curPage = 1;
28    this.initialCategories = {};
29    this.counters = {};
30        
31    this.init = function () {
32        self.contextPath = self.options.contextPath;
33        self.query = self.options.query;
34        if (self.query.searchTerm) {
35            self.$searchInput.val(self.query.searchTerm);
36        }
37        if (self.query.categories == null) {
38        	this.query.categories = {};
39        }
40        if (!self.isEmptyCriteria()) {
41        	self.triggerSearch(true, false);
42        }
43        this.globalSearchFilter.query = self.query;
44        this.globalSearchFilter.name = self.name;
45        
46        self.tagId = self.options.tagId;
47        
48        self.initEventHandlers();
49        
50        var filters = options.filters;
51        for (var i = 0; i < filters.length; ++i) {
52            var curFilter = filters[i];
53            self.filters.push({name: curFilter.name, id: curFilter.id, data: curFilter.data, url: curFilter.url, loaded: false});
54        }
55        
56        self.populateFilters(self.filters, true);
57        
58        // switch tab to site in query
59        if(this.query.site) {
60        	self.switchPage(this.query.site);
61        }
62        
63        if (self.searchId && self.isSearchPage) {
64            var historyState = {};
65            if (history.state) {
66                historyState = history.state;
67            }
68            var search = window.location.search;
69            if (search.indexOf("?searchId=") < 0 && search.indexOf("&searchId=") < 0) {
70                if (search.indexOf("?") < 0) {
71                    search = "?searchId=" + self.searchId;
72                }
73                else {
74                    search = search + "&searchId=" + self.searchId;
75                }
76            }
77            history.replaceState(historyState, "", window.location.pathname + search);
78        }
79    };
80    
81    this.initEventHandlers = function () {
82        this.searchUI.initEventHandlers();
83        
84        //User clicks on another page tab
85        self.$wrapper.find(".search-nav .tab").click(function () {
86            if (!$(this).hasClass("selected")) {
87                var name = $(this).data("value");
88                self.switchPage(name);
89            }
90        });
91        //User clicks on another page tab
92        self.$wrapper.find(".search-nav .tab").keydown(function (ev) {
93            if (ev.keyCode === 13 && !$(this).hasClass("selected")) {
94                var name = $(this).data("value");
95                self.switchPage(name);
96            }
97        });
98        
99        self.$wrapper.find(".search-copy-link").click(function () {
100            $(this).find(".i-check").removeClass("hidden");
101            copy($(this).data("searchLink"));
102        });
103        
104        // hide mobile search button on focus
105        self.$searchInput.focus(function () {
106        	self.hideMovingSearchButton();
107        });
108        self.$wrapper.find(".category .head").click(function () {
109        	self.hideMovingSearchButton();
110        });
111        
112        $(document).on("click", "#load-more-button", function () {
113            self.triggerAthleteSearch(false); 
114            $(this).remove();
115        });
116        
117        $("#search-hint .button").click(function () {
118            self.hideSearchHint();
119        });
120        
121        $(document).click(function () {
122           if (!$(this).closest("#search-hint").length) {
123               self.hideSearchHint();
124           } 
125        });
126    };
127    
128    // Populate all filters
129    this.populateFilters = function (filters, showCounters) {
130        for (var i = 0; i < filters.length; ++i) {
131            self.populateFilter(filters[i], showCounters);
132        }
133    };
134    
135    // Request the categories for a filter
136    this.populateFilter = function (filter, showCounters) {
137        jQuery.ajax({
138            type: "POST",
139            url: filter.url,
140            cache: false,
141            data: JSON.stringify(filter.data),
142            contentType: "application/json; charset=utf-8",
143            headers: { 
144                'Accept': 'application/json',
145                'Content-Type': 'application/json' 
146            },
147            dataType: "json",
148            success: function (data) {
149                // Append the categories to the HTML
150                self.searchUI.appendCategories(filter.id, data, showCounters);
151                self.initialCategories[filter.id] = data;
152                self.globalSearchFilter.populateAutocomplete(data, filter.name, filter.id, false);
153                // If some categories are already selected, update HTML accordingly
154                if (self.query && self.query.categories && self.query.categories[filter.id]) {
155                    self.searchUI.selectTags(self.query.categories[filter.id], filter.id);
156                }
157                if (self.tagId && data.map(function (e) { return e.id; }).indexOf(self.tagId) > -1) {
158                    self.globalSearchFilter.selectCategory(self.tagId, filter.id);
159                    self.triggerSearch(false, false);
160                }
161                self.updateCategoryCounts(filter.id, self.counters[filter.id] || self.createInitialCounts(filter.id));
162            }
163        });
164    };
165    
166    this.selectCategory = function (categoryId, parentName) {
167        self.globalSearchFilter.selectCategory(categoryId, parentName);
168    };
169    
170    this.deselectCategory = function (categoryId, parentName) {
171        self.globalSearchFilter.deselectCategory(categoryId, parentName);
172    };
173    
174    this.search = function (query, scrollTop, doNotHide, shouldUpdateCounters, categoryType) {
175    	// either get categoryType counter update from query or reset from initial
176    	var resetCategoryType = false;
177    	if (shouldUpdateCounters && categoryType) {
178    		// test if query is empty without this categoryType
179    		if(!self.isEmptyCriteria(query, categoryType)) {
180            	query.shouldUpdateCounters = true;
181                query.newCategory = categoryType;
182            }
183	    	else {
184	    		resetCategoryType = true;
185	            query.shouldUpdateCounters = false,
186	            query.newCategory = "";
187	    	}
188        }
189        else {
190            query.shouldUpdateCounters = false,
191            query.newCategory = "";
192        }
193        if (query.site !== "sow") {
194            query.categories = {};
195        }
196        if (query.site === "all") {
197            query.adapters = ["sow-document", "sow-site", "sow-faq", "handbuch-site", "sos-site", "sot2019-site"];
198        }
199        if (query.site === "sow") {
200            query.adapters = ["sow-document", "sow-site", "sow-faq"];
201        }
202        if (query.site === "sot") {
203            query.adapters = ["sot2019-document", "sot2019-site"];
204        }
205        query.type = "search"; // only get small results
206        if (!self.isEmptyCriteria(query)) {
207            self.$wrapper.find(".search-content-wrapper .search-overlay").removeClass("hidden");
208            jQuery.ajax({
209                method: "POST",
210                url: self.contextPath + "/.search/v1/filter",
211                contentType: "application/json; charset=utf-8",
212                cache: false,
213                data: JSON.stringify(query),
214                headers: { 
215                    'Accept': 'application/json',
216                    'Content-Type': 'application/json' 
217                },
218                success: function(data) {
219                    if (scrollTop) {
220                        self.searchUI.scrollToTop();
221                    }
222                    if (!doNotHide) {
223                        self.searchUI.slideUpCategories();
224                    }
225                    self.$wrapper.find(".search-content-wrapper .search-overlay").addClass("hidden");
226                    self.updateSearchResults(data);
227                    if (data.counters) {
228                        self.counters[categoryType] = data.counters;
229                        self.updateCategoryCounts(categoryType, data.counters);
230                    }
231                    var nPages = Math.ceil(data.count / data.pageSize);
232                    var showAll = false;
233                    if (data.filter.showAll) {
234                        showAll = true;
235                    }
236                    if (self.curPage == 1 && nPages !== self.nPages) {
237                        self.nPages = nPages;
238                        self.searchUI.updatePager(nPages, self.curPage, showAll);
239                    }
240                    else {
241                        self.searchUI.updatePagerState(self.curPage, showAll);
242                    }
243                    if (showAll) {
244                        $(".show-all").text(self.i18nMessages.pageView).attr("data-active", "true");
245                        if (data.count > data.result.length) {
246                            self.$wrapper.find(".max-results").removeClass("hidden");
247                        }
248                        else {
249                            self.$wrapper.find(".max-results").addClass("hidden");
250                        }
251                    }
252                    else {
253                        $(".show-all").text(self.i18nMessages.showAll).attr("data-active", "false");
254                        self.$wrapper.find(".max-results").addClass("hidden");
255                    }
256                    self.globalSearchFilter.pushHistoryState(data.filter);
257                },
258                error: function (data) {
259                    self.$searchResults.find(".header > h2 .text").text(self.i18nMessages.error);
260                    self.$searchResults.find(".header > hr").removeClass("hidden");
261                },
262                dataType: "json"
263            });
264            if(resetCategoryType) {
265            	self.updateCategoryCounts(categoryType, self.createInitialCounts(categoryType));
266            }
267            else if(!shouldUpdateCounters) {
268                self.updateAllCounters(query);
269            }
270        }
271        else {
272            self.removeSearchResults();
273        }
274    };
275    
276    this.triggerSearch = function (doNotHide, shouldUpdateCounters, category) {
277        if (self.query.site !== "sow") {
278            self.triggerAthleteSearch(true);
279        }
280        self.curPage = 1;
281        self.query.page = 1;
282        self.query.pageSize = self.pageSize;
283        self.query.showAll = false;
284        self.query.lastModified = self.lastModified;
285        self.globalSearchFilter.pushHistoryState(self.query);
286        self.$wrapper.find(".search-copy-link .i-check").addClass("hidden");
287        self.search(self.query, false, doNotHide, shouldUpdateCounters, category);
288        // send google analytics search query
289        self.sendGoogleAnalytics();
290    };
291    
292    this.triggerPageLoad = function () {
293        if (self.query.site !== "sow") {
294            if (self.curPage > 1) {
295                $(".athlete-results").addClass("hidden").next("hr").addClass("hidden");
296            }
297            else if ($(".athlete-results .athlete-preview-small").length) {
298                $(".athlete-results").removeClass("hidden").next("hr").removeClass("hidden");
299            }
300        }
301        var query = history.state[self.name];
302        query.page = self.curPage;
303        self.globalSearchFilter.pushHistoryState(query);
304        self.search(query, true, true, false);
305    };
306    
307    this.triggerLoadAll = function () {
308        if (self.query.site !== "sow" && $(".athlete-results .athlete-preview-small").length) {
309            $(".athlete-results").removeClass("hidden").next("hr").removeClass("hidden");
310        }
311        var query = history.state[self.name];
312        query.page = 1;
313        self.curPage = 1;
314        if ($(".show-all").attr("data-active") === "true") {
315            query.pageSize = self.pageSize;
316            query.showAll = false;
317            self.globalSearchFilter.pushHistoryState(query);
318            self.search(query, true, false, false);
319        }
320        else {
321            query.pageSize = 1000;
322            query.showAll = true;
323            self.globalSearchFilter.pushHistoryState(query);
324            self.search(query, true, false, false);
325        }
326    }
327    
328    this.triggerSort = function (sortBy) {
329        var tempQuery = history.state[self.name];
330        tempQuery.page = 1;
331        tempQuery.sortBy = sortBy;
332        self.globalSearchFilter.pushHistoryState(tempQuery);
333        self.curPage = 1;
334        self.search(tempQuery, false, true, false);
335    };
336
337    this.updateAllCounters = function (query) {
338        var that = this;
339        jQuery.ajax({
340            method: "POST",
341            url: self.contextPath + "/.search/v1/counters",
342            contentType: "application/json; charset=utf-8",
343            cache: false,
344            data: JSON.stringify(query),
345            headers: { 
346                'Accept': 'application/json',
347                'Content-Type': 'application/json' 
348            },
349            success: function(data) {
350            	$.each(data, function(key, value) {
351                    self.counters[key] = value;
352            		self.updateCategoryCounts(key, value)
353                });
354            },
355            dataType: "json"
356        });
357    };
358    
359    this.updateSearchLink = function () {
360    	var searchQuery = self.createSearchQuery(self.query);
361        $(".search-copy-wrapper .search-copy-link").data('searchLink', window.location.href + searchQuery);
362    };
363    
364    this.sendGoogleAnalytics = function () {
365        var categoriesSet = false;
366        $.each(self.query.categories, function(key, value) {
367            if (value.length) {
368                categoriesSet = true;
369            }
370        });
371        if (typeof ga != "undefined" && (self.query.searchTerm || categoriesSet)) {
372    		try {
373    			var searchQuery = self.createSearchQuery(self.query);
374    			ga("send", "pageview", window.location.pathname + searchQuery);
375    		}
376    		catch(err) {
377    			console.log(err.stack);
378    		}
379    	} else if (self.query.searchTerm || categoriesSet) {
380            var searchQuery = self.createSearchQuery(self.query);
381            pushEvent('search', 'global', searchQuery);
382        }
383    };
384    
385    this.createSearchQuery = function (query) {
386    	var query = self.query;
387        var searchTerm = "?";
388        if (query.type) {
389            searchTerm = searchTerm + "type=" + query.type;
390        }
391        if (query.searchTerm) {
392            searchTerm = searchTerm + "&searchTerm=" + encodeURIComponent(query.searchTerm);
393        }
394        $.each(query.categories, function(key, value) {
395            if (value.length) {
396                searchTerm = searchTerm + "&categories%5b" + key + "%5d" + "=" + value.join(",");
397            }
398        });
399        return searchTerm;
400    }
401    
402    this.updateCategoryTabCounter = function (name) {
403        self.globalSearchFilter.updateCategoryTabCounter(name);
404    };
405    
406    this.switchPage = function (name) {
407        self.query.site = name;
408        self.$wrapper.find("input[name='site']").val(self.query.site);
409        self.$wrapper.find(".search-nav").find(".tab").removeClass("selected");
410        self.$wrapper.find(".search-nav").find(".tab[data-value='" + name + "']").addClass("selected");
411        if (name !== "sow") {
412            self.$wrapper.find(".selected-tag").remove();
413            self.$wrapper.find(".selectable-tag input:checked").each(function () {
414                $(this).removeAttr("checked");
415            });
416            self.$wrapper.find(".counter").text("0").addClass("invisible");
417            self.$wrapper.find(".filter").addClass("hidden");
418            self.$wrapper.find(".reset").addClass("hidden");
419            self.$wrapper.find(".selected-tags").addClass("hidden");
420            self.$searchInput.catcomplete( "option", { disabled: true } );
421            if (!self.isEmptyCriteria()) {
422                self.triggerSearch(true, false);
423                // FIXME remove because already called in triggerSearch???
424                //self.triggerAthleteSearch(true);
425            }
426            else {
427                self.removeSearchResults();
428            }
429        }
430        else {
431            self.$wrapper.find(".filter").removeClass("hidden");
432            self.$wrapper.find(".reset").removeClass("hidden");
433            self.$wrapper.find(".selected-tags").removeClass("hidden");
434            self.$searchInput.catcomplete( "option", { disabled: false } );
435            if (!self.isEmptySearchTerm()) {
436                self.triggerSearch(true, false);
437            }
438            else {
439                self.removeSearchResults();
440            }
441            $(".athlete-results").addClass("hidden").next("hr").addClass("hidden");
442        }
443    };
444    
445    this.updateSearchResults = function (results) {
446        var resultCount = $("#search-hint").attr("data-count");
447        if (resultCount) {
448            resultCount = parseInt(resultCount, 10);
449        }
450        else {
451            resultCount = 21
452        }
453        if (results.count >= resultCount && this.isEmptyFilter() && !this.searchHintAlreadyShown() && self.isSearchPage && results.filter.site === "sow") {
454            this.showSearchHint();
455        }
456        this.$searchResults.find(".content").html("");
457        var searchResultList = results.result;
458        if (searchResultList) {
459            this.$searchResults.find(".header > h2 .text").text(results.count + " " + self.i18nMessages.results);
460            if (searchResultList.length > 0) {
461                this.$searchResults.find(".header > hr").removeClass("hidden");
462                this.$searchResults.find(".search-copy-link").removeClass("hidden");
463                self.$wrapper.find(".sort-by").removeClass("hidden");
464                self.updateSearchLink();
465            }
466            else {
467                this.$searchResults.find(".header > hr").addClass("hidden");
468                this.$searchResults.find(".search-copy-link").addClass("hidden");
469                self.$wrapper.find(".sort-by").addClass("hidden");
470            }
471            if (this.isEmptySearchTerm() || this.isEmptyFilter()) {
472            	this.$wrapper.find(".search-without-filter").addClass("hidden");
473            }
474            else {
475            	this.$wrapper.find(".search-without-filter").removeClass("hidden");
476            }
477            var $resultTemplate = $("#result-template .result");
478            for (var i = 0; i < searchResultList.length; ++i) {
479                var $result = $resultTemplate.clone();
480                var source = self.i18nMessages.sowTitle;
481                if (searchResultList[i].source === "sot") {
482                    source = self.i18nMessages.sotTitle;
483                }
484                else if (searchResultList[i].source === "handbuch") {
485                    source = self.i18nMessages.handbuchTitle;
486                }
487                else if (searchResultList[i].source === "sos") {
488                    source = self.i18nMessages.sosTitle;
489                }
490                self.$searchResults.find(".content").append($result);
491                if(results.filter.searchTerm) {
492                    var separator = (searchResultList[i].link.indexOf("?") === -1) ? "?":"&";
493                    $result.find("a").attr("href", searchResultList[i].link + separator + "searchQuery=" + encodeURIComponent(results.filter.searchTerm));
494                } else {
495                    $result.find("a").attr("href", searchResultList[i].link);
496                }
497                $result.find("h3").text(searchResultList[i].title);
498                if (searchResultList[i].type === "document") {
499                    $result.find(".result-type .type").text(" " + searchResultList[i].documentType.toUpperCase() + " " + searchResultList[i].fileSize + "");
500                    $result.find("a").attr("target", "_blank");
501                    if (searchResultList[i].linkFr || searchResultList[i].linkEn || searchResultList[i].linkIt) {
502                        $result.find(".language-links").removeClass("hidden");
503                        $result.find(".language-links .de").attr("href", searchResultList[i].linkDe);
504                        if (searchResultList[i].linkFr) {
505                            $result.find(".language-links .fr").attr("href", searchResultList[i].linkFr);
506                        }
507                        else {
508                            $result.find(".language-links .fr").addClass("hidden");
509                        }
510                        if (searchResultList[i].linkEn) {
511                            $result.find(".language-links .en").attr("href", searchResultList[i].linkEn);
512                        }
513                        else {
514                            $result.find(".language-links .en").addClass("hidden");
515                        }
516                        if (searchResultList[i].linkIt) {
517                            $result.find(".language-links .it").attr("href", searchResultList[i].linkIt);
518                        }
519                        else {
520                            $result.find(".language-links .it").addClass("hidden");
521                        }
522                        if (searchResultList[i].linkFr && (searchResultList[i].linkIt || searchResultList[i].linkEn)) {
523                            $result.find(".language-links .fr").next(".bar").removeClass("hidden");
524                        }
525                        if (searchResultList[i].linkEn && searchResultList[i].linkIt) {
526                            $result.find(".language-links .en").next(".bar").removeClass("hidden");
527                        }
528                    }
529                }
530                else if (searchResultList[i].type === "faq") {
531                	$result.find(".result-type .type").text(self.i18nMessages.typeFaq);
532                }
533                else {
534                    $result.find(".result-type .type").text(self.i18nMessages.typeWebsite);
535                    if (searchResultList[i].parentTitle) {
536                        $result.find(".result-type .parent").text(" | " + searchResultList[i].parentTitle);
537                    }
538                }
539                $result.find(".result-type .source .text").text(source + " | ");
540                if (searchResultList[i].source === "sot" || searchResultList[i].source === "handbuch" || searchResultList[i].source === "sos") {
541                    $result.find(".result-type .source i").removeClass("hidden");
542                    $result.find("a").attr("target", "_blank");
543                }
544                if (searchResultList[i].text && searchResultList[i].text.length > 0) {
545                    $result.find("p").append(searchResultList[i].text);
546                }
547                else {
548                    $result.find("p").addClass("hidden");
549                }
550                // debug info
551                if (results.debug) {
552                	var debugStr = " | score:" + searchResultList[i].score 
553                	debugStr = debugStr + " | uuid:" + searchResultList[i].uuid 
554                	$result.find(".result-type .debug .score").text(debugStr);
555                }
556            }
557        }
558        else {
559            self.$searchResults.find(".header > h2 .text").text(self.i18nMessages.error);
560            self.$searchResults.find(".header > hr").addClass("hidden");
561        }
562        // show result sources
563        if (results.filter.site === "all") {
564            self.$wrapper.find(".result .result-type .source").removeClass("hidden");
565        }
566        else {
567            self.$wrapper.find(".result .result-type .source").addClass("hidden");
568        }
569        // fill statistics
570        self.$wrapper.find(".search-statistics").find(".stats").text(JSON.stringify(results.statistics))
571        // show debug info
572        if (results.debug) {
573            self.$wrapper.find(".result .result-type .debug").removeClass("hidden");
574            self.$wrapper.find(".search-statistics").removeClass("hidden");
575        }
576        else {
577            self.$wrapper.find(".result .result-type .debug").addClass("hidden");
578            self.$wrapper.find(".search-statistics").addClass("hidden");
579        }
580    };
581    
582    this.updateCategoryCounts = function (categoryType, counters) {
583        self.$wrapper.find(".category-content[data-type='" + categoryType + "'] .selectable-tag").not(".more").each(function () {
584            var categoryId = $(this).data("id");
585            if (counters[categoryId]) {
586                var name = $(this).data("name"),
587                    parent = $(this).closest(".category-content").data("name");
588                $(this).removeClass("no-content").find(".counter").text(counters[categoryId]);
589                self.globalSearchFilter.addToAutocomplete(categoryId, name, parent, categoryType);
590            }
591            else {
592            	$(this).addClass("no-content").find(".counter").text("0");
593                self.globalSearchFilter.removeFromAutocomplete(categoryId);
594            }
595        });
596        self.$wrapper.find(".category-content").each(function () {
597            var tags = $(this).find(".selectable-tag").not(".more"),
598                hiddenTags = $(this).find(".selectable-tag.no-content").not(".more"),
599                categoryName = $(this).data("type");
600            if (tags.length === hiddenTags.length) {
601                $(this).find(".content").addClass("hidden");
602                $(this).find(".search-field").addClass("hidden");
603                $(this).find(".no-results").removeClass("hidden");
604                $(this).find(".apply-search-button").addClass("hidden").prev().addClass("hidden");
605                self.$wrapper.find(".category[data-type='" + categoryName + "']").addClass("empty");
606            }
607            else {
608                $(this).find(".content").removeClass("hidden");
609                $(this).find(".search-field").removeClass("hidden");
610                $(this).find(".no-results").addClass("hidden");;
611                $(this).find(".apply-search-button").removeClass("hidden").prev().removeClass("hidden");
612                self.$wrapper.find(".category[data-type='" + categoryName + "']").removeClass("empty");
613            }
614        });
615    };
616    
617    this.createInitialCounts = function (categoryType) {
618    	var counts = {};
619    	if(self.initialCategories.hasOwnProperty(categoryType)) {
620    		for (var i = 0; i < self.initialCategories[categoryType].length; ++i) {
621    			var category = self.initialCategories[categoryType][i]
622    			counts[category.id] = category.count;
623    		}
624    	}
625    	return counts;
626    }
627    
628    this.resetFilters = function () {
629        self.$wrapper.find(".selected-tag").each(function () {
630            var id = $(this).data("id"),
631                parent = $(this).data("parent");
632            if (id) {
633                self.globalSearchFilter.deselectCategory(id, parent);
634            }
635        });
636        if (self.$searchInput.val()) {
637            self.triggerSearch(true, false);
638        }
639        else {
640            self.removeSearchResults();
641            self.restoreInitialCategories();
642            self.searchUI.$paging.addClass("hidden");
643        }
644    };
645    
646    this.restoreInitialCategories = function (categoryType) {
647    	if(self.initialCategories.hasOwnProperty(categoryType)) {
648    		self.updateCategories(categoryType, self.initialCategories[categoryType]);
649    	}
650    	else {
651            $.each(self.initialCategories, function(key, value) {
652            	self.updateCategories(key, value);
653            });
654    	}
655    }
656    
657    this.updateCategories = function(categoryType, categories) {
658        self.$wrapper.find(".category-content[data-type='" + categoryType + "'] .selectable-tag").not(".more").remove();
659        self.searchUI.appendCategories(categoryType, categories, true);
660        if (categories.length) {
661            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".content").removeClass("hidden");
662            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".search-field").removeClass("hidden");
663            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".no-results").addClass("hidden");
664            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".apply-search-button").removeClass("hidden").prev().removeClass("hidden");;
665            self.$wrapper.find(".category[data-type='" + categoryType + "']").removeClass("empty");
666        }
667        else {
668            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".content").addClass("hidden");
669            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".search-field").addClass("hidden");
670            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".no-results").removeClass("hidden");
671            self.$wrapper.find(".category-content[data-type='" + categoryType + "']").find(".apply-search-button").addClass("hidden").prev().addClass("hidden");
672            self.$wrapper.find(".category[data-type='" + categoryType + "']").addClass("empty");
673        }
674    }
675
676    this.isEmptyCriteria = function (query, exceptCategoryType) {
677    	return self.isEmptySearchTerm(query) && self.isEmptyFilter(query, exceptCategoryType);
678    };
679    
680    this.isEmptySearchTerm = function (query) {
681    	if(query == null) {
682    		query = self.query;
683    	}
684        if (query.searchTerm) {
685            return false;
686        }
687        return true;
688    };
689    
690    this.isEmptyFilter = function (query, exceptCategoryType) {
691    	if(query == null) {
692    		query = self.query;
693    	}
694    	var cats = query.categories;
695        for (var key in cats) {
696            if (cats.hasOwnProperty(key) && cats[key].length > 0) {
697            	if(exceptCategoryType && key === exceptCategoryType) {
698            		continue;
699            	}
700            	return false;
701            }
702        }
703        return true;
704    };
705    
706    this.updateSearchTerm = function (searchTerm) {
707        self.query.searchTerm = searchTerm;  
708    };
709    
710    this.removeSearchResults = function () {
711        self.$searchResults.find(".content").html("");
712        self.$searchResults.find(".header > h2 .text").text(self.i18nMessages.emptyCriteria);
713        this.$searchResults.find(".header > hr").addClass("hidden");
714        self.searchUI.$paging.addClass("hidden");
715        self.$wrapper.find(".search-copy-link").addClass("hidden");
716        self.$wrapper.find(".search-without-filter").addClass("hidden");
717        self.$wrapper.find(".sort-by").addClass("hidden");
718        self.$wrapper.find(".search-statistics").addClass("hidden");
719        self.$wrapper.find(".search-statistics").find("stats").text("");
720        self.globalSearchFilter.pushHistoryState({});
721        self.restoreInitialCategories();
722    };
723    
724    this.loadNextPage = function () {
725        self.curPage++;
726        self.triggerPageLoad();
727    };
728    
729    this.loadPrevPage = function () {
730        self.curPage--;
731        self.triggerPageLoad();
732    };
733    
734    this.loadPage = function (i) {
735        self.curPage = i;
736        self.triggerPageLoad();
737    };
738
739    // Hide mobile search button
740    this.hideMovingSearchButton = function () {
741        $mobileSearchButton = $(".mobile-buttons .search-link-button");
742        if ($mobileSearchButton) {    		
743            $mobileSearchButton.addClass("hidden");
744        }
745    };
746    
747    this.searchHintAlreadyShown = function () {
748        var alreadyShown = localStorage.getItem("sow_search_hint");
749        return alreadyShown !== null;
750    };
751    
752    this.showSearchHint = function () {
753        if (getWindowWidth() < 768) {
754            var offset = $(".filter").offset().top - 90;
755            $(window).scrollTop(offset);
756        }
757        var $overlay = $("#dark-overlay");
758        $overlay.removeClass("hidden");
759        $("body").addClass("no-scrolling");
760        $("#branding-navigation").addClass("low-layer");
761        $("#search-hint").fadeIn(500);
762        localStorage.setItem("sow_search_hint", "true");
763    };
764    
765    this.hideSearchHint = function () {
766        $("#search-hint").fadeOut(500, function () {
767            var $overlay = $("#dark-overlay");
768            $overlay.addClass("hidden");
769            $("body").removeClass("no-scrolling");
770            $("#branding-navigation").removeClass("low-layer");
771        });
772    };
773    
774    this.triggerAthleteSearch = function (newSearch) {
775        if (newSearch) {
776            self.$athleteForm.find("input[name='searchQuery']").val(self.query.searchTerm);
777            self.$athleteForm.find("#filter-offset").val(0);
778        }
779        $.ajax({
780            type: "GET",
781            url: self.athleteServicePath,
782            cache: false,
783            data: self.$athleteForm.serialize(),
784            success: function (data) {
785                if (data.jsonWrapper.data.athletePreviews.length) {
786                    var athletePreviews = data.jsonWrapper.data.athletePreviews,
787                        pagingData = data.jsonWrapper.data.pagingData,
788                        portraitLinks = data.jsonWrapper.data.portraitLinks;
789                    if (newSearch) {
790                        $(".athlete-results").empty();
791                    }
792                    $(".athlete-results").removeClass("hidden").next("hr").removeClass("hidden");
793                    for (var i = 0; i < athletePreviews.length; ++i) {
794                        var $athleteTemplate = self.$athletePrevewTemplate.clone();
795                        
796                        var name = '';
797                        if(null !== athletePreviews[i].firstName) { name += athletePreviews[i].firstName + " "; }
798                        if(null !== athletePreviews[i].lastName) { name += athletePreviews[i].lastName; }
799                        
800                        var sport = '';
801                        if(null !== athletePreviews[i].sport) { sport = athletePreviews[i].sport; }
802                        
803                        var athleteId = '';
804                        if(null !== athletePreviews[i].athleteId) { athleteId = athletePreviews[i].athleteId; }
805                        var portraitLink = self.athletePortraitLinkBase + athleteId;
806                        if (portraitLinks[athletePreviews[i].eventId]) {
807                            portraitLink = portraitLinks[athletePreviews[i].eventId] + "?athleteId=" + athleteId;
808                        }
809                                                
810                        if (null != athletePreviews[i].pictureUrlLarge && athletePreviews[i].pictureUrlLarge != '') {
811                            var pictureUrl = athletePreviews[i].pictureUrlLarge;
812                            $athleteTemplate.find("div.bg").css("background-image",  "url('" + pictureUrl + "')");
813                        }
814                        else {
815                            $athleteTemplate.find("div.bg").addClass('no-picture');
816                        }
817                        
818                        $athleteTemplate.find("a").attr('href', portraitLink);
819                        
820                        $athleteTemplate.find('span.athlete-name').text(name);
821                        
822                        $athleteTemplate.find('span.athlete-sport').text(sport);
823                        
824                        $(".athlete-results").append($athleteTemplate);
825                    }
826                    if (pagingData.offset < pagingData.size) {
827                        self.$athleteForm.find("#filter-offset").val(pagingData.offset);
828                        $(".athlete-results").append($("#load-more-button").clone());
829                    }
830                }
831                else {
832                    $(".athlete-results").addClass("hidden").next("hr").addClass("hidden");
833                }
834            },
835            error: function() {
836                var i = 0;
837            }
838        });
839    }
840};

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.