PageSourceSearch

https://asiapacificpadeltour.com/wp-content/plugins/MyLeagueAPPT/js/appt-results-v2.js?ver=1787754538

js asiapacificpadeltour.com collected 2026-10-04 16:54:05 UTC 33,778 bytes, 955 lines download raw bytes

1(function () {
2  'use strict';
3
4  function qs(root, selector) {
5    return root ? root.querySelector(selector) : null;
6  }
7
8  function qsa(root, selector) {
9    return Array.prototype.slice.call(root ? root.querySelectorAll(selector) : []);
10  }
11
12
13  function initResultsCountrySelect(root, form) {
14    var wraps = qsa(root, '[data-appt-results-countryselect-field]');
15    var $ = window.jQuery;
16
17    function parseJson(value) {
18      try {
19        var parsed = JSON.parse(value || '[]');
20        return Array.isArray(parsed) ? parsed : [];
21      } catch (error) {
22        return [];
23      }
24    }
25
26    function normalizeCode(value) {
27      value = String(value == null ? '' : value).trim();
28      return /^[a-z]{2}$/i.test(value) ? value.toUpperCase() : '';
29    }
30
31    function fieldName(wrap) {
32      return wrap ? (wrap.getAttribute('data-filter-name') || '') : '';
33    }
34
35    function visibleInput(wrap) {
36      return qs(wrap, '[data-appt-results-countryselect-input]');
37    }
38
39    function hiddenInput(wrap) {
40      return qs(wrap, '[data-appt-results-country-code]');
41    }
42
43    function clearButton(wrap) {
44      return qs(wrap, '[data-appt-results-countryselect-clear]');
45    }
46
47    function countryList(wrap) {
48      return qs(wrap, '.country-list');
49    }
50
51    function placeholder(wrap) {
52      return wrap ? (wrap.getAttribute('data-placeholder') || 'All Countries') : 'All Countries';
53    }
54
55    function flagUrl(wrap, code) {
56      code = normalizeCode(code);
57      var base = wrap ? (wrap.getAttribute('data-flags-base') || '') : '';
58      return code && base ? base.replace(/\/?$/, '/') + code + '.png' : '';
59    }
60
61    function paintFlag(flag, wrap, code) {
62      if (!flag) return;
63      code = normalizeCode(code);
64      if (!code) {
65        flag.style.backgroundImage = '';
66        flag.classList.add('flag--all');
67        return;
68      }
69      flag.classList.remove('flag--all');
70      var url = flagUrl(wrap, code);
71      flag.style.backgroundImage = url ? 'url("' + url + '")' : '';
72    }
73
74    function ensureAllOption(wrap) {
75      var list = countryList(wrap);
76      if (!list) return null;
77      var existing = qs(list, '.appt-rv2-countryselect-all');
78      if (existing) {
79        var existingName = qs(existing, '.country-name');
80        if (existingName) existingName.textContent = placeholder(wrap);
81        return existing;
82      }
83
84      var item = document.createElement('li');
85      item.className = 'appt-rv2-countryselect-all';
86      item.setAttribute('role', 'option');
87      item.setAttribute('data-country-code', '');
88      item.innerHTML = '<span class="flag flag--all" aria-hidden="true"></span><span class="country-name"></span>';
89      qs(item, '.country-name').textContent = placeholder(wrap);
90      list.insertBefore(item, list.firstChild);
91      return item;
92    }
93
94    function syncAllOption(wrap) {
95      var item = ensureAllOption(wrap);
96      var hidden = hiddenInput(wrap);
97      if (!item) return;
98      var active = !(hidden && normalizeCode(hidden.value));
99      item.classList.toggle('active', active);
100      item.setAttribute('aria-selected', active ? 'true' : 'false');
101    }
102
103    function paintFlags(wrap) {
104      ensureAllOption(wrap);
105      qsa(wrap, '.country-list .country').forEach(function (item) {
106        paintFlag(qs(item, '.flag'), wrap, item.getAttribute('data-country-code') || '');
107      });
108      syncAllOption(wrap);
109    }
110
111    function close(wrap) {
112      var list = countryList(wrap);
113      if (list) list.classList.add('hide');
114      var arrow = qs(wrap, '.selected-flag .arrow');
115      if (arrow) arrow.classList.remove('up');
116    }
117
118    function open(wrap) {
119      if (!wrap || wrap.classList.contains('is-locked')) return;
120      var input = visibleInput(wrap);
121      var list = countryList(wrap);
122      var flag = qs(wrap, '.selected-flag');
123      if (input && input.disabled) return;
124
125      if (list && list.classList.contains('hide')) {
126        /* Use the same jQuery trigger as Players & Teams. */
127        if ($ && flag) {
128          $(flag).trigger('click');
129        }
130
131        /* Reliable fallback for the custom chevron hit area. */
132        if (list.classList.contains('hide')) {
133          list.classList.remove('hide');
134          var arrow = qs(wrap, '.selected-flag .arrow');
135          if (arrow) arrow.classList.add('up');
136        }
137      }
138
139      window.setTimeout(function () {
140        ensureAllOption(wrap);
141        paintFlags(wrap);
142      }, 0);
143    }
144
145    function pluginCode(wrap) {
146      var input = visibleInput(wrap);
147      var hidden = hiddenInput(wrap);
148      if (!input) return '';
149      var visible = String(input.value || '').trim();
150      var stored = hidden ? normalizeCode(hidden.value) : '';
151      if (!visible && !stored) return '';
152
153      /*
154       * When changing directly from one country to another, the APPT hidden
155       * field can still contain the previous ISO code while countrySelect has
156       * already marked the new row as active. Always trust the active row first.
157       */
158      var active = qs(wrap, '.country-list .country.active');
159      var activeCode = visible && active
160        ? normalizeCode(active.getAttribute('data-country-code') || '')
161        : '';
162      if (activeCode) return activeCode;
163
164      var generated = input.id ? document.getElementById(input.id + '_code') : null;
165      var generatedCode = generated ? normalizeCode(generated.value) : '';
166      if (generatedCode) return generatedCode;
167
168      return stored;
169    }
170
171    function countryItemCode(item) {
172      return item ? normalizeCode(item.getAttribute('data-country-code') || '') : '';
173    }
174
175    function countryItemLabel(item) {
176      if (!item) return '';
177      var label = qs(item, '.country-name');
178      return String(label ? label.textContent : item.textContent || '')
179        .replace(/\s+/g, ' ')
180        .trim();
181    }
182
183    function selectCountryItem(wrap, item, triggerChange) {
184      var code = countryItemCode(item);
185      var input = visibleInput(wrap);
186      var hidden = hiddenInput(wrap);
187      if (!code || !input || !hidden) return false;
188
189      qsa(wrap, '.country-list .country').forEach(function (row) {
190        row.classList.remove('active', 'highlight');
191        row.removeAttribute('aria-selected');
192      });
193      item.classList.add('active');
194      item.setAttribute('aria-selected', 'true');
195
196      input.value = countryItemLabel(item) || code;
197      input.setAttribute('data-country-code', code);
198
199      var generated = input.id ? document.getElementById(input.id + '_code') : null;
200      if (generated) generated.value = code.toLowerCase();
201
202      hidden.value = code;
203      wrap.classList.add('has-value');
204      var clear = clearButton(wrap);
205      if (clear) clear.hidden = false;
206
207      paintFlag(qs(wrap, '.selected-flag .flag'), wrap, code);
208      syncAllOption(wrap);
209      close(wrap);
210
211      if (triggerChange) {
212        hidden.dispatchEvent(new Event('change', { bubbles: true }));
213      }
214
215      return true;
216    }
217
218    function clearPlugin(wrap) {
219      var input = visibleInput(wrap);
220      if (!input) return;
221      input.value = '';
222      var generated = input.id ? document.getElementById(input.id + '_code') : null;
223      if (generated) generated.value = '';
224      qsa(wrap, '.country-list .country.active').forEach(function (item) {
225        item.classList.remove('active');
226        item.removeAttribute('aria-selected');
227      });
228      paintFlag(qs(wrap, '.selected-flag .flag'), wrap, '');
229      syncAllOption(wrap);
230    }
231
232    function setValue(name, value, triggerChange) {
233      var code = normalizeCode(value);
234      wraps.forEach(function (wrap) {
235        if (fieldName(wrap) !== name) return;
236        var input = visibleInput(wrap);
237        var hidden = hiddenInput(wrap);
238        var clear = clearButton(wrap);
239
240        if (hidden) hidden.value = code;
241        wrap.classList.toggle('has-value', !!code);
242        if (clear) clear.hidden = !code;
243
244        if ($ && $.fn && typeof $.fn.countrySelect === 'function' && input && input.dataset.apptResultsCountryReady === '1') {
245          try {
246            if (code) $(input).countrySelect('setCountry', code.toLowerCase());
247            else clearPlugin(wrap);
248          } catch (error) {
249            if (!code) clearPlugin(wrap);
250          }
251        } else if (!code) {
252          clearPlugin(wrap);
253        }
254
255        paintFlag(qs(wrap, '.selected-flag .flag'), wrap, code);
256        syncAllOption(wrap);
257
258        if (triggerChange && hidden) {
259          hidden.dispatchEvent(new Event('change', { bubbles: true }));
260        }
261      });
262    }
263
264    function sync() {
265      wraps.forEach(function (wrap) {
266        var hidden = hiddenInput(wrap);
267        if (!hidden) return;
268        var code = pluginCode(wrap);
269        if (code || !String(visibleInput(wrap).value || '').trim()) hidden.value = code;
270        wrap.classList.toggle('has-value', !!hidden.value);
271        var clear = clearButton(wrap);
272        if (clear) clear.hidden = !hidden.value;
273        paintFlag(qs(wrap, '.selected-flag .flag'), wrap, hidden.value);
274        syncAllOption(wrap);
275      });
276    }
277
278    function init() {
279      if (!wraps.length) return true;
280      if (!$ || !$.fn || typeof $.fn.countrySelect !== 'function') return false;
281
282      wraps.forEach(function (wrap) {
283        var input = visibleInput(wrap);
284        var hidden = hiddenInput(wrap);
285        if (!input || !hidden || input.dataset.apptResultsCountryReady === '1') return;
286        input.dataset.apptResultsCountryReady = '1';
287
288        var onlyCountries = parseJson(wrap.getAttribute('data-only-countries'))
289          .map(function (code) { return String(code || '').toLowerCase(); })
290          .filter(function (code) { return /^[a-z]{2}$/.test(code); });
291
292        $(input).countrySelect({
293          preferredCountries: [],
294          responsiveDropdown: true,
295          onlyCountries: onlyCountries.length ? onlyCountries : undefined
296        });
297
298        var instance = $.data(input, 'plugin_countrySelect');
299        if (instance && instance.ns) $(input).off('blur' + instance.ns);
300
301        $(input).on('click.apptResultsCountry', function (event) {
302          event.preventDefault();
303          event.stopPropagation();
304          open(wrap);
305        });
306
307        $(input).on('focus.apptResultsCountry', function () {
308          window.setTimeout(function () { paintFlags(wrap); }, 0);
309        });
310
311        $(wrap).on('click.apptResultsCountry', function (event) {
312          var target = event.target;
313          if (target && target.closest && target.closest('[data-appt-results-countryselect-clear]')) return;
314          if (target && target.closest && target.closest('.appt-rv2-countryselect-all')) return;
315          // Let the plugin process its selected flag exactly once.
316          if (target && target.closest && target.closest('.selected-flag')) return;
317          if (target && target.closest && target.closest('.country-list')) return;
318          event.preventDefault();
319          event.stopPropagation();
320          open(wrap);
321          input.focus();
322        });
323
324        $(wrap).on('click.apptResultsCountry', '.selected-flag', function () {
325          window.setTimeout(function () { paintFlags(wrap); }, 0);
326        });
327
328        /*
329         * Same stable selection flow used by Players & Teams: commit the new
330         * ISO code on mousedown, before countrySelect's blur/click sequence can
331         * expose the previous hidden value to the AJAX form serializer.
332         */
333        $(wrap).on('mousedown.apptResultsCountry', '.country-list .country', function (event) {
334          event.preventDefault();
335          event.stopPropagation();
336          if (typeof event.stopImmediatePropagation === 'function') {
337            event.stopImmediatePropagation();
338          }
339          selectCountryItem(wrap, this, true);
340        });
341
342        $(wrap).on('click.apptResultsCountry', '.country-list .country', function (event) {
343          event.preventDefault();
344          event.stopPropagation();
345          if (typeof event.stopImmediatePropagation === 'function') {
346            event.stopImmediatePropagation();
347          }
348        });
349
350        $(wrap).on('mousedown.apptResultsCountry click.apptResultsCountry', '.appt-rv2-countryselect-all', function (event) {
351          event.preventDefault();
352          event.stopPropagation();
353          event.stopImmediatePropagation();
354          clearPlugin(wrap);
355          setValue(fieldName(wrap), '', true);
356          close(wrap);
357        });
358
359        $(input).on('change.apptResultsCountry', function () {
360          var code = pluginCode(wrap);
361          hidden.value = code;
362          wrap.classList.toggle('has-value', !!code);
363          var clear = clearButton(wrap);
364          if (clear) clear.hidden = !code;
365          paintFlag(qs(wrap, '.selected-flag .flag'), wrap, code);
366          syncAllOption(wrap);
367          hidden.dispatchEvent(new Event('change', { bubbles: true }));
368        });
369
370        $(input).on('keydown.apptResultsCountry', function (event) {
371          if (event.key === 'Escape') setValue(fieldName(wrap), '', true);
372        });
373
374        setValue(fieldName(wrap), hidden.value || wrap.getAttribute('data-selected-code') || '', false);
375        window.setTimeout(function () { paintFlags(wrap); }, 0);
376      });
377
378      return true;
379    }
380
381    var ready = init();
382    if (!ready) {
383      window.setTimeout(init, 250);
384      window.addEventListener('load', init, { once: true });
385    }
386
387    form.addEventListener('click', function (event) {
388      var clear = event.target.closest('[data-appt-results-countryselect-clear]');
389      if (!clear) return;
390      event.preventDefault();
391      event.stopPropagation();
392      var wrap = clear.closest('[data-appt-results-countryselect-field]');
393      if (wrap) setValue(fieldName(wrap), '', true);
394    });
395
396    return {
397      setValue: setValue,
398      sync: sync,
399      text: function (name) {
400        var found = '';
401        wraps.some(function (wrap) {
402          if (fieldName(wrap) !== name) return false;
403          var input = visibleInput(wrap);
404          found = input ? String(input.value || '').trim() : '';
405          return true;
406        });
407        return found;
408      }
409    };
410  }
411
412  function isMobile() {
413    return !window.matchMedia || window.matchMedia('(max-width: 900px)').matches;
414  }
415
416  function showLoader(content) {
417    if (!content) return;
418    content.classList.add('is-loading');
419    if (!qs(content, '.appt-rv2-loader')) {
420      var loader = document.createElement('div');
421      loader.className = 'appt-rv2-loader';
422      loader.setAttribute('role', 'status');
423      loader.setAttribute('aria-live', 'polite');
424      loader.innerHTML = '<i aria-hidden="true"></i><strong>' + ((window.apptResultsV2 && window.apptResultsV2.loading) || 'Loading results...') + '</strong><span>Please wait while we refresh the results.</span>';
425      content.appendChild(loader);
426    }
427  }
428
429  function hideLoader(content) {
430    if (!content) return;
431    content.classList.remove('is-loading');
432    var loader = qs(content, '.appt-rv2-loader');
433    if (loader) loader.remove();
434  }
435
436
437  function setFilterLoading(root, loading) {
438    if (!root) return;
439    var loader = qs(root, '[data-appt-results-filter-loader]');
440    root.classList.toggle('is-filter-loading', !!loading);
441    root.setAttribute('aria-busy', loading ? 'true' : 'false');
442    if (loader) {
443      loader.classList.toggle('is-visible', !!loading);
444      loader.setAttribute('aria-hidden', loading ? 'false' : 'true');
445    }
446  }
447
448  function fieldValue(form, name) {
449    if (!form) return '';
450    var radio = qs(form, 'input[type="radio"][name="' + name + '"]:checked');
451    if (radio) return radio.value || '';
452    var field = form.elements[name];
453    return field ? String(field.value || '') : '';
454  }
455
456  function fieldText(form, name) {
457    if (!form) return '';
458    var radio = qs(form, 'input[type="radio"][name="' + name + '"]:checked');
459    if (radio) {
460      var label = radio.closest('label');
461      return label ? String(label.textContent || '').replace(/\s+/g, ' ').trim() : radio.value;
462    }
463    var field = form.elements[name];
464    if (name === 'country' && field) {
465      var countryWrap = field.closest('[data-appt-results-countryselect-field]');
466      var countryInput = qs(countryWrap, '[data-appt-results-countryselect-input]');
467      if (countryInput && String(countryInput.value || '').trim()) {
468        return String(countryInput.value || '').trim();
469      }
470    }
471    if (field && field.tagName === 'SELECT') {
472      var option = field.options[field.selectedIndex];
473      return option ? String(option.textContent || '').trim() : '';
474    }
475    return field ? String(field.value || '').trim() : '';
476  }
477
478  function updateResultsUrl(root) {
479    if (!root || !window.history || !window.history.replaceState) return;
480    var form = qs(root, '[data-appt-results-form]');
481    if (!form) return;
482    var url = new URL(window.location.href);
483    ['search','year','level','country','gender','start','end','surface','category','tab','sort'].forEach(function (name) {
484      var value = fieldValue(form, name);
485      if (!value || value === 'all' || (name === 'tab' && value === 'tournaments') || (name === 'sort' && value === 'recent')) {
486        url.searchParams.delete(name);
487      } else {
488        url.searchParams.set(name, value);
489      }
490    });
491    // 'rpage', not 'page': 'page' is a WordPress-reserved query var that
492    // gets intercepted/stripped by redirect_canonical() before the PHP
493    // shortcode ever sees it (see appt_results_v2_sanitize_filters() in
494    // shortcode_results_v2.php for the full explanation). Keep this in
495    // sync so a direct load of a shared/bookmarked ?rpage=N URL and an
496    // in-page click land on the same state.
497    var pageValue = fieldValue(form, 'rpage');
498    if (!pageValue || pageValue === '1') {
499      url.searchParams.delete('rpage');
500    } else {
501      url.searchParams.set('rpage', pageValue);
502    }
503    window.history.replaceState(null, '', url.toString());
504  }
505
506  function updateActiveFilters(root) {
507    var form = qs(root, '[data-appt-results-form]');
508    var target = qs(root, '[data-appt-results-active-filters]');
509    if (!form || !target) return;
510    var definitions = [
511      ['search', 'Search'], ['year', 'Year'], ['level', 'Level'], ['country', 'Country'],
512      ['gender', 'Gender'], ['start', 'From'], ['end', 'To'], ['surface', 'Surface'], ['category', 'Category']
513    ];
514    var active = [];
515    definitions.forEach(function (item) {
516      var value = fieldValue(form, item[0]);
517      if (!value || value === 'all') return;
518      active.push({ name: item[0], label: item[1] + ': ' + fieldText(form, item[0]) });
519    });
520    target.innerHTML = '';
521    var title = document.createElement('span');
522    title.textContent = 'Active filters';
523    target.appendChild(title);
524    if (!active.length) {
525      var empty = document.createElement('em');
526      empty.textContent = 'No active filters';
527      target.appendChild(empty);
528      return;
529    }
530    active.forEach(function (item) {
531      var button = document.createElement('button');
532      button.type = 'button';
533      button.setAttribute('data-appt-results-clear-filter', item.name);
534      button.textContent = item.label + ' ×';
535      target.appendChild(button);
536    });
537  }
538
539  function setPage(root, page) {
540    var input = qs(root, '[data-appt-results-current-page]');
541    if (input) input.value = String(page || 1);
542  }
543
544  function setTab(root, tab) {
545    var input = qs(root, '[data-appt-results-current-tab]');
546    if (input) input.value = tab || 'tournaments';
547  }
548
549  function syncSortToForm(root, value) {
550    var form = qs(root, '[data-appt-results-form]');
551    if (!form) return;
552    var existing = qs(form, 'input[name="sort"]');
553    if (!existing) {
554      existing = document.createElement('input');
555      existing.type = 'hidden';
556      existing.name = 'sort';
557      form.appendChild(existing);
558    }
559    existing.value = value || 'recent';
560  }
561
562  function serializeForm(root) {
563    var form = qs(root, '[data-appt-results-form]');
564    if (root && root._apptResultsCountryApi && typeof root._apptResultsCountryApi.sync === 'function') {
565      root._apptResultsCountryApi.sync();
566    }
567    var data = new FormData(form || undefined);
568    if (!data.get('action')) data.set('action', 'appt_results_v2_filter');
569    if (!data.get('nonce') && window.apptResultsV2) data.set('nonce', window.apptResultsV2.nonce || '');
570    return data;
571  }
572
573  function scrollIntoResults(root) {
574    if (!isMobile()) return;
575    var target = qs(root, '.appt-rv2-tabsbar') || qs(root, '[data-appt-results-content]') || root;
576    window.setTimeout(function () {
577      var rect = target.getBoundingClientRect();
578      var adminBar = document.getElementById('wpadminbar');
579      var offset = adminBar && adminBar.offsetHeight ? adminBar.offsetHeight : 0;
580      window.scrollTo({
581        top: Math.max(0, window.pageYOffset + rect.top - offset - 10),
582        behavior: 'smooth'
583      });
584    }, 80);
585  }
586
587
588  function isElementActuallyVisible(el) {
589    if (!el) return false;
590    var style = window.getComputedStyle ? window.getComputedStyle(el) : null;
591    if (style && (style.display === 'none' || style.visibility === 'hidden')) return false;
592    var rect = el.getBoundingClientRect();
593    return rect.width > 0 && rect.height > 0;
594  }
595
596  function getActiveRoots() {
597    var roots = qsa(document, '[data-appt-results-root]');
598    if (roots.length <= 1) return roots;
599
600    var preferred = null;
601    roots.some(function (root) {
602      if (isElementActuallyVisible(root)) {
603        preferred = root;
604        return true;
605      }
606      return false;
607    });
608    preferred = preferred || roots[0];
609
610    roots.forEach(function (root) {
611      if (root === preferred) {
612        root.hidden = false;
613        root.removeAttribute('aria-hidden');
614        root.classList.remove('is-duplicate-root');
615      } else {
616        root.hidden = true;
617        root.setAttribute('aria-hidden', 'true');
618        root.classList.add('is-duplicate-root');
619      }
620    });
621
622    return [preferred];
623  }
624
625  function dedupeRenderedTournaments(root) {
626    if (!root) return;
627    var seen = Object.create(null);
628    qsa(root, '.appt-rv2-card[data-appt-results-tournament-id]').forEach(function (card) {
629      var id = card.getAttribute('data-appt-results-tournament-id') || '';
630      if (!id) return;
631      if (seen[id]) {
632        card.parentNode && card.parentNode.removeChild(card);
633        return;
634      }
635      seen[id] = true;
636    });
637  }
638
639  function loadResults(root, opts) {
640    opts = opts || {};
641    if (!root || !window.fetch || !window.apptResultsV2) return;
642
643    var content = qs(root, '[data-appt-results-content]');
644    if (!content) return;
645
646    setFilterLoading(root, true);
647
648    if (root._apptResultsAbortController) {
649      root._apptResultsAbortController.abort();
650    }
651    var controller = window.AbortController ? new AbortController() : null;
652    root._apptResultsAbortController = controller;
653    root._apptResultsRequestId = (root._apptResultsRequestId || 0) + 1;
654    var requestId = root._apptResultsRequestId;
655
656    fetch(window.apptResultsV2.ajaxUrl, {
657      method: 'POST',
658      credentials: 'same-origin',
659      body: serializeForm(root),
660      signal: controller ? controller.signal : undefined
661    })
662      .then(function (response) { return response.json(); })
663      .then(function (response) {
664        if (requestId !== root._apptResultsRequestId) return;
665        if (!response || !response.success || !response.data || typeof response.data.html !== 'string') {
666          throw new Error('Invalid results response');
667        }
668        content.innerHTML = response.data.html;
669        dedupeRenderedTournaments(root);
670        initDynamic(root);
671        updateActiveFilters(root);
672        updateResultsUrl(root);
673        if (opts.scroll) scrollIntoResults(root);
674      })
675      .catch(function (error) {
676        if (error && error.name === 'AbortError') return;
677        if (requestId !== root._apptResultsRequestId) return;
678        content.innerHTML = '<div class="appt-rv2-empty-state"><strong>Results could not be loaded</strong><span>Please refresh the page and try again.</span></div>';
679      })
680      .finally(function () {
681        if (requestId === root._apptResultsRequestId) setFilterLoading(root, false);
682      });
683  }
684
685  function initCarousel(root) {
686    qsa(root, '[data-appt-results-carousel]').forEach(function (carousel) {
687      if (carousel.dataset.apptCarouselReady === '1') return;
688      carousel.dataset.apptCarouselReady = '1';
689
690      var track = qs(carousel, '[data-appt-carousel-track]');
691      var prev = qs(carousel, '[data-appt-carousel-prev]');
692      var next = qs(carousel, '[data-appt-carousel-next]');
693      if (!track) return;
694
695      function step() {
696        var card = qs(track, '.appt-rv2-champ-card');
697        return card ? Math.max(220, card.getBoundingClientRect().width + 16) : 260;
698      }
699
700      if (prev) {
701        prev.addEventListener('click', function () {
702          track.scrollBy({ left: -step(), behavior: 'smooth' });
703        });
704      }
705      if (next) {
706        next.addEventListener('click', function () {
707          track.scrollBy({ left: step(), behavior: 'smooth' });
708        });
709      }
710    });
711  }
712
713
714  function centerActiveTab(root, behavior) {
715    if (!root || !isMobile()) return;
716    var tabs = qs(root, '.appt-rv2-tabs');
717    var active = qs(tabs, 'button.is-active') || qs(tabs, 'button');
718    if (!tabs || !active) return;
719    window.setTimeout(function () {
720      var left = active.offsetLeft - ((tabs.clientWidth - active.offsetWidth) / 2);
721      left = Math.max(0, left);
722      if (typeof tabs.scrollTo === 'function') {
723        tabs.scrollTo({ left: left, behavior: behavior || 'smooth' });
724      } else {
725        tabs.scrollLeft = left;
726      }
727    }, 30);
728  }
729
730  function scrollFiltersIntoView(root) {
731    if (!root || !isMobile()) return;
732    var filters = qs(root, '.appt-rv2-filters');
733    if (!filters) return;
734    window.setTimeout(function () {
735      var rect = filters.getBoundingClientRect();
736      var adminBar = document.getElementById('wpadminbar');
737      var offset = adminBar && adminBar.offsetHeight ? adminBar.offsetHeight : 0;
738      if (rect.top < 0 || rect.top > window.innerHeight * .58) {
739        window.scrollTo({ top: Math.max(0, window.pageYOffset + rect.top - offset - 10), behavior: 'smooth' });
740      }
741    }, 70);
742  }
743
744  function initTabsAndSort(root) {
745    qsa(root, '[data-appt-results-tab]').forEach(function (button) {
746      if (button.dataset.apptTabReady === '1') return;
747      button.dataset.apptTabReady = '1';
748      button.addEventListener('click', function () {
749        var tab = button.getAttribute('data-appt-results-tab') || 'tournaments';
750        setTab(root, tab);
751        setPage(root, 1);
752        centerActiveTab(root, 'smooth');
753
754        window.dataLayer = window.dataLayer || [];
755        window.dataLayer.push({ event: 'results_tab_click', tab: tab });
756
757        loadResults(root, { scroll: true });
758      });
759    });
760
761    qsa(root, '[data-appt-results-sort]').forEach(function (select) {
762      if (select.dataset.apptSortReady === '1') return;
763      select.dataset.apptSortReady = '1';
764      select.addEventListener('change', function () {
765        syncSortToForm(root, select.value || 'recent');
766        setPage(root, 1);
767        loadResults(root, { scroll: false });
768      });
769    });
770
771    qsa(root, '[data-appt-results-page]').forEach(function (link) {
772      if (link.dataset.apptPageReady === '1') return;
773      link.dataset.apptPageReady = '1';
774      // These are real <a href="?...&rpage=N"> now (crawlable), not plain
775      // buttons — intercept the click to keep the existing no-reload AJAX
776      // UX; the href stays there and fully functional for direct
777      // navigation, middle-click-to-open-in-new-tab, and crawlers.
778      link.addEventListener('click', function (event) {
779        if (link.getAttribute('aria-disabled') === 'true') { event.preventDefault(); return; }
780        event.preventDefault();
781        var page = link.getAttribute('data-appt-results-page') || 1;
782        setPage(root, page);
783
784        window.dataLayer = window.dataLayer || [];
785        window.dataLayer.push({ event: 'results_pagination_click', page: page });
786
787        loadResults(root, { scroll: true });
788      });
789    });
790  }
791
792  function initAccordions(root) {
793    qsa(root, '.appt-rv2-card').forEach(function (details) {
794      if (details.dataset.apptDetailsReady === '1') return;
795      details.dataset.apptDetailsReady = '1';
796      details.addEventListener('toggle', function () {
797        if (!details.open) return;
798
799        window.dataLayer = window.dataLayer || [];
800        window.dataLayer.push({
801          event: 'results_card_expand',
802          tournament_id: details.getAttribute('data-appt-results-tournament-id') || ''
803        });
804
805        if (!isMobile()) return;
806        window.setTimeout(function () {
807          var rect = details.getBoundingClientRect();
808          var adminBar = document.getElementById('wpadminbar');
809          var offset = adminBar && adminBar.offsetHeight ? adminBar.offsetHeight : 0;
810          if (rect.top < 0 || rect.top > window.innerHeight * 0.75) {
811            window.scrollTo({ top: Math.max(0, window.pageYOffset + rect.top - offset - 12), behavior: 'smooth' });
812          }
813        }, 60);
814      });
815    });
816  }
817
818  function initDynamic(root) {
819    dedupeRenderedTournaments(root);
820    initCarousel(root);
821    initTabsAndSort(root);
822    initAccordions(root);
823    centerActiveTab(root, 'auto');
824  }
825
826    function initFilters(root) {
827    var form = qs(root, '[data-appt-results-form]');
828    if (!form || form.dataset.apptFiltersReady === '1') return;
829    form.dataset.apptFiltersReady = '1';
830
831    var advanced = qs(form, '[data-appt-results-advanced]');
832    var timer = null;
833    var countryApi = initResultsCountrySelect(root, form);
834    root._apptResultsCountryApi = countryApi;
835
836    function setAdvanced(open) {
837      if (!advanced) return;
838      advanced.classList.toggle('is-open', !!open);
839      qsa(form, '[data-appt-results-toggle-advanced]').forEach(function (button) {
840        button.setAttribute('aria-expanded', open ? 'true' : 'false');
841      });
842    }
843
844    function request(scroll) {
845      setPage(root, 1);
846      updateActiveFilters(root);
847
848      var fieldValue = function (name) {
849        var field = form.elements[name];
850        return field ? String(field.value || '') : '';
851      };
852      var start = fieldValue('start');
853      var end = fieldValue('end');
854
855      window.dataLayer = window.dataLayer || [];
856      window.dataLayer.push({
857        event: 'results_filter_applied',
858        search_term: fieldValue('search'),
859        gender: fieldValue('gender'),
860        date_range: (start || end) ? (start + (start && end ? ' - ' : '') + end) : '',
861        country_code: fieldValue('country'),
862        sort: fieldValue('sort') || 'recent'
863      });
864
865      loadResults(root, { scroll: !!scroll });
866    }
867
868    function clearFilter(name) {
869      if (name === 'country' && countryApi) {
870        countryApi.setValue('country', '', false);
871        return;
872      }
873      var radios = qsa(form, 'input[type="radio"][name="' + name + '"]');
874      if (radios.length) {
875        radios.forEach(function (radio) { radio.checked = radio.value === 'all'; });
876        return;
877      }
878      var field = form.elements[name];
879      if (!field) return;
880      if (field.tagName === 'SELECT') field.value = 'all';
881      else field.value = '';
882    }
883
884    form.addEventListener('submit', function (event) {
885      event.preventDefault();
886      request(true);
887    });
888
889    form.addEventListener('change', function (event) {
890      if (!event.target.closest('[data-appt-results-input]')) return;
891      request(false);
892    });
893
894    form.addEventListener('input', function (event) {
895      var field = event.target.closest('input[type="search"][data-appt-results-input]');
896      if (!field) return;
897      window.clearTimeout(timer);
898      timer = window.setTimeout(function () { request(false); }, 320);
899    });
900
901    form.addEventListener('click', function (event) {
902      var toggle = event.target.closest('[data-appt-results-toggle-advanced]');
903      if (toggle) {
904        event.preventDefault();
905        setAdvanced(!advanced.classList.contains('is-open'));
906        return;
907      }
908
909      var reset = event.target.closest('[data-appt-results-reset]');
910      if (reset) {
911        event.preventDefault();
912        var defaultYear = form.getAttribute('data-appt-results-default-year') || '';
913        ['search','start','end'].forEach(function (name) { if (form.elements[name]) form.elements[name].value = ''; });
914        ['level','surface','category'].forEach(function (name) { if (form.elements[name]) form.elements[name].value = 'all'; });
915        if (countryApi) countryApi.setValue('country', '', false);
916        if (form.elements.year) form.elements.year.value = defaultYear || 'all';
917        qsa(form, 'input[type="radio"][name="gender"]').forEach(function (radio) { radio.checked = radio.value === 'all'; });
918        setTab(root, 'tournaments');
919        setPage(root, 1);
920        syncSortToForm(root, 'recent');
921        setAdvanced(false);
922        request(true);
923        return;
924      }
925
926      var clear = event.target.closest('[data-appt-results-clear-filter]');
927      if (clear) {
928        event.preventDefault();
929        clearFilter(clear.getAttribute('data-appt-results-clear-filter') || '');
930        request(false);
931      }
932    });
933
934    updateActiveFilters(root);
935  }
936
937
938
939  function initRoot(root) {
940    if (!root) return;
941    initFilters(root);
942    initDynamic(root);
943    updateActiveFilters(root);
944  }
945
946  function boot() {
947    getActiveRoots().forEach(initRoot);
948  }
949
950  if (document.readyState === 'loading') {
951    document.addEventListener('DOMContentLoaded', boot);
952  } else {
953    boot();
954  }
955})();

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.