PageSourceSearch

https://www.neuehofburg.at/_assets/5e439985603ece5f8b37fa61dd03305…/suggest_controller.js?1791375810

js neuehofburg.at collected 2026-10-08 15:55:23 UTC 8,669 bytes, 223 lines download raw bytes

1function SuggestController() {
2
3    const _self = this;
4
5    this.run = () => {
6        if ('interactive' === document.readyState || 'complete' === document.readyState) {
7            _initialize();
8        } else {
9            document.addEventListener('DOMContentLoaded', _initialize, {once: true});
10        }
11        jQuery('body').on('tx_solr_updated', _initialize);
12    };
13
14    this.initializeForm = (form, suppress) => {
15
16        if (!(form instanceof HTMLFormElement)) {
17            throw new Error(`Argument 1 of SuggestController.initializeForm has to be an instance of HTMLFormElement, "${typeof form}" given.`);
18        }
19
20        suppress = suppress || false;
21
22        const
23            searchBox = form.querySelector('.tx-solr-suggest') || form,
24            autocomplete = form.querySelector('.tx-solr-autocomplete') || document.body,
25            focusElement = form.querySelector('.tx-solr-suggest-focus')
26        ;
27
28        searchBox.style.position = 'relative';
29        if (focusElement) focusElement.focus();
30
31            // prevent submission of empty search form
32        form.addEventListener('submit', event => {
33            if (!searchBox.value) {
34                event.preventDefault();
35                if (focusElement) {
36                    focusElement.focus();
37                }
38            }
39        });
40
41            // initialize actual autocomplete
42        const options = {
43            serviceUrl: form.dataset.suggest,
44            dataType: 'json',
45            ajaxSettings: {
46                jsonp: "tx_solr[callback]"
47            },
48            paramName: form.dataset.suggestParam || 'tx_solr[queryString]',
49            groupBy: 'category',
50            maxHeight: 1000,
51            appendTo: autocomplete,
52            autoSelectFirst: false,
53            triggerSelectOnValidInput: false,
54            width: searchBox.offsetWidth * 0.66,
55            minChars: suppress ? 9999 : 1,
56            transformResult: response => {
57                const result = {suggestions: []};
58                    // ad first object to select at the very top
59                if (response.documents) {
60                    response.documents.some(document => {
61                        if ('tx_theme_objectdb_object' === document.type) {
62                            result.suggestions.push({
63                                value: 'Objekt',
64                                data: document
65                            });
66                            return true;
67                        }
68                    });
69                }
70                    // ad regular suggestions
71                if (response.suggestions) {
72                    for (let suggestion in response.suggestions) {
73                        if (!response.suggestions.hasOwnProperty(suggestion)) {
74                            continue;
75                        }
76                        result.suggestions.push({
77                            value: suggestion,
78                            data:  {category: 'suggestion', count: response.suggestions[suggestion]}
79                        });
80                    }
81                }
82                    // treat a response object with exclusive numeric keys as array of suggestions
83                    // (for object quick search)
84                if (Object.keys(response).every(_isNumeric)) {
85                    Object.values(response).slice(0, 10).forEach(suggestion => {
86                        result.suggestions.push({
87                            value: suggestion,
88                            data: {category: 'suggestion', count: 1}
89                        });
90                    });
91                }
92                return result;
93            },
94            beforeRender: container => {
95                    // remove first group header
96                container.find('.autocomplete-group:first').remove();
97                container.addClass('tx-solr-autosuggest');
98                    // add active class to container
99                jQuery(searchBox).parent().addClass('autocomplete-active').fadeIn();
100            },
101            formatResult: (suggestion, currentValue) => {
102                if (!currentValue) return suggestion.value;
103                    // regular suggestion
104                if ('suggestion' === suggestion.data.category) {
105                    return _parseHtml(suggestion.value, currentValue);
106                }
107                    // object type suggestion
108                if ('tx_theme_objectdb_object' === suggestion.data.type) {
109                    return '<div class="' + suggestion.data.type + '">' +
110                            '<a href="' + suggestion.data.link + '" class="internal-link">' +
111                                '<span class="badge">Online Sammlung</span><br />' +
112                                '<span>' + _parseHtml(suggestion.data.title, currentValue) + '</span>' +
113                                '<span class="icon material-symbols-outlined flex-shrink-0 align-bottom notranslate" aria-hidden="true">arrow_outward</span>' +
114                            '</a>' +
115                        '</div>'
116                    ;
117                }
118                    // default suggestion
119                return '<div class="' + suggestion.data.type + '">' +
120                        (suggestion.data.previewImage ? '<figure' + (suggestion.data.hasVideo ? ' class="hasVideo"' : '') + '>
120<img src="' + suggestion.data.previewImage + '" /></figure>' : '') +
121                        '<a href="' + suggestion.data.link + '" class="internal-link">' + _parseHtml(suggestion.data.title, currentValue) + '</a>' +
122                    '</div>'
123                ;
124            },
125            onSelect: suggestion => {
126                if (suggestion.data.link) {
127                    if (suggestion.data.link.indexOf('https://www.youtube.com') === 0) {
128                        openVideoOverlay(suggestion.data.link);
129                    } else {
130                        window.location.href = suggestion.data.link;
131                    }
132                    return true;
133                }
134                _submitForm(form);
135            }
136        };
137
138        const filters = _extractFilters(form);
139        if (filters.hasOwnProperty('tx_solr')) {
140            options.params = filters;
141        }
142
143        jQuery(searchBox)
144            .devbridgeAutocomplete(options)
145            .on('blur', () => {
146                searchBox.parentNode.classList.remove('autocomplete-active');
147                const $subject = jQuery(this);
148                setTimeout(() => {$subject.devbridgeAutocomplete('hide');}, 200);
149            })
150        ;
151
152        if (suppress) {
153            requestAnimationFrame(() => {
154                jQuery(searchBox)
155                    .blur()
156                    .devbridgeAutocomplete().setOptions({minChars: 1})
157                ;
158            });
159        }
160    };
161
162    function _submitForm(form) {
163            // ugly check to properly submit forms utilizing htmx
164        if (form.hasAttribute('hx-post')) {
165            form.requestSubmit();
166        } else {
167            jQuery(form).trigger('submit');
168        }
169    }
170
171    function _extractFilters(form) {
172        if (!form.dataset.suggestFq) {
173            return {};
174        }
175
176        const filters = {};
177
178        form.dataset.suggestFq.split(',').forEach(filter => {
179            let value = filter.split(':');
180            if (value.length < 2) {
181                return;
182            }
183            let name = value.shift();
184            value = value.join(':');
185            if (!name || !value) {
186                return;
187            }
188            filters['tx_solr'] = filters.tx_solr || {};
189            filters.tx_solr['additionalFilters'] = filters.tx_solr.additionalFilters || {};
190            filters.tx_solr.additionalFilters[name] = value;
191        });
192
193        return filters;
194    }
195
196    function _initialize() {
197        [...document.querySelectorAll('form[data-suggest]')].forEach(form => {
198            _self.initializeForm(form);
199        });
200    }
201
202    function _parseHtml(html, currentValue) {
203        return html
204            .replace(
205                new RegExp(`(${jQuery.Autocomplete.utils.escapeRegExChars(currentValue.trim())})`, 'gi'),
206                '<strong>$1<\/strong>'
207            )
208            .replace(/&/g, '&amp;')
209            .replace(/</g, '&lt;')
210            .replace(/>/g, '&gt;')
211            .replace(/"/g, '&quot;')
212            .replace(/&lt;(\/?strong)&gt;/g, '<$1>')
213        ;
214    }
215
216    function _isNumeric(value) {
217        if ('number' === typeof value) return true;
218        if ('string' !== typeof value) return false;
219        return !isNaN(value) && !isNaN(parseFloat(value));
220    }
221}
222
223(new SuggestController()).run();

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.