PageSourceSearch

https://www.voznesenskycenter.ru/wp-content/themes/voznesensky/assets/js/modules/search.js

js voznesenskycenter.ru collected 2026-10-02 07:18:24 UTC 6,706 bytes, 222 lines download raw bytes

1import { makeAjaxRequest } from './utils/ajax.js';
2
3const MIN_SEARCH_LENGTH = 3;
4const SEARCH_DEBOUNCE_DELAY = 300;
5const MAX_CACHE_SIZE = 50;
6
7const initSearch = () => {
8    const $ = window.jQuery;
9
10    const searchFormMobile = document.getElementById('searchform_mobile');
11    const searchInputMobile = document.getElementById('s');
12
13    if (searchFormMobile && searchInputMobile) {
14        searchFormMobile.addEventListener('submit', (event) => {
15            const value = searchInputMobile.value.trim();
16
17            if (!value || value.length < 3) {
18                event.preventDefault();
19                searchInputMobile.focus();
20            }
21        });
22    }
23
24    const searchFormContent = document.getElementById('searchform_content');
25
26    if (searchFormContent) {
27        const searchInputContent = searchFormContent.querySelector('input[name="s"]');
28        const searchCloseContent = searchFormContent.querySelector('.header-search__close');
29
30        if (searchInputContent) {
31            searchFormContent.addEventListener('submit', (event) => {
32                const value = searchInputContent.value.trim();
33
34                if (!value || value.length < 3) {
35                    event.preventDefault();
36                    searchInputContent.focus();
37                }
38            });
39        }
40
41        if (searchCloseContent && searchInputContent) {
42            searchCloseContent.addEventListener('click', () => {
43                searchInputContent.value = '';
44                searchInputContent.focus();
45            });
46        }
47    }
48
49    if (!$) {
50        return;
51    }
52
53    const searchCache = new Map();
54    const inputStates = new WeakMap();
55    const managedInputs = new Set();
56
57    const setCacheValue = (term, html) => {
58        if (!term) {
59            return;
60        }
61
62        if (searchCache.has(term)) {
63            searchCache.delete(term);
64        }
65
66        searchCache.set(term, html);
67
68        if (searchCache.size > MAX_CACHE_SIZE) {
69            const firstKey = searchCache.keys().next().value;
70            searchCache.delete(firstKey);
71        }
72    };
73
74    const getCachedValue = (term) => (searchCache.has(term) ? searchCache.get(term) : undefined);
75
76    const clearPendingTimeout = (input) => {
77        const state = inputStates.get(input);
78
79        if (state && state.timeoutId) {
80            window.clearTimeout(state.timeoutId);
81            state.timeoutId = null;
82        }
83    };
84
85    const abortActiveRequest = (input) => {
86        const state = inputStates.get(input);
87
88        if (state && state.request && state.request.readyState !== 4) {
89            state.request.abort();
90        }
91
92        if (state) {
93            state.request = null;
94        }
95    };
96
97    const resetInputProcessing = (input) => {
98        clearPendingTimeout(input);
99        abortActiveRequest(input);
100    };
101
102    $('.search_input').each((_, element) => {
103        const input = element;
104        const $input = $(input);
105        const $results = $input.closest('form').find('.search_results');
106
107        if (!$results.length) {
108            return;
109        }
110
111        inputStates.set(input, {
112            timeoutId: null,
113            request: null,
114        });
115        managedInputs.add(input);
116
117        $input.on('keyup', () => {
118            const state = inputStates.get(input);
119            const searchValue = $input.val().trim();
120
121            clearPendingTimeout(input);
122
123            if (searchValue.length < MIN_SEARCH_LENGTH) {
124                abortActiveRequest(input);
125                $results.fadeOut(200).empty();
126                return;
127            }
128
129            if (searchCache.has(searchValue)) {
130                const cachedHtml = getCachedValue(searchValue);
131
132                if (cachedHtml && cachedHtml.trim()) {
133                    $results.fadeIn(200).html(cachedHtml);
134                } else {
135                    $results.fadeOut(200).empty();
136                }
137                return;
138            }
139
140            state.timeoutId = window.setTimeout(() => {
141                state.timeoutId = null;
142
143                abortActiveRequest(input);
144
145                $results.fadeIn(200).html('<div class="search-loading">Поиск...</div>');
146
147                state.request = makeAjaxRequest({
148                    data: {
149                        action: 'ajax_search',
150                        term: searchValue,
151                    },
152                }).done((response) => {
153                    state.request = null;
154
155                    const hasValidResponse = response && response.success && typeof response.data === 'string';
156                    const responseHtml = hasValidResponse ? response.data : '';
157
158                    setCacheValue(searchValue, responseHtml);
159
160                    if (responseHtml.trim()) {
161                        $results.html(responseHtml);
162                    } else if (hasValidResponse) {
163                        $results.fadeOut(200).empty();
164                    } else {
165                        $results.html('<div class="search-error">Ошибка поиска</div>');
166                    }
167                }).fail((xhr, status) => {
168                    state.request = null;
169
170                    if (status !== 'abort') {
171                        $results.html('<div class="search-error">Ошибка соединения</div>');
172                    }
173                });
174            }, SEARCH_DEBOUNCE_DELAY);
175        });
176
177        $input.on('blur', () => {
178            clearPendingTimeout(input);
179        });
180    });
181
182    $(document).on('click', '.header-search__icon', () => {
183        $('.header-search').toggleClass('open');
184    });
185
186    $(document).on('click', '.header-search__close', () => {
187        $('.header-search__input').val('');
188        $('.header-search').removeClass('open');
189        $('.header-search__items').fadeOut();
190        managedInputs.forEach((input) => {
191            resetInputProcessing(input);
192        });
193        $('.header-search .search_results').empty();
194    });
195
196    $(document).on('input', '.header-search__input', function handleInput() {
197        if ($(this).val().length) {
198            $('.header-search__items').fadeIn();
199        } else {
200            $('.header-search__items').fadeOut();
201        }
202    });
203
204    $(document).on('click', (event) => {
205        const $search = $('.header-search');
206
207        if (!$search.hasClass('open')) {
208            return;
209        }
210
211        if (!$search.is(event.target) && $search.has(event.target).length === 0) {
212            $('.header-search').removeClass('open');
213            $('.header-search__input').val('');
214            $('.header-search__items').fadeOut();
215        }
216    });
217};
218
219export default {
220    name: 'search',
221    init: initSearch,
222};

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.