PageSourceSearch

https://ticket-compare.de/wp-content/themes/ticket-compare/blocks/search/searchbox/searchboxViewMobile.js

js ticket-compare.de collected 2026-09-30 11:37:11 UTC 9,049 bytes, 305 lines download raw bytes

1/**
2 * 
3 *  searchbar handler
4 * 
5 * @constructor
6 */
7
8class SearchboxViewMobile {
9  #template = null;
10  #searchDataObj = null;
11  #resultsAreOpen = null;
12  #requestToken = 0;
13
14  /**
15   * 
16   * @param {object} searchDataObj 
17   */
18  constructor(searchDataObj) {
19    this.#searchDataObj = searchDataObj;
20  }
21
22  /**
23   * Init Events
24   * @returns {void}
25   */
26  initEvents() {
27    this.#compile();
28    this.#subscriptions();
29    this.#initState();
30  };
31  /**
32   * subscriptions
33   * @returns {void}
34   */
35  #subscriptions() {
36    const self = this;
37    events.subscribe('SearchboxView_menu_state_change', (data) => {
38      if (!data.fullScreen) self.#closeResultsDiv();
39    });
40  };
41  /**
42   * Init State
43   * @returns {void}
44   */
45  #initState() {
46    this.#clickOutside();
47    let timer;
48    document.getElementsByClassName('search-mobile s')[0].addEventListener('input', async (e) => {
49      clearTimeout(timer);
50      timer = setTimeout(async () => {
51        this.#searchDataObj.searchTerm = e.target.value;
52        if (this.#searchDataObj.searchTerm.length > 1) {
53          document.body.classList.toggle('results-open', !this.#resultsAreOpen);
54          this.#resultsAreOpen = true;
55          const currentRequest = ++this.#requestToken;
56          this.#showLoading();
57          const results = await AlgoliaView.search(this.#searchDataObj);
58          if (currentRequest !== this.#requestToken) return; // stale response, a newer search superseded it
59          this.#renderResults(results, this.#searchDataObj.searchTerm);
60        } else {
61          this.#requestToken++;
62          document.getElementsByClassName('algolia_results mobile')[0].style.display = 'none';
63        }
64      }, 250);
65    });
66  }
67
68  /**
69   * open full screen upon click
70   * @param {object} el
71   * @returns {void}
72   */
73  static open(el) {
74    const container = document.getElementById('searchBoxContainer');
75    if (container) {
76      container.style.zIndex = '99999';
77      container.style.position = 'relative';
78      container.classList.remove('hidden');
79      container.classList.add('flex');
80    }
81
82    document.body.classList.add('full-screen');
83    el.focus();
84
85    events.publish('SearchboxView_menu_state_change', { fullScreen: true });
86
87    if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
88      const form = document.querySelector('.algolia-form');
89      const input = document.querySelector('.search.s');
90
91      Object.assign(form.style, {
92        position: 'fixed',
93        top: '0',
94        left: '0',
95        right: '0',
96        height: 'auto',
97        backgroundColor: '#fff',
98        zIndex: '9999'
99      });
100
101      // Handle keyboard appearance using visualViewport
102      if (window.visualViewport) {
103        // Store handler in form property instead of window
104        form._viewportHandler = () => {
105          const offsetTop = window.visualViewport.offsetTop;
106          form.style.transform = `translateY(${offsetTop}px)`;
107        };
108
109        // Add viewport event listeners
110        window.visualViewport.addEventListener('resize', form._viewportHandler);
111        window.visualViewport.addEventListener('scroll', form._viewportHandler);
112      }
113
114      // Initial focus
115      requestAnimationFrame(() => {
116        input.focus();
117      });
118    }
119
120    setTimeout(() => {
121      if (window.scrollY > 0) {
122        window.scrollTo({ top: 0 });
123      }
124    }, 500);
125  }
126
127  static close(el) {
128    const container = document.getElementById('searchBoxContainer');
129    container.classList.remove('flex');
130    container.classList.add('hidden');
131
132    document.body.classList.remove('full-screen');
133    el.closest('form').querySelector('input[type=search]').value = '';
134
135    if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
136      SearchboxViewMobile.cleanup();
137    }
138
139    events.publish('SearchboxView_menu_state_change', { fullScreen: false });
140  }
141
142  static cleanup() {
143    const form = document.querySelector('.algolia-form');
144    if (!form) return;
145
146    document.documentElement.style.overflow = '';
147    form.style.height = 'auto';
148    form.style.position = '';
149
150    // Clean up viewport event listeners
151    if (window.visualViewport && form._viewportHandler) {
152      window.visualViewport.removeEventListener('resize', form._viewportHandler);
153      window.visualViewport.removeEventListener('scroll', form._viewportHandler);
154      delete form._viewportHandler;
155    }
156
157    form.style.transform = '';
158  }
159
160  /**
161   * hiding results-open css class - in use for default template
162   * @returns {void}
163   */
164  #closeResultsDiv() {
165    document.body.classList.remove('results-open');
166    const algoliaResults = document.querySelector('.algolia_results.mobile');
167    if (algoliaResults) {
168      algoliaResults.style.display = 'none';
169    }
170  }
171
172  /**
173   * Hide the algolia results box when clicking anywhere else
174   * @returns {void}
175   */
176  #clickOutside() {
177    document.body.addEventListener("click", (e) => {
178      const algoliaResults = document.querySelector('.algolia_results.mobile');
179      if (algoliaResults && !e.target.form && !algoliaResults.contains(e.target) && !e.target.input) {
180        algoliaResults.innerHTML = '';
181        algoliaResults.style.display = 'none';
182      }
183    });
184  }
185
186  /**
187   * Analyze and render array
188   * @param {str} results
189   * @param {string} searchTerm
190   * @returns {arr}
191   */
192  /**
193   * show a loading indicator while results are being fetched
194   * @returns {void}
195   */
196  #showLoading() {
197    const algoliaResults = document.querySelector('.algolia_results.mobile');
198    algoliaResults.innerHTML = '<div class="search-loading"><span class="search-loading__spinner"></span></div>';
199    algoliaResults.style.display = 'block';
200  }
201
202  #renderResults(results, searchTerm) {
203    const algoliaResults = document.querySelector('.algolia_results.mobile');
204    algoliaResults.innerHTML = '';
205    algoliaResults.style.display = 'block';
206
207    let hasResults = false;
208
209    for (const [k, v] of Object.entries(results)) {
210      if (v.length > 0) {
211        hasResults = true; // Set to true if at least one result is found
212
213        const sectionName = this.#getSectionName(k);
214        this.#render('section', {
215          sectionName,
216          sectionNameCSSClass: k//SingleFunctionsView.slugify(k)
217        });
218
219        v.forEach(result => {
220          if (['teams', 'stadiums', 'circuits', 'arenas', 'courts', 'venues', 'locations'].includes(k)) {
221            this.#render('teamOrStadium', {
222              title: result.title,
223              url: result.url
224            });
225          } else if (['football', 'tennis', 'concert', 'motorsport', 'rugby', 'basketball', 'cricket', 'fighting', 'nfl'].includes(k)) {
226            this.#render('event', result);
227          } else if (k === 'seatingplans' || k === 'reviews') {
228            this.#renderOther(result, k);
229          } else {
230            this.#renderCategory(result, k);
231          }
232        });
233      }
234    }
235
236    if (!hasResults) {
237      algoliaResults.innerHTML = `
238          <div class="no-results-message">
239              ${AlgoliaSearchJS.translations.no_results}
240          </div>
241      `;
242    }
243
244    this.#render('footer', { searchTerm });
245  }
246
247  #getSectionName(key) {
248    let sectionName = key;
249    if (key.indexOf("_categories") !== -1) {
250      sectionName = key.split('_categories')[0];
251      if (key.indexOf("concert") !== -1) {
252        sectionName = AlgoliaSearchJS.translations.artists;
253      } else {
254        if (sectionName === 'football') {
255          sectionName = AlgoliaSearchJS.translations.football + ' ';
256        }
257        sectionName += ' ' + AlgoliaSearchJS.translations.tournaments;
258      }
259    } else if (key.indexOf("concert") !== -1) {
260      sectionName = AlgoliaSearchJS.translations.concerts;
261    } else if (sectionName === 'seatingplans') {
262      sectionName = 'SEATING PLANS';
263    } else {
264      if (typeof AlgoliaSearchJS.translations[sectionName.toLowerCase()] != 'undefined') {
265        sectionName = AlgoliaSearchJS.translations[sectionName.toLowerCase()];
266      }
267    }
268    return sectionName;
269  }
270
271  #renderCategory(result, key) {
272    const viewAllEventsTranslation = AlgoliaSearchJS.translations.view_all_events;
273    this.#render('category', { title: result.title, url: result.url, viewAllEventsTranslation });
274  }
275
276  #renderOther(result, key) {
277    this.#render('other', { title: result.title, url: result.url });
278  }
279
280  /**
281   * compile handlebars
282   * @returns {void}
283   */
284  #compile() {
285    const source = document.getElementById('algolia').innerHTML;//load full inventory handlebar
286    this.#template = Handlebars.compile(source);
287  };
288
289  /**
290   * render results
291   * @returns {void}
292   */
293  #render(template, data) {
294    document.getElementsByClassName('algolia_results mobile')[0].innerHTML += this.#template({
295      event: template === 'event',
296      section: template === 'section',
297      teamOrStadium: template === 'teamOrStadium',
298      other: template === 'other',
299      category: template === 'category',
300      footer: template === 'footer',
301      data: data,
302      translations: AlgoliaSearchJS.translations
303    });
304  };
305};

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.