PageSourceSearch

https://photohuman.space/wp-content/themes/photohuman/source/js/script.js?ver=1.6.815

js photohuman.space collected 2026-09-28 08:54:24 UTC 8,570 bytes, 283 lines download raw bytes

1import { feedback } from "./modules/feedback.js?v=1.6.370";
2import { loadReview } from "./modules/loadReview.js?v=1.6.284";
3import { loadProduct } from "./modules/loadProduct.js?v=1.6.624";
4import { loadPosts } from "./modules/loadPosts.js?v=1.6.624";
5import { favorite } from "./modules/favorite.js?v=1.6.622";
6import { initViewedProducts } from "./modules/viewed.js?v=1.6.284";
7import { shareProduct } from "./modules/shareProduct.js?v=1.6.284";
8import { shareCart } from "./modules/shareCart.js?v=1.6.284";
9import { initCatalogFilter } from "./modules/catalogFilter.js?v=1.6.797";
10import { initApplyCatalogFilter } from "./modules/applyCatalogFilter.js?v=1.6.797";
11import { initCartPage } from "./modules/cartPage.js?v=1.6.284";
12import { initCheckoutPage } from "./modules/checkoutPage.js?v=1.6.441";
13import { initWorksCards } from "./modules/worksCard.js?v=1.6.403";
14import { initSolutionSliders } from "./modules/solutionSlider.js?v=1.6.577";
15import { openModal, debounce, serverReq } from "./modules/helpers.js?v=1.6.769";
16import {
17  accountLogin,
18  accountRecover,
19  accountRegister,
20  accountResetPassword,
21  accountProfile,
22  initAccountAuth,
23} from "./modules/accountAuth.js?v=1.6.389";
24import {
25  accountOrderCancel,
26  initAccountOrders,
27} from "./modules/accountOrders.js?v=1.6.624";
28import { initAccountNav } from "./modules/accountNav.js?v=1.6.325";
29
30const SEARCH_MIN_QUERY_LENGTH = 2;
31
32const getSearchRoots = () => document.querySelectorAll('[data-site-search]');
33
34const hideSearchDropdown = (root) => {
35  const dropdown = root.querySelector('[data-site-search-dropdown]');
36  if (!dropdown) return;
37  dropdown.hidden = true;
38  dropdown.innerHTML = '';
39  root.classList.remove('_suggest-open');
40};
41
42const getSearchToggle = (root) => root.querySelector('.search-icon, .menu-search__icon');
43
44const setSearchLoading = (root, isLoading) => {
45  const toggle = getSearchToggle(root);
46  if (!toggle) return;
47  toggle.classList.toggle('_loading', isLoading);
48  toggle.disabled = isLoading;
49};
50
51const showSearchDropdown = (root, html) => {
52  const dropdown = root.querySelector('[data-site-search-dropdown]');
53  if (!dropdown) return;
54  if (!html) {
55    hideSearchDropdown(root);
56    return;
57  }
58  dropdown.innerHTML = html;
59  dropdown.hidden = false;
60  root.classList.add('_suggest-open');
61};
62
63const fetchSearchSuggestions = async (root, query) => {
64  const trimmed = String(query || '').trim();
65  if (trimmed.length < SEARCH_MIN_QUERY_LENGTH) {
66    hideSearchDropdown(root);
67    setSearchLoading(root, false);
68    return;
69  }
70
71  root._searchRequestSeq = (root._searchRequestSeq || 0) + 1;
72  const requestSeq = root._searchRequestSeq;
73
74  setSearchLoading(root, true);
75
76  const formData = new FormData();
77  formData.append('q', trimmed);
78
79  try {
80    const response = await serverReq(formData, 'searchSuggest');
81
82    if (requestSeq !== root._searchRequestSeq) {
83      return;
84    }
85
86    if (!response?.success || !response.html) {
87      hideSearchDropdown(root);
88      return;
89    }
90
91    showSearchDropdown(root, response.html);
92  } catch {
93    if (requestSeq === root._searchRequestSeq) {
94      hideSearchDropdown(root);
95    }
96  } finally {
97    if (requestSeq === root._searchRequestSeq) {
98      setSearchLoading(root, false);
99    }
100  }
101};
102
103const debouncedSearchFetch = debounce((root, query) => {
104  fetchSearchSuggestions(root, query);
105}, 300);
106
107const bindSearchRoot = (root) => {
108  const input = root.querySelector('input[type="search"], .search-input, .menu-search__input');
109  if (!input) return;
110  input.setAttribute('autocomplete', 'off');
111  input.setAttribute('aria-autocomplete', 'list');
112  input.setAttribute('aria-expanded', 'false');
113  input.addEventListener('input', () => {
114    debouncedSearchFetch(root, input.value);
115    input.setAttribute('aria-expanded', input.value.trim().length >= SEARCH_MIN_QUERY_LENGTH ? 'true' : 'false');
116  });
117  input.addEventListener('focus', () => {
118    if (input.value.trim().length >= SEARCH_MIN_QUERY_LENGTH) {
119      fetchSearchSuggestions(root, input.value);
120    }
121  });
122  root.addEventListener('submit', () => hideSearchDropdown(root));
123};
124
125const initSiteSearch = () => {
126  const roots = getSearchRoots();
127  if (!roots.length) return;
128  roots.forEach(bindSearchRoot);
129  document.addEventListener('click', (event) => {
130    roots.forEach((root) => {
131      if (!root.contains(event.target)) hideSearchDropdown(root);
132    });
133  });
134  document.addEventListener('keydown', (event) => {
135    if (event.key === 'Escape') roots.forEach(hideSearchDropdown);
136  });
137};
138
139
140// События
141const events = {
142  feedback,
143  loadReview,
144  loadProduct,
145  loadPosts,
146  favorite,
147  "share-product": shareProduct,
148  "share-cart": shareCart,
149  "open-feedback-modal": () => openModal("feedbackRequestModal"),
150  "account-login": accountLogin,
151  "account-register": accountRegister,
152  "account-recover": accountRecover,
153  "account-reset-password": accountResetPassword,
154  "account-profile": accountProfile,
155  "account-order-cancel": accountOrderCancel,
156}
157/*
158document.addEventListener("click", function (e) {
159  const target = e.target;
160  const event = target.dataset.click;
161  if(events[event]) {
162    e.preventDefault();
163    events[event](target);
164  }
165});*/
166
167document.addEventListener("click", function (e) {
168  const target = e.target.closest('[data-click]');
169  
170  if(!target) return;
171  const event = target.dataset.click;
172  if(events[event]) {
173    e.preventDefault();
174    events[event](target);
175  }
176});
177
178document.addEventListener("change", (e) => {
179  const target = e.target;
180  const event = target.dataset.change;
181  if(events[event]) {
182    events[event](target);
183  }
184});
185
186document.addEventListener("selectCallback", (e) => {
187    const select = e.detail.select;
188    const eventName = select.dataset.change;
189    if (eventName && events[eventName]) {
190        events[eventName](select);
191    }
192});
193
194document.addEventListener("input", (e) => {
195  const target = e.target;
196  const event = target.dataset.input;
197  if(events[event]) {
198    events[event](target);
199  }
200});
201
202// Формы
203document.addEventListener("submit", (e) => {
204  const form = e.target;
205
206  if (!(form instanceof HTMLFormElement)) {
207    return;
208  }
209
210  const event = form.dataset.submit;
211
212  if (!event) {
213    return;
214  }
215
216  e.preventDefault();
217
218  if (events[event]) {
219    events[event](form);
220  }
221});
222
223
224//Добавление opacity к body при клике на ссылку для мобильнык
225document.addEventListener('DOMContentLoaded', function () {
226  initWorksCards();
227  initSolutionSliders();
228  initViewedProducts();
229  initCatalogFilter();
230  initApplyCatalogFilter();
231  initCartPage();
232  initCheckoutPage();
233  initAccountAuth();
234  initAccountOrders();
235  initAccountNav();
236  initSiteSearch();
237 
238  const isMobile = window.innerWidth <= 768;
239  if (isMobile) {
240    
241    // Функция для сброса opacity
242    function resetOpacity() {
243      document.body.style.opacity = '';
244      document.body.style.transition = '';
245    }
246    
247    // Сбрасываем opacity при загрузке страницы
248    resetOpacity();
249    
250    // Сбрасываем opacity при восстановлении страницы из кэша (кнопка "назад")
251    window.addEventListener('pageshow', function(event) {
252      if (event.persisted) {
253        resetOpacity();
254      }
255    });
256    
257    const links = document.querySelectorAll('a');
258    links.forEach(link => {
259      // Исключаем ссылки, которые не должны добавлять opacity
260      if (link.href &&
261        !link.href.startsWith('javascript:') &&
262        !link.href.startsWith('mailto:') &&
263        !link.href.startsWith('tel:') &&
264        !link.classList.contains('no-opacity') &&
265        !link.classList.contains('glightbox') &&
266        !link.parentElement.classList.contains('no-opacity') &&
267        !link.hasAttribute('data-fls-scrollto') &&
268        link.target !== '_blank') {
269
270        link.addEventListener('click', function (e) {
271          // Добавляем opacity к body
272          document.body.style.transition = 'opacity 0.2s ease';
273          document.body.style.opacity = '0.7';
274          
275          // Убираем opacity после перехода
276          setTimeout(() => {
277            resetOpacity();
278          }, 3000);
279        });
280      }
281    });
282  }
283});

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.