PageSourceSearch

https://thearabianpost.com/wp-content/themes/arabianpost/assets/js/ap-ajax-navigation.js?ver=1789639416

js thearabianpost.com collected 2026-09-24 18:30:57 UTC 59,572 bytes, 1,652 lines download raw bytes

1/* Arabian Post AJAX navigation v47.6-header-instant - no header fade/slide during AJAX swaps */
2(function (window, document, $) {
3  'use strict';
4
5  /*
6   * The runtime is intentionally loaded early from header.php and may also be
7   * printed later by WordPress for standalone templates. If the early copy
8   * completed successfully, the later copy must not bind click/popstate/
9   * prefetch handlers a second time. If the early copy failed before ready,
10   * the later copy is still allowed to initialise.
11   */
12  if (
13    window.AP_AJAX_NAV &&
14    window.AP_AJAX_NAV.ready === true &&
15    window.AP_AJAX_NAV.runtimeVersion === '47.6-header-instant' &&
16    typeof window.AP_AJAX_NAV.navigate === 'function'
17  ) {
18    return;
19  }
20
21  var config = window.AP_AJAX_NAV || {};
22
23  /*
24   * Rocket Loader-safe jQuery binding.
25   *
26   * This file can now be loaded early from header.php while Cloudflare keeps
27   * jQuery itself deferred. Never permanently capture an undefined jQuery.
28   * Refresh the local reference whenever a theme-only jQuery feature runs.
29   */
30  function getJQuery() {
31    if (!$ && window.jQuery) {
32      $ = window.jQuery;
33    }
34    return $ || null;
35  }
36  var contentSelector = config.contentSelector || '.eltd-content';
37  var menuSelector = config.menuSelector || '#menu-empty';
38  var requestController = null;
39  var navigationGeneration = 0;
40  var currentUrl = window.location.href;
41
42  function sameOrigin(url) {
43    return url.origin === window.location.origin;
44  }
45
46  function normalisePath(url) {
47    var path = url.pathname.replace(/\/+$/, '');
48    return path || '/';
49  }
50
51  function escapeSelector(value) {
52    if (window.CSS && typeof window.CSS.escape === 'function') return window.CSS.escape(value);
53    return String(value).replace(/([ #;?%&,.+*~\\':"!^$[\]()=>|\/@])/g, '\\$1');
54  }
55
56  function shouldHandleLink(link, event) {
57    if (!link || !link.href || link.target === '_blank' || link.hasAttribute('download')) return false;
58    if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false;
59    if (link.closest('[data-no-ajax], .no-ajax, .woocommerce-cart-form, .woocommerce-checkout')) return false;
60    if (link.classList.contains('no_link') || /^\s*(?:javascript:|mailto:|tel:)/i.test(link.getAttribute('href') || '')) return false;
61
62    var rawHref = (link.getAttribute('href') || '').trim();
63    if (!rawHref || rawHref === '#') return false;
64
65    var url;
66    try { url = new URL(link.href, window.location.href); } catch (e) { return false; }
67
68    if (!sameOrigin(url) || !/^https?:$/.test(url.protocol)) return false;
69    if (url.pathname.indexOf('/wp-admin') === 0 || url.pathname.indexOf('/wp-login.php') === 0) return false;
70    if (/\.(?:jpg|jpeg|png|gif|webp|svg|pdf|zip|xml|json|mp4|mp3|docx?|xlsx?)$/i.test(url.pathname)) return false;
71    if (url.searchParams.has('add-to-cart') || url.searchParams.has('wc-ajax')) return false;
72    if (url.hash && normalisePath(url) === normalisePath(new URL(window.location.href)) && url.search === window.location.search) return false;
73
74    return true;
75  }
76
77  var progressResetTimer = null;
78  var progressCreepTimer = null;
79  var currentProgress = 0;
80
81  function stopProgressCreep() {
82    if (progressCreepTimer) {
83      window.clearTimeout(progressCreepTimer);
84      progressCreepTimer = null;
85    }
86  }
87
88  function scheduleProgressCreep() {
89    stopProgressCreep();
90
91    function creep() {
92      if (!document.documentElement.classList.contains('ap-ajax-loading')) {
93        progressCreepTimer = null;
94        return;
95      }
96
97      /* Keep slow requests visibly alive without pretending to be complete. */
98      if (currentProgress < 91) {
99        var remaining = 91 - currentProgress;
100        var step = Math.max(0.45, Math.min(2.45, remaining * 0.082));
101        setProgress(currentProgress + step);
102      }
103
104      progressCreepTimer = window.setTimeout(creep, 520);
105    }
106
107    progressCreepTimer = window.setTimeout(creep, 560);
108  }
109
110  function setProgress(value, immediate) {
111    var bar = document.getElementById('progress-bar');
112    if (!bar) return;
113
114    var progress = Math.max(0, Math.min(100, Number(value) || 0));
115    currentProgress = Math.max(currentProgress, progress);
116    if (progress === 0 || immediate) currentProgress = progress;
117    if (progressResetTimer) {
118      window.clearTimeout(progressResetTimer);
119      progressResetTimer = null;
120    }
121
122    bar.style.transition = immediate ? 'none' : '';
123    bar.style.width = progress + '%';
124    bar.setAttribute('aria-valuenow', String(Math.round(progress)));
125
126    if (immediate) {
127      /* Force the initial 0% state to render before the first animated step. */
128      void bar.offsetWidth;
129      bar.style.transition = '';
130    }
131  }
132
133  var headerInstantReleaseRaf = 0;
134
135  function setLoading(isLoading) {
136    var root = document.documentElement;
137
138    if (isLoading) {
139      if (headerInstantReleaseRaf) {
140        window.cancelAnimationFrame(headerInstantReleaseRaf);
141        headerInstantReleaseRaf = 0;
142      }
143
144      root.classList.add('ap-ajax-header-instant');
145      root.classList.add('ap-ajax-loading');
146      document.body.setAttribute('aria-busy', 'true');
147
148      setProgress(0, true);
149      window.requestAnimationFrame(function () {
150        setProgress(6);
151        scheduleProgressCreep();
152      });
153      return;
154    }
155
156    stopProgressCreep();
157    root.classList.remove('ap-ajax-loading');
158    document.body.setAttribute('aria-busy', 'false');
159    setProgress(100);
160
161    headerInstantReleaseRaf = window.requestAnimationFrame(function () {
162      headerInstantReleaseRaf = window.requestAnimationFrame(function () {
163        root.classList.remove('ap-ajax-header-instant');
164        headerInstantReleaseRaf = 0;
165      });
166    });
167
168    progressResetTimer = window.setTimeout(function () {
169      var bar = document.getElementById('progress-bar');
170      if (bar) {
171        bar.classList.add('ap-progress-resetting');
172        bar.style.width = '0%';
173        currentProgress = 0;
174        window.setTimeout(function () {
175          bar.classList.remove('ap-progress-resetting');
176        }, 40);
177      }
178      progressResetTimer = null;
179    }, 320);
180  }
181
182  var contentVersionStoragePrefix = 'apEditorialLandingVersion:';
183  var initialDocumentContentVersion = config.contentVersion ? String(config.contentVersion) : '';
184  var initialLandingVersions = config.landingVersions && typeof config.landingVersions === 'object' ?
185    config.landingVersions :
186    {};
187  var versionCheckThrottleMs = Math.max(0, Number(config.versionCheckThrottleMs) || 8000);
188  var editorialFreshBudgetMs = Math.max(300, Number(config.editorialFreshBudgetMs) || 1400);
189  var editorialVersionPollMs = Math.max(10000, Number(config.editorialVersionPollMs) || 30000);
190  var cachedVersionPayload = null;
191  var cachedVersionPayloadAt = 0;
192  var dirtyLandingVersions = {};
193  var versionCheckPromise = null;
194  var versionPollTimer = null;
195
196  function landingKeyForUrl(value) {
197    var requested;
198    try {
199      requested = new URL(value, window.location.href);
200    } catch (e) {
201      return '';
202    }
203
204    var targets = Array.isArray(config.freshLandingUrls) && config.freshLandingUrls.length ?
205      config.freshLandingUrls :
206      [config.homeUrl || '/'];
207
208    for (var i = 0; i < targets.length; i += 1) {
209      var target;
210      try {
211        target = new URL(targets[i], window.location.origin);
212      } catch (e) {
213        continue;
214      }
215
216      if (
217        requested.origin === target.origin &&
218        normalisePath(requested) === normalisePath(target) &&
219        requested.search === target.search
220      ) {
221        return requested.origin + normalisePath(target);
222      }
223    }
224
225    return '';
226  }
227
228  function landingPathForUrl(value) {
229    var requested;
230    try {
231      requested = new URL(value, window.location.href);
232    } catch (e) {
233      return '';
234    }
235
236    var targets = Array.isArray(config.freshLandingUrls) && config.freshLandingUrls.length ?
237      config.freshLandingUrls :
238      [config.homeUrl || '/'];
239
240    for (var i = 0; i < targets.length; i += 1) {
241      var target;
242      try {
243        target = new URL(targets[i], window.location.origin);
244      } catch (e) {
245        continue;
246      }
247
248      if (
249        requested.origin === target.origin &&
250        normalisePath(requested) === normalisePath(target) &&
251        requested.search === target.search
252      ) {
253        return normalisePath(target);
254      }
255    }
256
257    return '';
258  }
259
260  function isFreshLandingTarget(value) {
261    return Boolean(landingPathForUrl(value));
262  }
263
264  function initialVersionForLanding(value) {
265    var path = landingPathForUrl(value);
266    if (path && initialLandingVersions[path]) {
267      return String(initialLandingVersions[path]);
268    }
269    return initialDocumentContentVersion || '';
270  }
271
272  function versionForLandingPayload(payload, value) {
273    if (!payload || typeof payload !== 'object') return '';
274
275    var path = landingPathForUrl(value);
276    if (
277      path &&
278      payload.versions &&
279      typeof payload.versions === 'object' &&
280      payload.versions[path]
281    ) {
282      return String(payload.versions[path]);
283    }
284
285    return payload.version ? String(payload.version) : '';
286  }
287
288  function getRememberedLandingVersion(value) {
289    var key = landingKeyForUrl(value);
290    if (!key) return '';
291
292    try {
293      return window.sessionStorage.getItem(contentVersionStoragePrefix + key) || '';
294    } catch (e) {
295      return '';
296    }
297  }
298
299  function rememberLandingVersion(value, version) {
300    var key = landingKeyForUrl(value);
301    if (!key || !version) return;
302
303    try {
304      window.sessionStorage.setItem(contentVersionStoragePrefix + key, String(version));
305    } catch (e) {}
306  }
307
308  function buildAjaxRequestUrl(value, forceFresh) {
309    var requestUrl = new URL(value, window.location.href);
310
311    if (forceFresh) {
312      requestUrl.searchParams.set('_ap_editorial_fresh', String(Date.now()));
313    }
314
315    return requestUrl.href;
316  }
317
318  function fetchEditorialContentVersion(signal) {
319    var now = Date.now();
320
321    if (!config.contentVersionUrl) {
322      return Promise.resolve({
323        version: initialDocumentContentVersion || '',
324        versions: initialLandingVersions
325      });
326    }
327
328    var versionUrl;
329    try {
330      versionUrl = new URL(config.contentVersionUrl, window.location.href);
331      versionUrl.searchParams.set('_apv', String(now));
332    } catch (e) {
333      return Promise.resolve({
334        version: initialDocumentContentVersion || '',
335        versions: initialLandingVersions
336      });
337    }
338
339    return fetch(versionUrl.href, {
340      method: 'GET',
341      credentials: 'same-origin',
342      cache: 'no-store',
343      headers: {
344        'Accept': 'application/json',
345        'Cache-Control': 'no-cache, no-store, max-age=0',
346        'Pragma': 'no-cache'
347      },
348      signal: signal
349    }).then(function (response) {
350      if (!response.ok) throw new Error('Content version HTTP ' + response.status);
351      return response.json();
352    }).then(function (payload) {
353      if (!payload || typeof payload !== 'object') {
354        throw new Error('Invalid editorial version payload');
355      }
356      cachedVersionPayload = payload;
357      cachedVersionPayloadAt = Date.now();
358      return payload;
359    });
360  }
361
362  function markEditorialChanges(payload) {
363    if (!payload || typeof payload !== 'object') return;
364
365    var targets = Array.isArray(config.freshLandingUrls) ? config.freshLandingUrls : [];
366    targets.forEach(function (targetUrl) {
367      var path = landingPathForUrl(targetUrl);
368      if (!path) return;
369
370      var incoming = versionForLandingPayload(payload, targetUrl);
371      if (!incoming) return;
372
373      var remembered = getRememberedLandingVersion(targetUrl) || initialVersionForLanding(targetUrl);
374
375      if (remembered && incoming !== remembered) {
376        dirtyLandingVersions[path] = incoming;
377      } else if (remembered && incoming === remembered) {
378        delete dirtyLandingVersions[path];
379      }
380    });
381  }
382
383  function runEditorialVersionCheck() {
384    if (versionCheckPromise) return versionCheckPromise;
385
386    var controller = new AbortController();
387    versionCheckPromise = fetchEditorialContentVersion(controller.signal)
388      .then(function (payload) {
389        markEditorialChanges(payload);
390        return payload;
391      })
392      .catch(function () {
393        return cachedVersionPayload || null;
394      })
395      .then(function (payload) {
396        versionCheckPromise = null;
397        return payload;
398      });
399
400    return versionCheckPromise;
401  }
402
403  function scheduleEditorialVersionCheck(delay) {
404    if (versionPollTimer) window.clearTimeout(versionPollTimer);
405
406    versionPollTimer = window.setTimeout(function poll() {
407      runEditorialVersionCheck().then(function () {
408        scheduleEditorialVersionCheck(editorialVersionPollMs);
409      });
410    }, Math.max(0, Number(delay) || 0));
411  }
412
413  function landingDirtyVersion(value) {
414    var path = landingPathForUrl(value);
415    return path && dirtyLandingVersions[path] ? String(dirtyLandingVersions[path]) : '';
416  }
417
418  function markLandingFresh(value, version) {
419    var path = landingPathForUrl(value);
420    if (!path || !version) return;
421
422    rememberLandingVersion(value, version);
423    if (dirtyLandingVersions[path] === String(version)) {
424      delete dirtyLandingVersions[path];
425    }
426  }
427
428  function fetchHtmlWithProgress(url, signal, attempt, forceFresh) {
429    /*
430     * Do not impose a client-side timeout. A genuinely slow server request is
431     * allowed to remain alive while the progress bar creeps forward. If the
432     * server/cache actually fails, retry once and only then hand the URL back
433     * to the browser as a normal navigation so the user is never stranded.
434     */
435    attempt = attempt || 0;
436    setProgress(attempt ? Math.max(currentProgress, 58) : 12);
437
438    var freshEditorialRequest = Boolean(forceFresh && isFreshLandingTarget(url));
439    var requestUrl = buildAjaxRequestUrl(url, freshEditorialRequest);
440
441    return fetch(requestUrl, {
442      method: 'GET',
443      credentials: 'same-origin',
444      headers: (function () {
445        var headers = {
446          'Accept': 'text/html,application/xhtml+xml'
447        };
448
449        if (freshEditorialRequest) {
450          headers['Cache-Control'] = 'no-cache, no-store, max-age=0';
451          headers['Pragma'] = 'no-cache';
452          headers['X-AP-Editorial-Fresh'] = '1';
453        }
454
455        return headers;
456      })(),
457      cache: freshEditorialRequest ? 'no-store' : (attempt ? 'reload' : 'default'),
458      signal: signal
459    }).then(function (response) {
460      if (!response.ok) {
461        var error = new Error('HTTP ' + response.status);
462        error.status = response.status;
463        throw error;
464      }
465      setProgress(Math.max(currentProgress, 54));
466      return response.text();
467    }).then(function (html) {
468      if (!html || html.length < 128) throw new Error('Empty or incomplete HTML response');
469      setProgress(Math.max(currentProgress, 68));
470      return html;
471    }).catch(function (error) {
472      if (error && error.name === 'AbortError') throw error;
473
474      var status = Number(error && error.status || 0);
475      var transient = !status || status === 408 || status === 425 || status === 429 || status >= 500;
476
477      if (attempt === 0 && transient && !signal.aborted) {
478        setProgress(Math.max(currentProgress, 62));
479        return new Promise(function (resolve) {
480          window.setTimeout(resolve, 220);
481        }).then(function () {
482          if (signal.aborted) {
483            var aborted = new Error('Navigation aborted');
484            aborted.name = 'AbortError';
485            throw aborted;
486          }
487          return fetchHtmlWithProgress(url, signal, 1, forceFresh);
488        });
489      }
490
491      throw error;
492    });
493  }
494
495  function fetchFreshHtmlSilently(url, signal) {
496    /*
497     * Fresh editorial request used only when a landing is already known dirty.
498     * It deliberately does NOT touch the progress bar. The visible bar belongs
499     * to the single normal navigation lifecycle, so users never see a second
500     * loading phase.
501     */
502    var requestUrl = buildAjaxRequestUrl(url, true);
503
504    return fetch(requestUrl, {
505      method: 'GET',
506      credentials: 'same-origin',
507      headers: {
508        'Accept': 'text/html,application/xhtml+xml',
509        'Cache-Control': 'no-cache, no-store, max-age=0',
510        'Pragma': 'no-cache',
511        'X-AP-Editorial-Fresh': '1'
512      },
513      cache: 'no-store',
514      signal: signal
515    }).then(function (response) {
516      if (!response.ok) {
517        var error = new Error('HTTP ' + response.status);
518        error.status = response.status;
519        throw error;
520      }
521      return response.text();
522    }).then(function (html) {
523      if (!html || html.length < 128) {
524        throw new Error('Empty or incomplete fresh HTML response');
525      }
526      return html;
527    });
528  }
529
530  function absoluteAssetUrl(value, base) {
531    try {
532      var url = new URL(value, base || window.location.href);
533      url.hash = '';
534      return url.href;
535    } catch (e) {
536      return value || '';
537    }
538  }
539
540  function assetSet(root, selector, attribute, base) {
541    return Array.prototype.map.call(root.querySelectorAll(selector), function (node) {
542      return absoluteAssetUrl(node.getAttribute(attribute), base);
543    }).filter(Boolean);
544  }
545
546  function copyAttributes(source, target) {
547    Array.prototype.forEach.call(source.attributes || [], function (attribute) {
548      target.setAttribute(attribute.name, attribute.value);
549    });
550  }
551
552  function loadStylesheetFromNode(incomingLink, base) {
553    return new Promise(function (resolve) {
554      var link = document.createElement('link');
555      copyAttributes(incomingLink, link);
556      link.href = absoluteAssetUrl(incomingLink.getAttribute('href'), base);
557      link.setAttribute('data-ap-ajax-style', 'true');
558      link.onload = resolve;
559      link.onerror = resolve;
560      document.head.appendChild(link);
561    });
562  }
563
564  function styleTextSignature(text) {
565    var value = String(text || '').replace(/\s+/g, ' ').trim();
566    var hash = 2166136261;
567    for (var i = 0; i < value.length; i += 1) {
568      hash ^= value.charCodeAt(i);
569      hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
570    }
571    return 'ap-style-' + (hash >>> 0).toString(36);
572  }
573
574  function syncInlineStyles(parsed) {
575    var incomingView = parsed.querySelector(contentSelector);
576
577    /*
578     * Replace the complete managed destination-style layer on every AJAX page.
579     * Earlier versions only appended anonymous style blocks, so styles from the
580     * previous template remained later in the cascade and could keep controlling
581     * the persistent header. The original server-rendered styles remain intact;
582     * this managed layer is rebuilt in destination order and appended last.
583     */
584    Array.prototype.forEach.call(
585      document.querySelectorAll('style[data-ap-destination-style="true"]'),
586      function (style) { style.parentNode.removeChild(style); }
587    );
588
589    var incomingStyles = Array.prototype.filter.call(parsed.querySelectorAll('style'), function (style) {
590      return !incomingView || !incomingView.contains(style);
591    });
592
593    incomingStyles.forEach(function (incomingStyle, index) {
594      var text = incomingStyle.textContent || '';
595      if (!text.trim()) return;
596
597      var style = document.createElement('style');
598      Array.prototype.forEach.call(incomingStyle.attributes || [], function (attribute) {
599        /* Avoid duplicate DOM ids while preserving media/type/nonce attributes. */
600        if (attribute.name.toLowerCase() !== 'id') {
601          style.setAttribute(attribute.name, attribute.value);
602        }
603      });
604      style.setAttribute('data-ap-destination-style', 'true');
605      style.setAttribute('data-ap-destination-style-order', String(index));
606      if (incomingStyle.id) style.setAttribute('data-ap-source-style-id', incomingStyle.id);
607      style.textContent = text;
608      document.head.appendChild(style);
609    });
610  }
611
612  function copyShellElementState(parsed, selector, options) {
613    var incoming = parsed.querySelector(selector);
614    var current = document.querySelector(selector);
615    if (!incoming || !current) return;
616
617    options = options || {};
618
619    if (options.classes !== false) current.className = incoming.className;
620
621    if (options.style) {
622      if (incoming.hasAttribute('style')) current.setAttribute('style', incoming.getAttribute('style'));
623      else current.removeAttribute('style');
624    }
625
626    Array.prototype.forEach.call(options.attributes || [], function (name) {
627      if (incoming.hasAttribute(name)) current.setAttribute(name, incoming.getAttribute(name) || '');
628      else current.removeAttribute(name);
629    });
630  }
631
632  function syncPersistentShellState(parsed) {
633    /*
634     * Keep the same header DOM nodes, but update the destination's classes and
635     * top-level inline state. WordPress templates often change these selectors,
636     * and their CSS depends on the destination classes even though the markup is
637     * intentionally persistent.
638     */
639    copyShellElementState(parsed, '.eltd-wrapper', { classes: true });
640    copyShellElementState(parsed, '.eltd-wrapper-inner', { classes: true });
641    copyShellElementState(parsed, '#eltd-content-wrapper', { classes: true, style: true });
642    copyShellElementState(parsed, '.eltd-page-header', { classes: true, style: true });
643    copyShellElementState(parsed, '.eltd-mobile-header', { classes: true, style: true });
644    copyShellElementState(parsed, '.eltd-sticky-header', { classes: true, style: true });
645    copyShellElementState(parsed, 'footer', { classes: true, style: true });
646  }
647
648  function ensureRequiredAssets(parsed) {
649    isolateHomeToSingleStyles(parsed);
650
651    var incomingBase = parsed.baseURI || window.location.href;
652    var currentStyles = assetSet(document, 'link[rel="stylesheet"][href]', 'href');
653    var incomingLinks = Array.prototype.slice.call(parsed.querySelectorAll('link[rel="stylesheet"][href]'));
654
655    var missingLinks = incomingLinks.filter(function (link) {
656      var url = absoluteAssetUrl(link.getAttribute('href'), incomingBase);
657      if (!url || currentStyles.indexOf(url) !== -1) return false;
658      currentStyles.push(url);
659      return true;
660    });
661
662    /* WordPress templates and widgets frequently print essential CSS as inline
663       style blocks rather than linked files. Carry those blocks forward before
664       swapping content, while leaving existing global/header CSS in place. */
665    syncInlineStyles(parsed);
666
667    /* Never inject all fetched scripts: doing so would reinitialise the preserved
668       header. Only missing destination stylesheets are loaded here. */
669    return Promise.all(missingLinks.map(function (link) {
670      return loadStylesheetFromNode(link, incomingBase);
671    }));
672  }
673
674  function elementSignature(root, selector) {
675    var element = root.querySelector(selector);
676    if (!element) return 'missing';
677
678    /* Page-specific classes frequently differ between archives and single posts.
679       They do not mean that the shared header/footer DOM is incompatible. */
680    return element.tagName + '|' + String(element.id || '');
681  }
682
683  function isHomeContext(body) {
684    if (!body) return false;
685    return body.classList.contains('home') ||
686      body.classList.contains('blog') ||
687      body.classList.contains('front-page') ||
688      body.classList.contains('page-template-default-home');
689  }
690
691  function linkedStylesheetSet(root, base) {
692    return assetSet(root, 'link[rel="stylesheet"][href]', 'href', base).sort();
693  }
694
695  function isSinglePostContext(body) {
696    if (!body) return false;
697    return body.classList.contains('single-post') ||
698      body.classList.contains('single') ||
699      body.classList.contains('singular');
700  }
701
702  function isPersistentGlobalStylesheet(link) {
703    var id = String(link.id || '').toLowerCase();
704    var href = String(link.getAttribute('href') || '').toLowerCase();
705    var token = id + ' ' + href;
706
707    return /(?:modules(?:-responsive)?\.min\.css|plugins\.min\.css|style_dynamic(?:_responsive)?\.css|\/style\.css(?:\?|$)|ap-ajax-navigation\.css|wp-block-library|global-styles|classic-theme-styles|dashicons|admin-bar|font-?awesome|elegant-icons|ionicons|google-font|fonts\.googleapis|revslider|rs6\.css)/i.test(token);
708  }
709
710  function isPersistentGlobalInlineStyle(style) {
711    var id = String(style.id || '').toLowerCase();
712    return /(?:global-styles|wp-block-library|classic-theme-styles|admin-bar|emoji|custom-background|eltd-style-dynamic)/i.test(id);
713  }
714
715  /*
716   * A direct homepage request can contain homepage-only linked and inline CSS.
717   * Because the header document is intentionally preserved, those original
718   * source-page rules otherwise remain active after the body becomes a single
719   * post. Suspend only source styles that are absent from the fetched single
720   * page, while never touching the theme's global CSS. Suspended rules are
721   * restored automatically when a later destination contains them again.
722   */
723  function isolateHomeToSingleStyles(parsed) {
724    var homeToSingle = isHomeContext(document.body) && isSinglePostContext(parsed.body);
725    var incomingBase = parsed.baseURI || window.location.href;
726    var incomingHrefs = new Set(assetSet(parsed, 'link[rel="stylesheet"][href]', 'href', incomingBase));
727
728    Array.prototype.forEach.call(document.querySelectorAll('link[rel="stylesheet"][href]'), function (link) {
729      var href = absoluteAssetUrl(link.getAttribute('href'), window.location.href);
730      var required = incomingHrefs.has(href);
731
732      if (required && link.hasAttribute('data-ap-home-style-suspended')) {
733        link.disabled = false;
734        link.removeAttribute('data-ap-home-style-suspended');
735        return;
736      }
737
738      if (homeToSingle && !required && !isPersistentGlobalStylesheet(link)) {
739        link.disabled = true;
740        link.setAttribute('data-ap-home-style-suspended', 'true');
741      }
742    });
743
744    var incomingView = parsed.querySelector(contentSelector);
745    var incomingSignatures = new Set();
746    Array.prototype.forEach.call(parsed.querySelectorAll('style'), function (style) {
747      if (incomingView && incomingView.contains(style)) return;
748      incomingSignatures.add(styleTextSignature(style.textContent || ''));
749    });
750
751    var currentView = document.querySelector(contentSelector);
752    Array.prototype.forEach.call(document.querySelectorAll('style'), function (style) {
753      if (style.getAttribute('data-ap-destination-style') === 'true') return;
754      if (currentView && currentView.contains(style)) return;
755
756      var signature = styleTextSignature(style.textContent || '');
757      var required = incomingSignatures.has(signature);
758
759      if (required && style.hasAttribute('data-ap-home-style-suspended')) {
760        var oldMedia = style.getAttribute('data-ap-old-media');
761        if (oldMedia !== null && oldMedia !== '') style.setAttribute('media', oldMedia);
762        else style.removeAttribute('media');
763        style.removeAttribute('data-ap-old-media');
764        style.removeAttribute('data-ap-home-style-suspended');
765        return;
766      }
767
768      if (homeToSingle && !required && !isPersistentGlobalInlineStyle(style)) {
769        if (!style.hasAttribute('data-ap-home-style-suspended')) {
770          style.setAttribute('data-ap-old-media', style.getAttribute('media') || '');
771        }
772        style.setAttribute('media', 'not all');
773        style.setAttribute('data-ap-home-style-suspended', 'true');
774      }
775    });
776  }
777
778  function stylingContextIsCompatible(parsed) {
779    /* In persistent-shell mode the header must never be discarded. Missing
780       destination styles are loaded before content replacement, including
781       home-to-inner and inner-to-home transitions. */
782    if (config.preserveHeaderShell || config.neverHardReload) return true;
783
784    if (isHomeContext(document.body) !== isHomeContext(parsed.body)) return false;
785
786    var currentStyles = linkedStylesheetSet(document, window.location.href);
787    var incomingStyles = linkedStylesheetSet(parsed, parsed.baseURI || window.location.href);
788
789    /* Missing destination styles are safe to load; a radically different core
790       stylesheet set is not. Compare only theme/plugin CSS basenames. */
791    function coreNames(list) {
792      return list.map(function (url) {
793        try { return new URL(url).pathname.split('/').pop().replace(/\?.*$/, ''); }
794        catch (e) { return url; }
795      }).filter(function (name) {
796        return /(?:modules|plugins|style_dynamic|responsive|woocommerce|js_composer|visual-composer)/i.test(name);
797      }).sort().join('|');
798    }
799
800    return coreNames(currentStyles) === coreNames(incomingStyles);
801  }
802
803  function structureIsCompatible(parsed) {
804    var incoming = parsed.querySelector(contentSelector);
805    var current = document.querySelector(contentSelector);
806    if (!incoming || !current || incoming.tagName !== current.tagName) return false;
807
808    /* Different WordPress templates may enqueue different page CSS. Missing
809       destination styles are loaded by ensureRequiredAssets(), while the live
810       header shell remains untouched. Do not reject navigation solely because
811       the current and destination stylesheet lists differ. */
812
813    /* Header, mobile header, footer, side area and wrapper are deliberately not
814       replaced. If any of those structures differ, use a normal page load. */
815    var structuralSelectors = [
816      '.eltd-wrapper',
817      '.eltd-wrapper-inner',
818      '#eltd-content-wrapper',
819      '.eltd-page-header',
820      'footer'
821    ];
822
823    if (!(config.preserveHeaderShell || config.neverHardReload) &&
824        !structuralSelectors.every(function (selector) {
825          return elementSignature(document, selector) === elementSignature(parsed, selector);
826        })) {
827      if (config.debug && window.console) {
828        console.warn('[AP AJAX] Shared shell mismatch; using normal navigation.');
829      }
830      return false;
831    }
832
833    /*
834     * Custom archive/rewrite routes may render a different mobile-menu copy or
835     * omit the optional side-menu container. Those nodes are outside
836     * contentSelector and are not replaced, so they must not force a full reload.
837     */
838
839    /* Do not AJAX into or out of deliberately standalone/special templates. */
840    var specialBodyClasses = [
841      'page-template-landing-page',
842      'page-template-landing-page-php',
843      'woocommerce-checkout',
844      'woocommerce-cart'
845    ];
846    if (!(config.preserveHeaderShell || config.neverHardReload) && specialBodyClasses.some(function (className) {
847      return document.body.classList.contains(className) || (parsed.body && parsed.body.classList.contains(className));
848    })) return false;
849
850    return true;
851  }
852
853  function isRepeatablePageLoader(src) {
854    var absolute = absoluteAssetUrl(src || '');
855
856    /*
857     * These scripts are page-local renderers rather than one-time application
858     * bundles. They must execute again after AJAX inserts fresh ad placeholders.
859     * Keep this allow-list deliberately narrow so theme/plugin scripts are never
860     * duplicated or reinitialised accidentally.
861     */
862    return /\/www\/delivery\/asyncjs\.php(?:[?#]|$)/i.test(absolute) ||
863      /\/js\/[a-f0-9]{16,}\.js(?:[?#]|$)/i.test(absolute);
864  }
865
866  function executeScripts(container) {
867    var scripts = Array.prototype.slice.call(container.querySelectorAll('script'));
868    scripts.forEach(function (oldScript) {
869      var src = oldScript.getAttribute('src');
870      if (src) {
871        var absolute = absoluteAssetUrl(src);
872        var repeatablePageLoader = isRepeatablePageLoader(absolute);
873        var alreadyLoaded = Array.prototype.some.call(document.querySelectorAll('script[src]'), function (script) {
874          return absoluteAssetUrl(script.getAttribute('src')) === absolute;
875        });
876        if (alreadyLoaded && !repeatablePageLoader) {
877          oldScript.remove();
878          return;
879        }
880      }
881
882      var newScript = document.createElement('script');
883      Array.prototype.slice.call(oldScript.attributes).forEach(function (attr) {
884        newScript.setAttribute(attr.name, attr.value);
885      });
886      if (!src) newScript.text = oldScript.textContent;
887      oldScript.parentNode.replaceChild(newScript, oldScript);
888    });
889  }
890
891  function syncOneMenu(incomingMenu, currentMenu) {
892    if (!incomingMenu || !currentMenu) return;
893
894    var currentItems = currentMenu.querySelectorAll('[id^="nav-menu-item-"]');
895    Array.prototype.forEach.call(currentItems, function (item) {
896      var incomingItem = incomingMenu.querySelector('#' + escapeSelector(item.id));
897      if (!incomingItem) return;
898      item.className = incomingItem.className;
899
900      var currentLink = item.firstElementChild && item.firstElementChild.tagName === 'A' ? item.firstElementChild : null;
901      var incomingLink = incomingItem.firstElementChild && incomingItem.firstElementChild.tagName === 'A' ? incomingItem.firstElementChild : null;
902      if (currentLink && incomingLink) currentLink.className = incomingLink.className;
903    });
904  }
905
906  function syncMenuState(parsed) {
907    var incomingPrimary = parsed.querySelector(menuSelector);
908    var currentPrimary = document.querySelector(menuSelector);
909    syncOneMenu(incomingPrimary, currentPrimary);
910
911    /* The theme can render desktop, sticky and mobile copies of the same menu. */
912    var currentMenus = document.querySelectorAll('nav ul, .eltd-mobile-nav ul');
913    Array.prototype.forEach.call(currentMenus, function (currentMenu) {
914      if (currentMenu === currentPrimary || !currentMenu.id) return;
915      syncOneMenu(parsed.getElementById(currentMenu.id), currentMenu);
916    });
917  }
918
919  function syncHead(parsed) {
920    document.title = parsed.title || document.title;
921
922    ['canonical', 'prev', 'next'].forEach(function (rel) {
923      var incoming = parsed.querySelector('link[rel="' + rel + '"]');
924      var current = document.querySelector('link[rel="' + rel + '"]');
925      if (incoming && current) current.href = incoming.href;
926      else if (incoming && !current) document.head.appendChild(incoming.cloneNode(true));
927      else if (!incoming && current && rel !== 'canonical') current.remove();
928    });
929
930    ['description', 'robots'].forEach(function (name) {
931      var incoming = parsed.querySelector('meta[name="' + name + '"]');
932      var current = document.querySelector('meta[name="' + name + '"]');
933      if (incoming && current) current.setAttribute('content', incoming.getAttribute('content') || '');
934      else if (incoming && !current) document.head.appendChild(incoming.cloneNode(true));
935      else if (!incoming && current && name !== 'robots') current.remove();
936    });
937  }
938
939  function recalcUncoveringFooter() {
940    var footer = document.querySelector('footer');
941    var wrapper = document.getElementById('eltd-content-wrapper');
942    if (!footer) return;
943
944    if (document.body.classList.contains('eltd-uncovering-footer') && wrapper && !document.documentElement.classList.contains('touch')) {
945      var inner = footer.querySelector('.eltd-footer-inner');
946      var height = inner ? inner.getBoundingClientRect().height : footer.getBoundingClientRect().height;
947      wrapper.style.marginBottom = height + 'px';
948      footer.style.height = height + 'px';
949      footer.style.visibility = 'visible';
950      wrapper.style.borderBottom = '2px solid #dddcdc';
951      wrapper.style.backgroundColor = window.getComputedStyle(document.body).backgroundColor;
952    } else {
953      footer.style.height = '';
954      footer.style.visibility = '';
955      if (wrapper) {
956        wrapper.style.marginBottom = '';
957        wrapper.style.borderBottom = '';
958        wrapper.style.backgroundColor = '';
959      }
960    }
961  }
962
963
964
965  function initialisePreservedHeaderMenus() {
966    $ = getJQuery();
967    if (!$) return;
968
969    var itemSelector = '.eltd-page-header .eltd-drop-down > ul > li.eltd-menu-has-sub, ' +
970      '.eltd-page-header .eltd-drop-down > ul > li.menu-item-has-children';
971
972    /*
973     * The header is deliberately preserved during AJAX navigation. Some cached
974     * pages leave visibility:hidden and overflow:hidden inline on the preserved
975     * submenu. Those inline values override the theme's hover CSS even when the
976     * normal eltd-drop-down-start class is added. Remove only those stale values
977     * and provide namespaced hover handling for both main and sticky menus.
978     */
979    function normalise($item) {
980      var $second = $item.children('.eltd-menu-second');
981      if (!$second.length) return $second;
982
983      var node = $second.get(0);
984      node.style.removeProperty('visibility');
985      node.style.removeProperty('overflow');
986
987      if (!$second.data('ap-natural-height')) {
988        var inner = $second.children('.eltd-menu-inner').get(0);
989        var natural = inner ? inner.scrollHeight : node.scrollHeight;
990        if (natural > 0) $second.data('ap-natural-height', natural);
991      }
992
993      return $second;
994    }
995
996    $(itemSelector).each(function () {
997      normalise($(this));
998    });
999
1000    $(document)
1001      .off('.apPreservedMenu')
1002      .on('mouseenter.apPreservedMenu focusin.apPreservedMenu', itemSelector, function () {
1003        var $item = $(this);
1004        var $second = normalise($item);
1005        if (!$second.length) return;
1006
1007        var height = $second.data('ap-natural-height');
1008        if (!height) {
1009          var inner = $second.children('.eltd-menu-inner').get(0);
1010          height = inner ? inner.scrollHeight : $second.get(0).scrollHeight;
1011          if (height > 0) $second.data('ap-natural-height', height);
1012        }
1013
1014        $second.addClass('eltd-drop-down-start').css({
1015          height: height ? height + 'px' : 'auto',
1016          opacity: '1',
1017          visibility: 'visible',
1018          overflow: 'visible'
1019        });
1020      })
1021      .on('mouseleave.apPreservedMenu focusout.apPreservedMenu', itemSelector, function (event) {
1022        if (event.type === 'focusout' && event.relatedTarget && this.contains(event.relatedTarget)) return;
1023
1024        var $second = $(this).children('.eltd-menu-second');
1025        if (!$second.length) return;
1026
1027        $second.removeClass('eltd-drop-down-start').css({
1028          height: '0px',
1029          opacity: '0'
1030        });
1031        $second.get(0).style.removeProperty('visibility');
1032        $second.get(0).style.removeProperty('overflow');
1033      });
1034  }
1035
1036  function closeOpenPreservedSubmenus() {
1037    $ = getJQuery();
1038    if (!$) return;
1039
1040    var $openMenus = $('.eltd-page-header .eltd-menu-second').filter(function () {
1041      var node = this;
1042      var style = window.getComputedStyle(node);
1043      return node.classList.contains('eltd-drop-down-start') ||
1044        parseFloat(style.opacity || '0') > 0 ||
1045        node.getBoundingClientRect().height > 1;
1046    });
1047
1048    if (!$openMenus.length) return;
1049
1050    $openMenus.each(function () {
1051      var $second = $(this);
1052      $second.removeClass('eltd-drop-down-start').css({
1053        height: '0px',
1054        opacity: '0'
1055      });
1056      this.style.removeProperty('visibility');
1057      this.style.removeProperty('overflow');
1058    });
1059  }
1060
1061  function reinitialiseFeatures() {
1062    /*
1063     * Header-critical AJAX may execute before Rocket Loader releases jQuery.
1064     * The page swap itself does not require jQuery, so never turn that into a
1065     * hard-navigation fallback. Run Newsroom/jQuery enhancement hooks only
1066     * when jQuery is actually available.
1067     */
1068    $ = getJQuery();
1069
1070    try {
1071      if ($ && window.eltd) {
1072        window.eltd.body = $(document.body);
1073        window.eltd.windowWidth = $(window).width();
1074        window.eltd.windowHeight = $(window).height();
1075      }
1076
1077      var common = window.eltd && window.eltd.modules && window.eltd.modules.common;
1078      if (common) {
1079        ['eltdFluidVideo', 'eltdPrettyPhoto', 'eltdPostGallerySlider', 'eltdPreloadBackgroun
1079ds',
1080         'eltdInitAudioPlayer', 'eltdInitSelfHostedVideoPlayer', 'eltdSelfHostedVideoSize', 'eltdInitParallax']
1081          .forEach(function (name) {
1082            if (typeof common[name] === 'function') {
1083              try { common[name](); } catch (e) {}
1084            }
1085          });
1086      }
1087
1088      if (typeof window.vc_js === 'function') {
1089        try { window.vc_js(); } catch (e) {}
1090      }
1091
1092      if (window.Waypoint && typeof window.Waypoint.refreshAll === 'function') {
1093        try { window.Waypoint.refreshAll(); } catch (e) {}
1094      }
1095
1096      if ($) {
1097        try { $(document.body).trigger('post-load'); } catch (e) {}
1098        try { $(window).trigger('resize').trigger('scroll'); } catch (e) {}
1099      } else {
1100        /*
1101         * Native wake-up is enough for the custom sticky/sidebar runtimes.
1102         * A short delayed pass catches jQuery once Rocket Loader releases it.
1103         */
1104        try { window.dispatchEvent(new Event('resize')); } catch (e) {}
1105        try { window.dispatchEvent(new Event('scroll')); } catch (e) {}
1106
1107        window.setTimeout(function () {
1108          $ = getJQuery();
1109
1110          if ($) {
1111            try { $(document.body).trigger('post-load'); } catch (e) {}
1112            try { $(window).trigger('resize').trigger('scroll'); } catch (e) {}
1113            try { initialisePreservedHeaderMenus(); } catch (e) {}
1114          }
1115        }, 120);
1116      }
1117    } finally {
1118      window.requestAnimationFrame(recalcUncoveringFooter);
1119      window.setTimeout(recalcUncoveringFooter, 120);
1120      window.setTimeout(recalcUncoveringFooter, 420);
1121    }
1122  }
1123
1124  function syncBodyClasses(parsedBody) {
1125    if (!parsedBody) return;
1126
1127    /*
1128     * WordPress template classes must follow the destination so home, single,
1129     * category, archive and custom templates retain their CSS. Runtime shell
1130     * classes are preserved because they belong to the already-mounted header.
1131     */
1132    var preserve = [
1133      'admin-bar', 'logged-in', 'customize-support',
1134      'eltd-side-menu-opened', 'eltd-mobile-nav-opened',
1135      'ap-ajax-loading'
1136    ];
1137    var runtime = preserve.filter(function (name) {
1138      return document.body.classList.contains(name);
1139    });
1140
1141    document.body.className = parsedBody.className || '';
1142    runtime.forEach(function (name) { document.body.classList.add(name); });
1143  }
1144
1145  function syncBodyAttributes(parsedBody) {
1146    if (!parsedBody) return;
1147
1148    /* Keep accessibility/schema attributes in step with the destination while
1149       never replacing the body or any persistent header descendants. */
1150    ['itemscope', 'itemtype', 'itemid', 'dir'].forEach(function (name) {
1151      if (parsedBody.hasAttribute(name)) {
1152        document.body.setAttribute(name, parsedBody.getAttribute(name) || '');
1153      } else if (name !== 'dir') {
1154        document.body.removeAttribute(name);
1155      }
1156    });
1157  }
1158
1159  function markPersistentShell() {
1160    var header = document.querySelector('.eltd-page-header');
1161    var mobileHeader = document.querySelector('.eltd-mobile-header');
1162    var content = document.querySelector(contentSelector);
1163    if (header) header.setAttribute('data-ap-persistent-header', 'true');
1164    if (mobileHeader) mobileHeader.setAttribute('data-ap-persistent-header', 'true');
1165    if (content) content.setAttribute('data-ap-ajax-root', 'true');
1166  }
1167
1168  /*
1169   * Close WP Responsive Menu through the plugin's own click handler.
1170   * No plugin classes, styles, overlay states or body states are changed here.
1171   */
1172  function requestPluginMobileMenuClose() {
1173    var panel = document.getElementById('mg-wprm-wrap');
1174    var bar = document.getElementById('wprmenu_bar');
1175    var overlay = document.querySelector('.wprm-overlay');
1176    var hamburger = bar && bar.querySelector('.hamburger');
1177
1178    var isOpen = Boolean(
1179      (panel && panel.classList.contains('cbp-spmenu-open')) ||
1180      (bar && bar.classList.contains('active')) ||
1181      (overlay && overlay.classList.contains('active')) ||
1182      (hamburger && hamburger.classList.contains('is-active'))
1183    );
1184
1185    if (!isOpen || !bar) return;
1186
1187    /* Let the successful page replacement settle before the plugin closes. */
1188    window.requestAnimationFrame(function () {
1189      window.requestAnimationFrame(function () {
1190        try {
1191          bar.dispatchEvent(new MouseEvent('click', {
1192            bubbles: true,
1193            cancelable: true,
1194            view: window
1195          }));
1196        } catch (error) {
1197          /* Older mobile browsers still support the native click method. */
1198          if (typeof bar.click === 'function') bar.click();
1199        }
1200      });
1201    });
1202  }
1203
1204  function updateDocument(parsed, url, pushState, closeSubmenuAfterLoad, closeMobileMenuAfterLoad) {
1205    if (!structureIsCompatible(parsed)) throw new Error('AJAX structure mismatch');
1206
1207    var incoming = parsed.querySelector(contentSelector);
1208    var current = document.querySelector(contentSelector);
1209    var imported = document.importNode(incoming, true);
1210
1211    current.replaceWith(imported);
1212    syncHead(parsed);
1213    syncMenuState(parsed);
1214
1215    syncBodyClasses(parsed.body);
1216    syncBodyAttributes(parsed.body);
1217    syncPersistentShellState(parsed);
1218    markPersistentShell();
1219
1220    executeScripts(imported);
1221
1222    if (pushState) window.history.pushState({ apAjax: true }, '', url);
1223    currentUrl = url;
1224    window.scrollTo(0, 0);
1225
1226    if (closeSubmenuAfterLoad) closeOpenPreservedSubmenus();
1227    if (closeMobileMenuAfterLoad) requestPluginMobileMenuClose();
1228    reinitialiseFeatures();
1229
1230    var loadedEvent;
1231    try {
1232      loadedEvent = new CustomEvent('ap:ajax:loaded', { detail: { url: url } });
1233    } catch (e) {
1234      loadedEvent = document.createEvent('CustomEvent');
1235      loadedEvent.initCustomEvent('ap:ajax:loaded', false, false, { url: url });
1236    }
1237    document.dispatchEvent(loadedEvent);
1238
1239    $ = getJQuery();
1240    if ($) {
1241      try {
1242        $(document).trigger('ap_ajax_loaded', [url]);
1243      } catch (e) {}
1244    }
1245  }
1246
1247  function isArchiveContext(body) {
1248    if (!body) return false;
1249    return body.classList.contains('category') ||
1250      body.classList.contains('archive') ||
1251      body.classList.contains('search') ||
1252      body.classList.contains('blog');
1253  }
1254
1255  function hasPagination(root) {
1256    if (!root) return false;
1257    return !!root.querySelector(
1258      '.eltd-pagination, .eltd-blog-pagination, .pagination, .page-numbers, ' +
1259      'nav.navigation.pagination, .wp-pagenavi, [class*="pagination"]'
1260    );
1261  }
1262
1263  function validateFetchedDocument(parsed, url) {
1264    var incoming = parsed.querySelector(contentSelector);
1265    if (!incoming) throw new Error('Destination content wrapper missing');
1266
1267    /* A cached archive response must retain its server-rendered pagination.
1268       Do not reject a genuinely one-page archive, but reject suspiciously
1269       truncated responses when the current archive visibly has pagination. */
1270    if (isArchiveContext(parsed.body) && isArchiveContext(document.body)) {
1271      var currentContent = document.querySelector(contentSelector);
1272      if (hasPagination(currentContent) && !hasPagination(incoming)) {
1273        throw new Error('Archive response is missing pagination');
1274      }
1275    }
1276
1277    /* Single posts must contain a recognisable post/article body. This catches
1278       cache/plugin fragment responses before the current category is replaced. */
1279    if (isSinglePostContext(parsed.body)) {
1280      var hasPost = incoming.querySelector(
1281        'article, .eltd-blog-single, .single-post, .eltd-post-content, .eltd-post-text'
1282      );
1283      if (!hasPost) throw new Error('Single-post response is incomplete');
1284    }
1285
1286    return parsed;
1287  }
1288
1289  function fetchNavigationHtml(url, signal) {
1290    if (!isFreshLandingTarget(url)) {
1291      return fetchHtmlWithProgress(url, signal, 0, false);
1292    }
1293
1294    var dirtyVersion = landingDirtyVersion(url);
1295
1296    /*
1297     * No detected change = normal fast AJAX immediately.
1298     * The version detector runs independently in the background.
1299     */
1300    if (!dirtyVersion) {
1301      return fetchHtmlWithProgress(url, signal, 0, false);
1302    }
1303
1304    /*
1305     * A real change was already detected.
1306     *
1307     * Start normal cached AJAX and fresh HTML at the SAME time. Only the normal
1308     * AJAX request drives the progress bar. The fresh request is silent, so the
1309     * user sees one continuous loading effect instead of two phases.
1310     *
1311     * If fresh HTML arrives inside the configured budget it wins. Otherwise we
1312     * use the normal cached response and keep this route dirty for a later retry.
1313     */
1314    var freshController = new AbortController();
1315    var fallbackTimer = null;
1316
1317    function abortFreshFromParent() {
1318      if (!freshController.signal.aborted) freshController.abort();
1319    }
1320
1321    if (signal.aborted) {
1322      abortFreshFromParent();
1323    } else {
1324      signal.addEventListener('abort', abortFreshFromParent, { once: true });
1325    }
1326
1327    var normalPromise = fetchHtmlWithProgress(url, signal, 0, false);
1328
1329    var freshPromise = fetchFreshHtmlSilently(url, freshController.signal)
1330      .then(function (freshHtml) {
1331        return { type: 'fresh', html: freshHtml };
1332      })
1333      .catch(function () {
1334        if (signal.aborted) {
1335          var parentAbort = new Error('Navigation aborted');
1336          parentAbort.name = 'AbortError';
1337          throw parentAbort;
1338        }
1339        return null;
1340      });
1341
1342    var budgetPromise = new Promise(function (resolve) {
1343      fallbackTimer = window.setTimeout(function () {
1344        resolve({ type: 'budget' });
1345      }, editorialFreshBudgetMs);
1346    });
1347
1348    return Promise.race([freshPromise, budgetPromise]).then(function (winner) {
1349      if (winner && winner.type === 'fresh') {
1350        if (fallbackTimer) {
1351          window.clearTimeout(fallbackTimer);
1352          fallbackTimer = null;
1353        }
1354        signal.removeEventListener('abort', abortFreshFromParent);
1355        markLandingFresh(url, dirtyVersion);
1356        return winner.html;
1357      }
1358
1359      abortFreshFromParent();
1360      signal.removeEventListener('abort', abortFreshFromParent);
1361      return normalPromise;
1362    });
1363  }
1364
1365
1366  var apIntentCache = new Map();
1367  var AP_INTENT_TTL = 30000;
1368  var AP_INTENT_MAX = 6;
1369  var apPrefetchTimer = null;
1370
1371  function pruneIntentCache() {
1372    var now = Date.now();
1373    apIntentCache.forEach(function (entry, key) {
1374      if (!entry || entry.expiresAt <= now) apIntentCache.delete(key);
1375    });
1376    while (apIntentCache.size > AP_INTENT_MAX) {
1377      var firstKey = apIntentCache.keys().next().value;
1378      if (!firstKey) break;
1379      apIntentCache.delete(firstKey);
1380    }
1381  }
1382
1383  function eligiblePrefetchUrl(value) {
1384    var url;
1385    try { url = new URL(value, window.location.href); } catch (e) { return ''; }
1386    if (!sameOrigin(url) || !/^https?:$/.test(url.protocol)) return '';
1387    if (isFreshLandingTarget(url.href)) return '';
1388    if (url.pathname.indexOf('/wp-admin') === 0 || url.pathname.indexOf('/wp-login.php') === 0) return '';
1389    if (/\.(?:jpg|jpeg|png|gif|webp|svg|pdf|zip|xml|json|mp4|mp3|docx?|xlsx?)$/i.test(url.pathname)) return '';
1390    return url.href;
1391  }
1392
1393  function prefetchDestination(value) {
1394    var url = eligiblePrefetchUrl(value);
1395    if (!url) return;
1396    pruneIntentCache();
1397    var existing = apIntentCache.get(url);
1398    if (existing && existing.expiresAt > Date.now()) return;
1399
1400    var entry = { html: '', promise: null, expiresAt: Date.now() + AP_INTENT_TTL };
1401    entry.promise = fetch(url, {
1402      method: 'GET',
1403      credentials: 'same-origin',
1404      headers: { 'Accept': 'text/html,application/xhtml+xml' },
1405      cache: 'default'
1406    }).then(function (r) {
1407      if (!r.ok) throw new Error('Prefetch HTTP ' + r.status);
1408      return r.text();
1409    }).then(function (html) {
1410      if (!html || html.length < 128) throw new Error('Incomplete prefetch');
1411      entry.html = html;
1412      entry.promise = null;
1413      entry.expiresAt = Date.now() + AP_INTENT_TTL;
1414      return html;
1415    }).catch(function () {
1416      apIntentCache.delete(url);
1417      return '';
1418    });
1419
1420    apIntentCache.set(url, entry);
1421    pruneIntentCache();
1422  }
1423
1424  function rememberNavigationHtml(url, html) {
1425    if (!html || isFreshLandingTarget(url)) return;
1426
1427    var eligible = eligiblePrefetchUrl(url);
1428    if (!eligible) return;
1429
1430    apIntentCache.set(eligible, {
1431      html: html,
1432      promise: null,
1433      expiresAt: Date.now() + AP_INTENT_TTL
1434    });
1435
1436    pruneIntentCache();
1437  }
1438
1439  function cachedNavigationHtml(url, signal) {
1440    pruneIntentCache();
1441    var entry = apIntentCache.get(url);
1442    if (!entry || entry.expiresAt <= Date.now()) return null;
1443    if (entry.html) return Promise.resolve(entry.html);
1444    if (entry.promise) {
1445      return entry.promise.then(function (html) {
1446        if (signal.aborted) {
1447          var aborted = new Error('Navigation aborted');
1448          aborted.name = 'AbortError';
1449          throw aborted;
1450        }
1451        return html || null;
1452      });
1453    }
1454    return null;
1455  }
1456
1457
1458  /*
1459   * Keep the progress animation visibly continuous on very fast/prefetched pages.
1460   * The DOM must not swap while the CSS width transition is still visually
1461   * sitting around the middle of the bar.
1462   */
1463  function waitForProgressPaint(target, delay) {
1464    setProgress(target);
1465
1466    return new Promise(function (resolve) {
1467      window.requestAnimationFrame(function () {
1468        window.setTimeout(resolve, Math.max(0, Number(delay) || 0));
1469      });
1470    });
1471  }
1472
1473  function navigate(url, pushState, closeSubmenuAfterLoad, closeMobileMenuAfterLoad) {
1474    if (requestController) requestController.abort();
1475
1476    var controller = new AbortController();
1477    var generation = ++navigationGeneration;
1478    requestController = controller;
1479    setLoading(true);
1480
1481    var cachedHtmlPromise = isFreshLandingTarget(url)
1482      ? null
1483      : cachedNavigationHtml(url, controller.signal);
1484
1485    var htmlPromise = cachedHtmlPromise || fetchNavigationHtml(url, controller.signal);
1486
1487    htmlPromise
1488      .then(function (html) {
1489        rememberNavigationHtml(url, html);
1490        setProgress(72);
1491        var parsed = new DOMParser().parseFromString(html, 'text/html');
1492        validateFetchedDocument(parsed, url);
1493        if (!structureIsCompatible(parsed)) throw new Error('AJAX structure mismatch');
1494        setProgress(78);
1495        return ensureRequiredAssets(parsed).then(function () {
1496          /*
1497           * Everything needed for the destination is ready. Give the progress
1498           * bar one painted beat to visibly reach the high 90s, then swap only
1499           * .eltd-content. This preserves the persistent header/footer shell.
1500           */
1501          return waitForProgressPaint(94, 45).then(function () {
1502            updateDocument(
1503              parsed,
1504              url,
1505              pushState,
1506              closeSubmenuAfterLoad,
1507              closeMobileMenuAfterLoad
1508            );
1509            setProgress(98);
1510          });
1511        });
1512      })
1513      .catch(function (error) {
1514        if (error.name === 'AbortError') return;
1515
1516        if (window.console && config.debug) {
1517          console.error('[AP AJAX] Navigation failed; using browser fallback.', error);
1518        }
1519
1520        var failedEvent;
1521        try {
1522          failedEvent = new CustomEvent('ap:ajax:error', {
1523            detail: { url: url, error: String(error && error.message || error), fallback: true }
1524          });
1525        } catch (e) {
1526          failedEvent = document.createEvent('CustomEvent');
1527          failedEvent.initCustomEvent('ap:ajax:error', false, false, {
1528            url: url,
1529            error: String(error && error.message || error),
1530            fallback: true
1531          });
1532        }
1533        document.dispatchEvent(failedEvent);
1534
1535        /* Keep the bar visibly active until the browser takes over. */
1536        setProgress(Math.max(currentProgress, 94));
1537        window.setTimeout(function () {
1538          window.location.assign(url);
1539        }, 90);
1540        return;
1541      })
1542      .then(function () {
1543        /*
1544         * Only the navigation that currently owns the progress UI may finish
1545         * it. An older aborted request must never clear/reset a newer request's
1546         * bar or loading state.
1547         */
1548        if (generation !== navigationGeneration || requestController !== controller) {
1549          return;
1550        }
1551
1552        requestController = null;
1553        setLoading(false);
1554      });
1555  }
1556
1557  /* Public entry point used by the tiny bootstrap in header.php. */
1558  window.AP_AJAX_NAV = Object.assign({}, window.AP_AJAX_NAV || {}, {
1559    ready: true,
1560    runtimeVersion: '47.6-header-instant',
1561    navigate: function (url, pushState, closeSubmenuAfterLoad, closeMobileMenuAfterLoad) {
1562      navigate(url, pushState !== false, !!closeSubmenuAfterLoad, !!closeMobileMenuAfterLoad);
1563    }
1564  });
1565
1566
1567  document.addEventListener('pointerover', function (event) {
1568    var target = event.target;
1569    if (!target || typeof target.closest !== 'function') return;
1570    var link = target.closest('a[href]');
1571    if (!link) return;
1572    if (apPrefetchTimer) window.clearTimeout(apPrefetchTimer);
1573    apPrefetchTimer = window.setTimeout(function () {
1574      prefetchDestination(link.href);
1575    }, 45);
1576  }, { passive: true, capture: true });
1577
1578  document.addEventListener('touchstart', function (event) {
1579    var target = event.target;
1580    if (!target || typeof target.closest !== 'function') return;
1581    var link = target.closest('a[href]');
1582    if (link) prefetchDestination(link.href);
1583  }, { passive: true, capture: true });
1584
1585
1586  document.addEventListener('pointerdown', function (event) {
1587    var target = event.target;
1588    if (!target || typeof target.closest !== 'function') return;
1589    var link = target.closest('a[href]');
1590    if (!link) return;
1591    prefetchDestination(link.href);
1592  }, {
1593    passive: true,
1594    capture: true
1595  });
1596
1597  document.addEventListener('click', function (event) {
1598    var target = event.target;
1599    if (!target || typeof target.closest !== 'function') return;
1600    var link = target.closest('a');
1601    if (!shouldHandleLink(link, event)) return;
1602    var cameFromSubmenu = !!link.closest('.eltd-page-header .eltd-menu-second');
1603    var cameFromPluginMobileMenu = !!link.closest('#mg-wprm-wrap');
1604    event.preventDefault();
1605    event.stopImmediatePropagation();
1606    document.documentElement.setAttribute('data-ap-last-navigation', 'ajax');
1607    navigate(link.href, true, cameFromSubmenu, cameFromPluginMobileMenu);
1608  }, true);
1609
1610  document.addEventListener('submit', function (event) {
1611    var form = event.target;
1612    if (!form || String(form.method).toLowerCase() !== 'get') return;
1613    if (form.closest('[data-no-ajax], .no-ajax')) return;
1614    if (!(form.matches('[role="search"], .searchform, .woocommerce-product-search') || form.querySelector('input[name="s"]'))) return;
1615
1616    var action = new URL(form.action || window.location.href, window.location.href);
1617    if (!sameOrigin(action)) return;
1618
1619    event.preventDefault();
1620    var params = new URLSearchParams(new FormData(form));
1621    action.search = params.toString();
1622    navigate(action.toString(), true);
1623  }, true);
1624
1625  window.addEventListener('popstate', function () {
1626    if (window.location.href === currentUrl) return;
1627    navigate(window.location.href, false);
1628  });
1629
1630  markPersistentShell();
1631  initialisePreservedHeaderMenus();
1632
1633  /* Background-only editorial detector: immediate first check, then every 30s. */
1634  scheduleEditorialVersionCheck(0);
1635
1636  document.addEventListener('visibilitychange', function () {
1637    if (!document.hidden && (Date.now() - cachedVersionPayloadAt) >= editorialVersionPollMs) {
1638      runEditorialVersionCheck();
1639    }
1640  });
1641
1642  window.addEventListener('focus', function () {
1643    if ((Date.now() - cachedVersionPayloadAt) >= editorialVersionPollMs) {
1644      runEditorialVersionCheck();
1645    }
1646  });
1647
1648  window.addEventListener('load', function () {
1649    recalcUncoveringFooter();
1650    initialisePreservedHeaderMenus();
1651  });
1652})(window, document, window.jQuery);

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.