PageSourceSearch

https://deps.ua/media/mod_deps_product_search/js/search.js?39da90

js deps.ua collected 2026-09-25 05:21:53 UTC 11,302 bytes, 415 lines download raw bytes

1/**
2 * mod_deps_product_search live AJAX search
3 */
4(function () {
5  'use strict';
6
7  const DEBOUNCE_MS = 280;
8
9  function appendSearchParam(base, kw) {
10    const sep = base.indexOf('?') >= 0 ? '&' : '?';
11    return base + sep + 'search=' + encodeURIComponent(String(kw));
12  }
13
14  function ensureScriptOptionsLoaded() {
15    if (typeof Joomla !== 'undefined' && typeof Joomla.loadOptions === 'function') {
16      Joomla.loadOptions();
17    }
18  }
19
20  function getConfigFromScriptTag(optionsKey) {
21    const scripts = document.querySelectorAll('script.joomla-script-options');
22
23    for (let i = 0; i < scripts.length; i += 1) {
24      const content = scripts[i].text || scripts[i].textContent;
25
26      if (!content) {
27        continue;
28      }
29
30      try {
31        const parsed = JSON.parse(content);
32
33        if (parsed && parsed[optionsKey]) {
34          return parsed[optionsKey];
35        }
36      } catch (e) {
37        // ignore malformed script options
38      }
39    }
40
41    return null;
42  }
43
44  function getConfig(wrapper) {
45    const moduleId = wrapper.dataset.moduleId;
46    const optionsKey = 'mod_deps_product_search.module.' + moduleId;
47
48    ensureScriptOptionsLoaded();
49
50    if (typeof Joomla !== 'undefined' && Joomla.getOptions(optionsKey)) {
51      return Joomla.getOptions(optionsKey);
52    }
53
54    const fromScript = getConfigFromScriptTag(optionsKey);
55
56    if (fromScript) {
57      return fromScript;
58    }
59
60    return window.modDepsProductSearch && window.modDepsProductSearch[moduleId]
61      ? window.modDepsProductSearch[moduleId]
62      : null;
63  }
64
65  function getSearchLink(wrapper, config) {
66    if (config && config.searchLink) {
67      return config.searchLink;
68    }
69
70    if (wrapper.dataset.searchLink) {
71      return wrapper.dataset.searchLink;
72    }
73
74    const form = wrapper.querySelector('.search-form');
75
76    if (form && form.action) {
77      return form.action;
78    }
79
80    return '';
81  }
82
83  function navigateToSearchResults(wrapper, config, keyword) {
84    const trimmed = String(keyword).trim();
85    const base = getSearchLink(wrapper, config);
86
87    if (!trimmed || !base) {
88      return false;
89    }
90
91    window.location.href = appendSearchParam(base, trimmed);
92    return true;
93  }
94
95  function withCsrf(fields, config) {
96    const payload = Object.assign({}, fields);
97
98    if (config && config.csrfToken && config.csrfTokenName) {
99      payload[config.csrfTokenName] = config.csrfToken;
100    }
101
102    return payload;
103  }
104
105  function buildPostBody(fields) {
106    const body = new URLSearchParams();
107    Object.keys(fields).forEach((key) => {
108      if (fields[key] !== undefined && fields[key] !== null) {
109        body.append(key, String(fields[key]));
110      }
111    });
112    return body.toString();
113  }
114
115  function buildAjaxUrl(query) {
116    const paths = typeof Joomla !== 'undefined' ? Joomla.getOptions('system.paths') : null;
117    const root = paths && paths.root ? paths.root : '/';
118    const base = root.endsWith('/') ? root : root + '/';
119    return base + 'index.php?' + query;
120  }
121
122  function createSearchState() {
123    return {
124      debounceTimer: null,
125      activeXhr: null,
126      requestId: 0,
127    };
128  }
129
130  function clearDebounce(state) {
131    if (state.debounceTimer) {
132      clearTimeout(state.debounceTimer);
133      state.debounceTimer = null;
134    }
135  }
136
137  function abortActiveRequest(state) {
138    if (state.activeXhr) {
139      state.activeXhr.abort();
140      state.activeXhr = null;
141    }
142  }
143
144  function setSearching(wrapper, isSearching) {
145    wrapper.classList.toggle('is-searching', isSearching);
146  }
147
148  function renderSearchResponse(wrapper, config, response, keyword) {
149    const popup = wrapper.querySelector('.search-popup');
150    const input = wrapper.querySelector('.vssearch');
151    const currentKeyword = input ? input.value : '';
152
153    if (String(currentKeyword).trim() !== String(keyword).trim()) {
154      return;
155    }
156
157    if (popup) {
158      popup.innerHTML = response;
159    }
160
161    if (popup && popup.querySelector('.has-more-results')) {
162      const hrefAll = appendSearchParam(getSearchLink(wrapper, config), String(currentKeyword).trim());
163      const btn = document.createElement('a');
164      btn.href = hrefAll;
165      btn.className = 'button_default mt-10';
166      btn.textContent = Joomla.JText._('MOD_DEPS_PRODUCT_SEARCH_VIEW_ALL_RESULTS');
167
168      if (window.innerWidth >= 768) {
169        const left = popup.querySelector('.search-left');
170        if (left) {
171          left.appendChild(btn);
172        }
173      } else {
174        popup.appendChild(btn);
175      }
176    }
177
178    const resetBtn = wrapper.querySelector('.search-reset-btn');
179    if (resetBtn) {
180      resetBtn.style.visibility = 'visible';
181    }
182
183    if (popup) {
184      popup.style.display = 'block';
185      popup.classList.remove('history-active');
186    }
187  }
188
189  function clearSearchResults(wrapper) {
190    const popup = wrapper.querySelector('.search-popup');
191    abortActiveRequest(wrapper._depsSearchState);
192    setSearching(wrapper, false);
193
194    if (popup) {
195      popup.innerHTML = '';
196      popup.style.display = 'none';
197      popup.classList.remove('history-active');
198    }
199  }
200
201  function runSearch(wrapper, keyword, state) {
202    const config = getConfig(wrapper);
203    if (!config) {
204      return;
205    }
206
207    const minChars = parseInt(wrapper.dataset.minChars || '2', 10);
208    const lang = wrapper.dataset.lang || '';
209    const trimmed = String(keyword).trim();
210
211    if (trimmed.length <= minChars) {
212      clearSearchResults(wrapper);
213      return;
214    }
215
216    abortActiveRequest(state);
217    state.requestId += 1;
218    const requestId = state.requestId;
219
220    setSearching(wrapper, true);
221
222    state.activeXhr = Joomla.request({
223      url: buildAjaxUrl('option=com_ajax&module=' + config.ajaxModule + '&format=raw'),
224      method: 'POST',
225      data: buildPostBody(withCsrf({
226        keyword: trimmed,
227        module_id: wrapper.dataset.moduleId,
228        lang: lang,
229      }, config)),
230      onSuccess(response) {
231        if (requestId !== state.requestId) {
232          return;
233        }
234        renderSearchResponse(wrapper, config, response, trimmed);
235      },
236      onError(xhr) {
237        if (!xhr || xhr.status === 0) {
238          return;
239        }
240      },
241      onComplete() {
242        if (requestId !== state.requestId) {
243          return;
244        }
245        state.activeXhr = null;
246        setSearching(wrapper, false);
247      },
248    });
249  }
250
251  function scheduleSearch(wrapper, state, immediate) {
252    clearDebounce(state);
253
254    if (immediate) {
255      const keyword = wrapper.querySelector('.vssearch')?.value || '';
256      runSearch(wrapper, keyword, state);
257      return;
258    }
259
260    state.debounceTimer = setTimeout(() => {
261      state.debounceTimer = null;
262      const keyword = wrapper.querySelector('.vssearch')?.value || '';
263      runSearch(wrapper, keyword, state);
264    }, DEBOUNCE_MS);
265  }
266
267  function initProductSearch() {
268    ensureScriptOptionsLoaded();
269
270    document.querySelectorAll('.mod-deps-product-search').forEach(function (wrapper) {
271      const input = wrapper.querySelector('.vssearch');
272      const config = getConfig(wrapper);
273      const popup = wrapper.querySelector('.search-popup');
274      const form = wrapper.querySelector('.search-form');
275      const state = createSearchState();
276
277      wrapper._depsSearchState = state;
278
279      if (!input) {
280        return;
281      }
282
283      input.addEventListener('input', function () {
284        const resetBtn = wrapper.querySelector('.search-reset-btn');
285        if (resetBtn) {
286          resetBtn.style.visibility = input.value ? 'visible' : 'hidden';
287        }
288        scheduleSearch(wrapper, state, false);
289      });
290
291      input.addEventListener('focus', function () {
292        wrapper.classList.add('active');
293        document.body.classList.add('mod-deps-product-search-active');
294        if (popup && popup.classList.contains('history-active')) {
295          popup.style.display = 'block';
296        }
297      });
298
299      wrapper.addEventListener('click', function (event) {
300        const target = event.target.closest('.search-reset-btn');
301        if (!target) {
302          return;
303        }
304
305        clearDebounce(state);
306        abortActiveRequest(state);
307        state.requestId += 1;
308
309        input.value = '';
310        target.style.visibility = 'hidden';
311        setSearching(wrapper, false);
312
313        if (!config) {
314          return;
315        }
316
317        Joomla.request({
318          url: buildAjaxUrl('option=com_ajax&module=' + config.ajaxModule + '&method=clearsearch&format=raw'),
319          method: 'POST',
320          data: buildPostBody(withCsrf({ module_id: wrapper.dataset.moduleId }, config)),
321          onSuccess(response) {
322            if (popup) {
323              popup.innerHTML = response || '';
324              if (!response) {
325                popup.style.display = 'none';
326              } else {
327                popup.classList.add('history-active');
328                popup.style.display = 'block';
329              }
330            }
331          },
332        });
333      });
334
335      wrapper.addEventListener('click', function (event) {
336        const phraseBtn = event.target.closest('.search-phrase');
337        if (!phraseBtn) {
338          return;
339        }
340
341        const phrase = phraseBtn.getAttribute('data-phrase') || '';
342        input.value = phrase;
343        scheduleSearch(wrapper, state, true);
344      });
345
346      if (form) {
347        form.addEventListener('submit', function (event) {
348          event.preventDefault();
349          const keyword = input.value.trim();
350
351          if (!keyword) {
352            return;
353          }
354
355          clearDebounce(state);
356          abortActiveRequest(state);
357          document.body.classList.remove('mod-deps-product-search-active');
358          wrapper.classList.remove('active');
359          navigateToSearchResults(wrapper, config, keyword);
360        });
361      }
362
363      wrapper.addEventListener('click', function (event) {
364        if (!event.target.closest('.clear-history')) {
365          return;
366        }
367
368        if (!config) {
369          return;
370        }
371
372        Joomla.request({
373          url: buildAjaxUrl('option=com_ajax&module=' + config.ajaxModule + '&method=clearhistory&format=raw'),
374          method: 'POST',
375          data: buildPostBody(withCsrf({ module_id: wrapper.dataset.moduleId }, config)),
376          onSuccess() {
377            if (popup) {
378              popup.classList.remove('history-active');
379              popup.style.display = 'none';
380            }
381          },
382        });
383      });
384
385      const bg = wrapper.querySelector('.search_popup_bg');
386      if (bg) {
387        bg.addEventListener('click', function () {
388          document.body.classList.remove('mod-deps-product-search-active');
389          wrapper.classList.remove('active');
390          if (popup) {
391            popup.style.display = 'none';
392          }
393        });
394      }
395
396      input.addEventListener('keydown', function (event) {
397        if (event.key === 'Escape') {
398          clearDebounce(state);
399          abortActiveRequest(state);
400          wrapper.classList.remove('active');
401          document.body.classList.remove('mod-deps-product-search-active');
402          if (popup) {
403            popup.style.display = 'none';
404          }
405        }
406      });
407    });
408  }
409
410  if (document.readyState === 'loading') {
411    document.addEventListener('DOMContentLoaded', initProductSearch);
412  } else {
413    initProductSearch();
414  }
415})();

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.