PageSourceSearch

https://www.nowayrshireradio.co.uk/wp-content/themes/proradio/comp…/_proradio-ajax-pageload.js?ver=4.0.3

js nowayrshireradio.co.uk collected 2026-10-08 07:52:33 UTC 43,874 bytes, 1,419 lines download raw bytes

1/**====================================================================
2 *
3 *  QT Ajax Page Loader main script
4 *  @author ProRadio
5 *  
6 ====================================================================**/
7
8
9window.PRAJAX_BENCH_ENABLED = false;
10
11document.addEventListener('DOMContentLoaded', function () {
12	window.PRAJAX_BENCH_ENABLED =
13		document.body && document.body.classList.contains('proradio-ajax_debug_on');
14});
15
16
17function praplWaitImagesSettled(rootElOrJq, timeoutMs) {
18	try {
19		var $root = (rootElOrJq && rootElOrJq.jquery) ? rootElOrJq : jQuery(rootElOrJq);
20		var imgs = $root.find("img").toArray();
21		var pending = [];
22
23		for (var i = 0; i < imgs.length; i++) {
24			var img = imgs[i];
25			if (!img) continue;
26			if (img.complete) continue; // already done
27			pending.push(img);
28		}
29
30		if (!pending.length) return Promise.resolve();
31
32		var pAll = Promise.allSettled(pending.map(function (img) {
33			return new Promise(function (res) {
34				img.onload = img.onerror = function () { res(); };
35			});
36		}));
37
38		var pTimeout = new Promise(function (res) {
39			setTimeout(res, timeoutMs || 2000);
40		});
41
42		return Promise.race([pAll, pTimeout]);
43	} catch (e) {
44		return Promise.resolve();
45	}
46}
47
48
49function praplWaitImagesSettledDetailed(rootElOrJq, timeoutMs, label) {
50var $root = (rootElOrJq && rootElOrJq.jquery) ? rootElOrJq : jQuery(rootElOrJq);
51var imgs = $root.find("img").toArray();
52var t0 = (performance && performance.now) ? performance.now() : Date.now();
53var rows = [];
54var pending = 0;
55var doneResolve;
56
57timeoutMs = timeoutMs || 3000;
58label = label || "Images detailed";
59
60function now() { return (performance && performance.now) ? performance.now() : Date.now(); }
61
62function normSrc(img) {
63	return (img.currentSrc || img.src || img.getAttribute("src") || "").trim();
64}
65function praplImgInfo(img) {
66return {
67	src: (img.getAttribute("src") || "").trim(),
68	currentSrc: (img.currentSrc || "").trim(),
69	dataSrc: (img.getAttribute("data-src") || "").trim(),
70	dataLazySrc: (img.getAttribute("data-lazy-src") || "").trim(),
71	srcset: (img.getAttribute("srcset") || "").trim(),
72	loading: (img.getAttribute("loading") || "").trim(),
73	complete: !!img.complete,
74	natural_w: img.naturalWidth || 0,
75	natural_h: img.naturalHeight || 0
76};
77}
78
79
80	
81function pushRow(img, evt, ms) {
82var info = praplImgInfo(img);
83rows.push({
84	ms: Math.round(ms),
85	event: evt,
86	loading: info.loading,
87	complete: info.complete,
88	natural_w: info.natural_w,
89	natural_h: info.natural_h,
90	src: info.src,
91	currentSrc: info.currentSrc,
92	dataSrc: info.dataSrc,
93	dataLazySrc: info.dataLazySrc
94});
95}
96
97function doneOne() {
98	pending--;
99	if (pending <= 0 && doneResolve) doneResolve(rows);
100}
101
102// Nothing to wait
103if (!imgs.length) return Promise.resolve([]);
104
105// Track only images that are not complete yet OR have no natural size (edge cases)
106var tracked = [];
107
108for (var i = 0; i < imgs.length; i++) {
109	var img = imgs[i];
110	if (!img) continue;
111
112	// Ignore lazy images completely (they are intentionally not blocking)
113	var loading = (img.getAttribute("loading") || "").toLowerCase();
114	if (loading === "lazy") {
115		pushRow(img, "ignored-lazy", now() - t0);
116		continue;
117	}
118
119	// already loaded (or already failed but complete=true in some cases)
120	if (img.complete && (img.naturalWidth || img.naturalHeight || img.src)) {
121		pushRow(img, "already-complete", now() - t0);
122		continue;
123	}
124
125	tracked.push(img);
126}
127
128
129if (!tracked.length) {
130	console.groupCollapsed(label + " (no pending)");
131	console.table(rows.sort(function(a,b){ return b.ms - a.ms; }));
132	console.groupEnd();
133	return Promise.resolve(rows);
134}
135
136pending = tracked.length;
137
138var p = new Promise(function (res) { doneResolve = res; });
139
140// Per-image listeners + per-image timeout
141for (var j = 0; j < tracked.length; j++) {
142	(function (img) {
143		var finished = false;
144		var start = now();
145
146		function finish(evt) {
147			if (finished) return;
148			finished = true;
149
150			// cleanup (avoid memory leaks)
151			img.removeEventListener("load", onLoad);
152			img.removeEventListener("error", onError);
153
154			pushRow(img, evt, now() - start);
155			doneOne();
156		}
157
158		function onLoad() { finish("load"); }
159		function onError() { finish("error"); }
160
161		img.addEventListener("load", onLoad, { once: true });
162		img.addEventListener("error", onError, { once: true });
163
164		// In case browser never fires events (lazy-loading not triggered, src swapped later, etc.)
165		setTimeout(function () { finish("timeout"); }, timeoutMs);
166	})(tracked[j]);
167}
168
169return p.then(function (r) {
170	console.groupCollapsed(label + " (" + r.length + " rows)");
171	console.table(r.sort(function(a,b){ return b.ms - a.ms; }));
172	console.groupEnd();
173	return r;
174});
175}
176
177
178
179
180
181
182/* ============================================================
183* Pro Radio Ajax Benchmark (simple + robust)
184* Usage:
185*   PRAjaxBenchmarkMeasure("Initial http request","start");
186*   PRAjaxBenchmarkMeasure("Initial http request","stop");
187*   PRAjaxBenchmarkReport(); // at the very end
188* Toggle:
189*   window.PRAJAX_BENCH_ENABLED = true|false;
190* ========================================================== */
191(function (w) {
192"use strict";
193
194if (typeof w.PRAJAX_BENCH_ENABLED === "undefined") {
195w.PRAJAX_BENCH_ENABLED = true;
196}
197
198var bench = {
199enabled: function () { return !!w.PRAJAX_BENCH_ENABLED; },
200runId: 0,
201t0: 0,
202url: "",
203starts: Object.create(null),
204results: [],
205errors: [],
206reset: function (url) {
207  bench.runId++;
208  bench.t0 = (w.performance && performance.now) ? performance.now() : Date.now();
209  bench.url = url || "";
210  bench.starts = Object.create(null);
211  bench.results = [];
212  bench.errors = [];
213}
214};
215
216function nowMs() {
217return (w.performance && performance.now) ? performance.now() : Date.now();
218}
219
220function safeKey(name) {
221return String(name || "").trim() || "unnamed";
222}
223
224function measure(name, action) {
225if (!bench.enabled()) return;
226name = safeKey(name);
227action = String(action || "").toLowerCase();
228
229var t = nowMs();
230
231if (action === "start") {
232  bench.starts[name] = t;
233  return;
234}
235
236if (action === "stop") {
237  if (typeof bench.starts[name] === "undefined") {
238	bench.errors.push({ type: "stop_without_start", name: name, at: Math.round(t - bench.t0) });
239	return;
240  }
241  var d = t - bench.starts[name];
242  delete bench.starts[name];
243
244  bench.results.push({
245	name: name,
246	ms: Math.round(d)
247  });
248  return;
249}
250
251// ignore invalid action
252bench.errors.push({ type: "invalid_action", name: name, action: action });
253}
254
255function report() {
256if (!bench.enabled()) return;
257
258// Close any unclosed timers (optional, non-fatal)
259var t = nowMs();
260for (var k in bench.starts) {
261  if (!Object.prototype.hasOwnProperty.call(bench.starts, k)) continue;
262  bench.results.push({
263	name: k + " (auto-stop)",
264	ms: Math.round(t - bench.starts[k])
265  });
266}
267bench.starts = Object.create(null);
268
269// Aggregate by name (so you can start/stop the same label multiple times)
270var agg = Object.create(null);
271for (var i = 0; i < bench.results.length; i++) {
272  var r = bench.results[i];
273  if (!agg[r.name]) agg[r.name] = { name: r.name, calls: 0, total: 0, max: 0 };
274  agg[r.name].calls++;
275  agg[r.name].total += r.ms;
276  if (r.ms > agg[r.name].max) agg[r.name].max = r.ms;
277}
278
279var rows = [];
280for (var key in agg) {
281  if (!Object.prototype.hasOwnProperty.call(agg, key)) continue;
282  rows.push({
283	phase: agg[key].name,
284	calls: agg[key].calls,
285	total_ms: agg[key].total,
286	max_ms: agg[key].max,
287	avg_ms: Math.round(agg[key].total / agg[key].calls)
288  });
289}
290
291// Sort by total desc
292rows.sort(function (a, b) { return b.total_ms - a.total_ms; });
293
294var total = Math.round(nowMs() - bench.t0);
295
296console.groupCollapsed("PRAJAX BENCH #" + bench.runId + " " + bench.url + " (" + total + "ms)");
297console.table(rows);
298if (bench.errors.length) {
299  console.warn("PRAJAX BENCH warnings:");
300  console.table(bench.errors);
301}
302console.groupEnd();
303
304return { rows: rows, errors: bench.errors, total_ms: total, url: bench.url, runId: bench.runId };
305}
306
307// Expose
308w.PRAjaxBenchmarkStart = function (url) {
309if (!bench.enabled()) return;
310bench.reset(url);
311};
312
313w.PRAjaxBenchmarkMeasure = function (name, action) {
314try { measure(name, action); } catch (e) {}
315};
316
317w.PRAjaxBenchmarkReport = function () {
318try { return report(); } catch (e) {}
319};
320})(window);
321
322
323
324
325/**
326 * Lazy load compatibility
327 * ==================================================
328 * */
329
330
331(function ($) {
332	"use strict";
333
334	// Call this after your AJAX swap, e.g. proradio_after_ajax_dom_swap($newPageRoot[0]);
335	window.proradio_after_ajax_dom_swap = function (rootEl) {
336		if (!rootEl) return;
337
338		var $scope = $(rootEl);
339
340		// 1) Re-trigger Elementor init for widgets inside the new DOM
341		if (window.elementorFrontend && elementorFrontend.elementsHandler) {
342			try {
343				elementorFrontend.elementsHandler.runReadyTrigger($scope);
344			} catch (e) {}
345		}
346
347		// 2) Try Elementor native lazyload utilities (version-dependent)
348		if (window.elementorFrontend && elementorFrontend.utils && elementorFrontend.utils.lazyload) {
349			try {
350				elementorFrontend.utils.lazyload.run(rootEl);
351				return; // if it exists, let Elementor handle it
352			} catch (e) {}
353		}
354
355		// 3) Fallback: lazyload images + Elementor background-lazy patterns in the injected scope
356		proradio_fallback_lazyload(rootEl);
357	};
358
359	function proradio_fallback_lazyload(rootEl) {
360		var root = rootEl.nodeType ? rootEl : document;
361
362		// IMG candidates: common patterns used by Elementor / WP
363		var imgs = root.querySelectorAll([
364			"img[data-src]",
365			"img[data-lazy-src]",
366			"img[data-srcset]",
367			"img[data-lazy-srcset]",
368			"img.e-lazyload",
369			"img.elementor-lazyload"
370		].join(","));
371
372		// BG candidates: Elementor background lazy patterns can vary by version/templates
373		var bgs = root.querySelectorAll([
374			"[data-bg]",
375			"[data-background-image]",
376			"[data-e-bg-lazyload]",
377			"[data-bg-lazyload]"
378		].join(","));
379
380		var io = ("IntersectionObserver" in window) ? new IntersectionObserver(function (entries) {
381			entries.forEach(function (entry) {
382				if (!entry.isIntersecting) return;
383				var el = entry.target;
384				io.unobserve(el);
385
386				if (el.tagName === "IMG") {
387					load_img(el);
388				} else {
389					load_bg(el);
390				}
391			});
392		}, { rootMargin: "200px 0px", threshold: 0.01 }) : null;
393
394		// Observe or load immediately if no IO
395		imgs.forEach(function (img) {
396			if (io) io.observe(img);
397			else load_img(img);
398		});
399
400		bgs.forEach(function (el) {
401			if (io) io.observe(el);
402			else load_bg(el);
403		});
404	}
405
406	function load_img(img) {
407		// src
408		var src = img.getAttribute("data-src") || img.getAttribute("data-lazy-src");
409		if (src && !img.getAttribute("src")) {
410			img.setAttribute("src", src);
411		} else if (src) {
412			img.setAttribute("src", src);
413		}
414
415		// srcset
416		var srcset = img.getAttribute("data-srcset") || img.getAttribute("data-lazy-srcset");
417		if (srcset) img.setAttribute("srcset", srcset);
418
419		// sizes (optional)
420		var sizes = img.getAttribute("data-sizes");
421		if (sizes) img.setAttribute("sizes", sizes);
422
423		// cleanup
424		img.removeAttribute("data-src");
425		img.removeAttribute("data-lazy-src");
426		img.removeAttribute("data-srcset");
427		img.removeAttribute("data-lazy-srcset");
428		img.removeAttribute("data-sizes");
429		img.classList.remove("e-lazyload");
430		img.classList.remove("elementor-lazyload");
431	}
432
433	function load_bg(el) {
434		// Most common places to store the bg URL
435		var url =
436			el.getAttribute("data-bg") ||
437			el.getAttribute("data-background-image") ||
438			el.getAttribute("data-bg-lazyload");
439
440		// Some Elementor patterns use a flag + CSS var / other attribute; handle only direct URLs here
441		if (url) {
442			el.style.backgroundImage = 'url("' + url + '")';
443		}
444
445		el.removeAttribute("data-bg");
446		el.removeAttribute("data-background-image");
447		el.removeAttribute("data-bg-lazyload");
448		el.removeAttribute("data-e-bg-lazyload");
449	}
450
451})(jQuery);
452
453
454
455
456
457
458
459(function($) {
460	"use strict";
461
462	/**
463	 * ========================================================================================================================
464	 * Preloader animation
465	 * ========================================================================================================================
466	 */
467
468	 $.ProradioAjaxPreloader = {
469		preloaderItem: null,
470		globalContainer: null,
471		theTimeout: null,
472		isVisible: false,
473
474		sync: function () {
475			this.preloaderItem = $('.proradio-apl-preloader');
476			this.globalContainer = $('#proradio-global');
477		},
478
479		init: function () {
480			console.log('New preloader');
481			$('.proradio-ajax-body-preloader').removeClass('proradio-ajax-body-preloader');
482			if ($('body').hasClass('proradio-is_editor')) return;
483			this.sync();
484			if (this.preloaderItem.data('apl-preloader') == '1') {
485				// this.show(230);
486			}
487		},
488
489		show: function (timeout = 5000, autoTimeout = true) {
490			this.sync(); // critical: survive ajax swaps
491			if (!this.preloaderItem.length || !this.globalContainer.length) return;
492			if (this.preloaderItem.data('apl-changeloader') != '1') return;
493			if (!this.isVisible) {
494				this.isVisible = true;
495				void this.preloaderItem[0].offsetHeight;
496				this.globalContainer.addClass('proradio-apl-loading');
497			} else {
498				this.globalContainer.addClass('proradio-apl-loading');
499			}
500			if (this.theTimeout) clearTimeout(this.theTimeout);
501			if (autoTimeout === true) {
502				this.theTimeout = setTimeout(() => {
503					if (this.globalContainer.hasClass('proradio-apl-loading')) {
504						this.hide();
505					}
506				}, timeout);
507			}
508		},
509
510		hide: function () {
511			var th = this;
512			this.sync(); // critical: survive ajax swaps
513			if (!this.globalContainer || !this.globalContainer.length) return;
514			// $('.proradio-ajax-body-preloader').removeClass('proradio-ajax-body-preloader');
515			this.globalContainer.removeClass('proradio-apl-loading');
516			this.isVisible = false;
517			if (this.theTimeout) {
518				clearTimeout(this.theTimeout);
519				this.theTimeout = null;
520			}
521		}
522	};
523
524	
525	/*$.ProradioAjaxPreloader = {
526		preloaderItem: $('.proradio-apl-preloader'),
527		globalContainer: $('#proradio-global'),
528		theTimeout: null,
529		isVisible: false,
530		init: function () {
531			if ($('body').hasClass('proradio-is_editor')) return;
532			if (this.preloaderItem.data('apl-preloader') == '1') {
533				this.show(230);
534			}
535		},
536		show: function (timeout = 5000, autoTimeout = true) {
537			if (this.preloaderItem.data('apl-changeloader') != '1') return;
538			if (!this.isVisible) {
539				this.isVisible = true;
540				this.preloaderItem.removeClass('prapl-hidden').addClass('prapl-visible');
541				this.globalContainer.addClass('proradio-apl-loading');
542				if (this.preloaderItem.length) {
543					void this.preloaderItem[0].offsetHeight;
544				}
545			} else {
546				this.globalContainer.addClass('proradio-apl-loading');
547			}
548			if (this.theTimeout) clearTimeout(this.theTimeout);
549			if (autoTimeout === true) {
550				this.theTimeout = setTimeout(() => {
551					if (this.globalContainer.hasClass('proradio-apl-loading')) {
552						this.hide('debug 7');
553					}
554				}, timeout);
555			}
556		},
557		hide: function (debug) {
558			this.globalContainer.removeClass('proradio-apl-loading');
559			$('.proradio-ajax-body-preloader').removeClass('proradio-ajax-body-preloader');
560			this.preloaderItem.removeClass('prapl-visible').addClass('prapl-hidden');
561			this.isVisible = false;
562			if (this.theTimeout) {
563				clearTimeout(this.theTimeout);
564				this.theTimeout = null;
565			}
566		}
567	};
568*/
569
570	/**
571	 * ============================================================
572	 * Global HTML selectors
573	 * ============================================================                                                                                                                                         [description]
574	 */
575	var qtAplSelector = "#proradio-ajax-master";
576	var	qtAplMaincontent = $(qtAplSelector);
577	var	atAplPreloader = $("#qtajaxpreloadericon");
578
579
580	/**
581	 * ============================================================
582	 * Normal loading fallback
583	 * ============================================================                                                                                                                                         [description]
584	 */
585	$.fn.qtAplFallbackLink = function(e, link, code){
586		if(false !== e){
587			return e;
588		}
589		if( link ){
590			link = link.replace(/[?&]ajax-loaded=[^&]*(&|$)/, '$1').replace(/&$/, '');
591			window.location.replace(link);
592		}
593	}
594
595	/**
596	 * ============================================================
597	 * ScrollTop
598	 * ============================================================                                                                                                                                         [description]
599	 */
600	$.fn.qtAplScrollTop = function(){
601		$('html, body').animate({
602			scrollTop: 0
603		},60);
604		return true;
605	};
606
607
608	/**
609	 * ============================================================
610	 * WPML
611	 * ============================================================                                                                                                                                         [description]
612	 */
613	$('.wpml-ls-item, .wpml-ls-item-toggle').on("click",'a', function(e) {
614		$.fn.qtAplFallbackLink(e, $(this).attr('href'), '001');
615		return e;
616	});
617
618
619	/**
620	 * ============================================================
621	 * Search form submission
622	 * ============================================================                                                                                                                                         [description]
623	 */
624	 
625	 $('body').on('submit', 'form.proradio-search-ajax', function(e) {
626		e.preventDefault(); 
627		var $form = $(this); 
628		var searchQuery = $form.find('input[name="s"]').val(); 
629		var postType = $form.find('input[name="post_type"]').val(); 
630		var searchURL = $form.attr('action') + '?s=' + encodeURIComponent(searchQuery);
631		if (postType) {
632			searchURL += '&post_type=' + encodeURIComponent(postType);
633		}
634		var fakeLink = $('<a>', {
635			href: searchURL,
636			css: {
637				position: 'absolute',
638				top: '-9999px',
639				left: '-9999px',
640				height: '1px',
641				width: '1px',
642				overflow: 'hidden'
643			}
644		}).appendTo('body');
645		fakeLink[0].click();
646		fakeLink.remove();
647		$.ProradioAjaxPreloader.hide('debug 1');
648		e.stopPropagation();
649		return;
650	});
651
652
653
654	/**
655	 * ============================================================
656	 * Initialize ajax page load
657	 * ============================================================                                                                                                                                         [description]
658	 */
659	$.fn.qtAplInitAjaxPageLoad = function(){
660
661		if($('body').hasClass('proradio-is_editor') ){
662			return;
663		}
664
665		$("body").off("click", "a").on("click", 
666		  'a:not([href="#"]):not(.qtmplayer__play):not(.wc-block-components-checkout-place-order-button):not(.prsidekick-btn):not(.ajax_add_to_cart):not(.proradio-popupwindow):not(.qt-popupwindow):not(.noajax):not([target="_blank"]):not([type="submit"]):not([type="button"])' +
667		  ':not(.wpml-ls-item):not(.lang-item):not([href*="respond"]):not([href*="wp-admin"]):not([href*="mailto:"]):not([data-qttrackurl]):not([data-proradio-switch]):not(.qtmplayer-play)' +
668		  ':not([href$=".zip"]):not([href$=".jpg"]):not([href$=".jpeg"]):not([href$=".gif"]):not([href$=".mp3"]):not([href$=".pdf"]):not([href$=".png"]):not([href$=".rar"])' +
669		  ':not([href*="#noajax"]):not([href*="noajax"]):not([href*="download_file"]):not([href*="/download/"]):not([href*="replytocom"])', function(e) {
670
671			// console.log('ProRadio Ajax Click detected');
672			var that = $(this),
673				href = $(this).attr('href');
674
675
676
677			/**
678			 * ===============================================================
679			 *  Link undefined
680			 * ===============================================================
681			 */
682			if( href === undefined || href == "" || href == "#" || that.hasClass('wp-element-button')){
683				$.ProradioAjaxPreloader.hide('debug 2');
684				return e;
685			}
686
687			 /**
688			 * ===============================================================
689			 *  Link undefined
690			 * ===============================================================
691			 */
692			if( href === undefined || href == "" || that.hasClass('wp-element-button')){
693				$.ProradioAjaxPreloader.hide('debug 3');
694				return e;
695			}
696
697			
698			 /**
699			 * ===============================================================
700			 *  Skip ajax for WooCommerce endpoints
701			 * ===============================================================
702			 */
703
704			$.each( $('#qt-ajax-pageload-woocommerce-urls').data(), function(index,endpointUrl){
705				if( href === endpointUrl ){
706					$.fn.qtAplFallbackLink(false, href, 'WOOCOMMERCE');
707					return e;
708				}
709			});
710
711			/**
712			 * ===============================================================
713			 * [exceptions that will skip ajax loading]
714			 * ===============================================================
715			 */
716			var qtAjaxpatt = /(\/respond|\/wp-admin|mailto:|\.zip|\.jpg|\.jpeg|\.gif|\.mp3|\.pdf|\.png|\.rar|#noajax|noajax|download_file|\/download\/|replytocom)/;
717			if ( that.hasClass('prsidekick-btn') || that.hasClass("ajax_add_to_cart") || that.parent().hasClass("noajax") || that.parent().hasClass("lang-item") || ( !href.match(document.domain) ) || href.match('replytocom')  || that.attr("target") === '_blank' || that.hasClass("noajax") || that.attr("type") === 'submit' || that.parent().hasClass("wpml-ls-item") || that.attr("type") === 'button' || href.match(qtAjaxpatt) ) {
718				$.ProradioAjaxPreloader.hide('debug 3');
719				return e;
720			}
721
722			/**
723			 * ===============================================================
724			 * Push state browser
725			 * ===============================================================
726			 */
727			if(href.match(document.domain) ){
728				e.preventDefault();
729				try {
730					if (window.history.pushState) {
731						var pageurl = href;
732						if (pageurl !== window.location) {
733							window.history.pushState({
734							path: pageurl,
735							state:'new'
736							}, '', pageurl);
737						}
738					}
739				} catch (e) {
740					console.log (e);
741				}
742				
743				/**
744				 * Close the sidebar and player
745				 */
746				$("li.current_page_item").removeClass("current_page_item");
747				$("li.current-menu-item").removeClass("current-menu-item");
748				$('[href="'+pageurl+'"]').closest('li').addClass("current-menu-item").addClass("current_page_item");
749				$("body").removeClass("proradio-overlayopen"); // 2023 09 18
750				qtAplExecuteAjaxLink(href);
751				qtAplMaincontent.fadeTo( 100 ,0, function() {
752					 $.fn.qtAplScrollTop();
753				});
754			}
755		});
756
757
758		
759		/**
760		 * ============================================================
761		 * [ajax call]
762		 * @param  {[text]} link [url to load]
763		 * @return {[bol]}
764		 * ============================================================
765		 */
766
767		 
768		
769
770		function qtAplExecuteAjaxLink(link){
771			if (window.PRAJAX_BENCH_ENABLED) PRAjaxBenchmarkStart(link);
772
773			 PRAjaxBenchmarkMeasure("Initial http request", "start");
774			 PRAjaxBenchmarkMeasure("qtAplExecuteAjaxLink", "start");
775
776			var docClass, parser;
777
778			$.ProradioAjaxPreloader.show();
779
780			if( jQuery('#proradio-ajax-mask').hasClass( 'proradio-changeloader' ) ){
781				$.ProradioAjaxPreloader.show();
782			}
783
784			/**
785			 * ============================================================
786			 * Ajax call
787			 * ============================================================  
788			 */
789
790			var url = new URL(link);
791			var fallbackLink = url;
792			url.searchParams.set("ajax-loaded", "1"); // Add or update the parameter
793			link = url.toString();
794
795			$.ajax({
796				url: link,
797				headers: {
798				    'X-ProRadio-Ajax': '1'
799				 },
800				success:function(data) {
801					PRAjaxBenchmarkMeasure("ajax success handler", "start");
802					 PRAjaxBenchmarkMeasure("Initial http request", "stop");
803
804					$.fn.qtAplScrollTop();
805					/*
806					*   Retrive the contents
807					*/
808
809
810					 PRAjaxBenchmarkMeasure("Parse/extract", "start");
811
812
813					$.ajaxData = data;
814					parser = new DOMParser();
815					$.qtAplAjaxContents = $($.ajaxData).find(qtAplSelector).html();
816					$.qtAplAjaxTitle = $($.ajaxData).filter("title").text();
817					docClass = $($.ajaxData).filter("body").attr("class");
818					$.qtAplBodyMatches = data.match(/<body.*class=["']([^"']*)["'].*>/);
819
820					if(typeof($.qtAplBodyMatches) !== 'undefined' && $.qtAplBodyMatches !== null){
821						docClass = $.qtAplBodyMatches[1];
822					}else{
823						$.fn.qtAplFallbackLink(false, link, '002');
824						return;
825					}
826
827					/**
828					 * ============================================================
829					 * Update body classes
830					 * ============================================================     
831					 */
832					var modifiedAjaxResult = data.replace(/<body/i,'<div id="re_body"').replace(/<\/body/i,'</div'),
833						bodyClassesNew = $(modifiedAjaxResult).filter("#re_body").attr("class"),
834						js_composer_front_css= $(modifiedAjaxResult).filter('#js_composer_front-inline-css').text();
835					if(bodyClassesNew){
836						docClass = bodyClassesNew;
837					}
838
839					/**
840					 * ============================================================
841					 * @since 2.2 checkbox skip 
842					 * ============================================================ 
843					 */
844					if(  bodyClassesNew.indexOf("proradio-ajax-skip") > 0 ){
845						$.fn.qtAplFallbackLink(false, link, '003');
846						return;
847					}
848
849					$.wpadminbar = $($.ajaxData).filter("#wpadminbar").html(); 
850					$.visual_composer_styles = $($.ajaxData).filter('style[data-type=vc_shortcodes-custom-css]').text();					
851					
852					/**
853					 * [if we have WPML plugin language selector]
854					 */
855					if($("#qwLLT")){
856						$.langswitcher = $($.ajaxData).find("#qwLLT").html(); 
857					}
858
859
860					PRAjaxBenchmarkMeasure("Parse/extract", "stop");
861
862					
863					/**
864					 * ============================================================
865					 * Start putting the data in the page    
866					 * ============================================================ 
867					 */
868					if(docClass !== undefined && $.qtAplAjaxContents !== undefined){
869
870
871						 PRAjaxBenchmarkMeasure("Classes change", "start");
872
873						$("body").attr("class",docClass);
874						$("title").text($.qtAplAjaxTitle);
875						$("#wpadminbar").html($.wpadminbar);
876						$("#qwLLT").html($.langswitcher);
877						if($("#proradio-breadcrumb")){
878							$("#proradio-breadcrumb").html( $($.ajaxData).find("#proradio-breadcrumb").html() ); 
879						}
880						if($("style[data-type=vc_shortcodes-custom-css]").length > 0){
881							$("style[data-type=vc_shortcodes-custom-css]").append($.visual_composer_styles);
882						} else {
883							$("head").append('<style type="text/css"  data-type="vc_shortcodes-custom-css">'+$.visual_composer_styles+'</style>');
884						}
885
886						PRAjaxBenchmarkMeasure("Classes change", "stop");
887
888						// 2019 may 27 js composer update css
889						PRAjaxBenchmarkMeasure("JS files", "start");
890						if( js_composer_front_css != '' && js_composer_front_css != false && js_composer_front_css != undefined ){
891							if($("style#js_composer_front-inline-css").length > 0){
892								$("style#js_composer_front-inline-css").html(js_composer_front_css);
893							} else {
894								$("head").append('<style id="js_composer_front-inline-css">'+js_composer_front_css+'</style>');
895							}
896						} else {
897							$("head style#js_composer_front-inline-css").remove();
898						}
899
900						PRAjaxBenchmarkMeasure("JS files", "stop");
901						
902
903						qtAplMaincontent.html( $.qtAplAjaxContents ).delay(500).promise().done(function(){
904
905
906							// ---- BENCHMARK IMAGES
907						    window.__prapl_images_promise = null;
908
909						   	if (window.PRAJAX_BENCH_ENABLED) {
910							  PRAjaxBenchmarkMeasure("Images settled", "start");
911
912							  window.__prapl_images_promise = praplWaitImagesSettledDetailed(qtAplMaincontent, 3000, "PRAPL Images detailed").then(function () {
913							    PRAjaxBenchmarkMeasure("Images settled", "stop");
914							  });
915							}
916						    // ---- END BENCHMARK IMAGES ----
917
918							PRAjaxBenchmarkMeasure("Scripts eval", "start");
919							var scripts = qtAplMaincontent.find("script");
920							if(scripts.length > 0){
921								scripts.each(function(){
922									var code = $(this).html();
923									code = '('+code+')'; // not really needed
924									try{
925										eval($(this).html());
926									} catch(e){
927										console.log(e);
928									}
929								});	
930							}
931							PRAjaxBenchmarkMeasure("Scripts eval", "stop");
932
933							if(true === $.ProRadioMainObj.fn.initializeAfterAjax( true )){
934								qtAplMaincontent.fadeTo( 40 ,1).promise().done(function(){
935									// After reloading we scroll till the place of the anch
935or
936									// Since 2019 04 18 + support internal links
937									var link_array = link.split('#');
938									if( link_array.length > 1){
939										var targetDiv = $('#'+link_array[1] );
940										// console.log( 'ProRadio Ajax - Internal Link reload animation: link ' + link_array[1] );
941										if( targetDiv.length > 0 ){
942											var point = targetDiv.offset().top;
943											// console.log('Scroll To  V2');
944											$('html, body').stop(true, true).animate({scrollTop: point },
945												80
946											  );
947											 return;
948										}
949									}
950								});
951
952								/**
953								 * @since  2.4
954								 * Execute custom javascript
955								 */
956								PRAjaxBenchmarkMeasure("Ajax custom script", "start");
957								$.getScript(  $('#qt-ajax-customscript-url').data('customscripturl') ) 
958									.done(function( script, textStatus ) {}) 
959									.fail(function( jqxhr, settings, exception ) {});
960								PRAjaxBenchmarkMeasure("Ajax custom script", "stop");
961								
962						
963
964
965								/**
966								 * @since  3.0
967								 * Load all Elementor styles
968								 */
969								
970								PRAjaxBenchmarkMeasure("Remove old css", "start");
971								$('link[rel="stylesheet"][data-loaded="prapl-loaded"]').each(function() {
972									// console.log('Style removed');
973									$(this).remove();
974								});
975								PRAjaxBenchmarkMeasure("Remove old css", "stop");
976
977
978								
979
980								// Extract remote styles 
981								// @version2026 02 02
982
983								PRAjaxBenchmarkMeasure("Add new CSS ajax", "start");
984								// $.targetStyles = $($.ajaxData).filter("link[rel='stylesheet']");
985
986								// Fix missing elementor styles
987								var doc = new DOMParser().parseFromString($.ajaxData, "text/html");
988								$.targetStyles = jQuery(doc).find("link[rel='stylesheet']");
989
990
991
992								var canDisplayContent = true;
993
994								if ($.targetStyles.length) {
995									canDisplayContent = false;
996
997									var appended = 0;
998									var doneCount = 0;
999									var MAX_WAIT_MS = 8000; // per stylesheet
1000									var finalizeCalled = false;
1001
1002									var finalize = function () {
1003										PRAjaxBenchmarkMeasure("Add new CSS ajax", "stop");
1004										if (finalizeCalled) return;
1005										finalizeCalled = true;
1006										canDisplayContent = true;
1007										// continue flow here
1008									};
1009
1010									var markDone = function () {
1011										doneCount++;
1012										if (doneCount >= appended) finalize();
1013									};
1014
1015									var normalizeUrlNoQuery = function (url) {
1016										if (!url) return "";
1017										try {
1018											return new URL(url, window.location.href).href.split("#")[0].split("?")[0];
1019										} catch (e) {
1020											return "";
1021										}
1022									};
1023
1024									var getExistingStylesheetByNormalizedUrl = function (normalizedUrl) {
1025										if (!normalizedUrl) return null;
1026										var found = null;
1027										$('link[rel="stylesheet"]').each(function () {
1028											var current = normalizeUrlNoQuery(this.href || "");
1029											if (current === normalizedUrl) {
1030												found = this;
1031												return false;
1032											}
1033										});
1034										return found;
1035									};
1036
1037									var insertBeforeThemeCss = function (link) {
1038										var anchor =
1039											document.getElementById('proradio-style-css') ||
1040											document.getElementById('proradio-child-style-css') ||
1041											document.getElementById('proradio-business-tools-css');
1042
1043										if (anchor && anchor.parentNode) {
1044											anchor.parentNode.insertBefore(link, anchor);
1045										} else {
1046											document.head.appendChild(link);
1047										}
1048									};
1049
1050									$.each($.targetStyles, function (_i, item) {
1051										var $item = $(item);
1052										var cssurl = ($item.attr("href") || "").trim();
1053										if (!cssurl) return;
1054
1055										var absUrl;
1056										try {
1057											absUrl = new URL(cssurl, window.location.href).href;
1058										} catch (e) {
1059											return;
1060										}
1061
1062										var normalizedUrl = normalizeUrlNoQuery(absUrl);
1063										if (!normalizedUrl) return;
1064
1065										var fileName = normalizedUrl.split('/').pop() || '';
1066										var id = ($item.attr("id") || "").trim();
1067
1068										var isElementorCss = (normalizedUrl.indexOf('/uploads/elementor/css/') !== -1);
1069										var isElementorGlobalCustomFrontend = (fileName === 'custom-frontend.min.css');
1070
1071										// 1) GLOBAL Elementor frontend CSS:
1072										//    - must exist only once
1073										//    - must NOT be appended after theme CSS
1074										//    - if missing, insert before theme CSS
1075										if (isElementorGlobalCustomFrontend) {
1076											if (getExistingStylesheetByNormalizedUrl(normalizedUrl)) {
1077												return;
1078											}
1079
1080											appended++;
1081
1082											var globalLink = document.createElement("link");
1083											globalLink.rel = "stylesheet";
1084											globalLink.href = absUrl;
1085											globalLink.media = $item.attr("media") || "all";
1086											globalLink.setAttribute("data-loaded", "prapl-loaded");
1087											if (id) globalLink.id = id;
1088
1089											var globalDoneOnce = false;
1090											var globalTimeout = setTimeout(function () {
1091												if (globalDoneOnce) return;
1092												globalDoneOnce = true;
1093												markDone();
1094											}, MAX_WAIT_MS);
1095
1096											var globalDone = function () {
1097												if (globalDoneOnce) return;
1098												globalDoneOnce = true;
1099												clearTimeout(globalTimeout);
1100												markDone();
1101											};
1102
1103											globalLink.onload = globalDone;
1104											globalLink.onerror = globalDone;
1105
1106											insertBeforeThemeCss(globalLink);
1107											return;
1108										}
1109
1110										// 2) Non-Elementor CSS:
1111										//    if already present, skip
1112										if (!isElementorCss) {
1113											if (getExistingStylesheetByNormalizedUrl(normalizedUrl)) {
1114												return;
1115											}
1116										}
1117
1118										// 3) Page-specific Elementor CSS:
1119										//    if already present, replace IN PLACE instead of removing + appending last
1120										var existingElementorNode = null;
1121
1122										if (isElementorCss) {
1123											if (id) {
1124												var byId = document.getElementById(id);
1125												if (byId && normalizeUrlNoQuery(byId.href || "") === normalizedUrl) {
1126													existingElementorNode = byId;
1127												}
1128											}
1129
1130											if (!existingElementorNode) {
1131												existingElementorNode = getExistingStylesheetByNormalizedUrl(normalizedUrl);
1132											}
1133										}
1134
1135										appended++;
1136
1137										var link = document.createElement("link");
1138										link.rel = "stylesheet";
1139										link.href = absUrl;
1140										link.media = $item.attr("media") || "all";
1141										link.setAttribute("data-loaded", "prapl-loaded");
1142										if (id) link.id = id;
1143
1144										// If this is Elementor page CSS already present, replace it in place
1145										if (isElementorCss && existingElementorNode && existingElementorNode.parentNode) {
1146											var replaceDoneOnce = false;
1147											var oldNode = existingElementorNode;
1148
1149											var replaceTimeout = setTimeout(function () {
1150												if (replaceDoneOnce) return;
1151												replaceDoneOnce = true;
1152
1153												try {
1154													if (oldNode && oldNode.parentNode) {
1155														oldNode.parentNode.removeChild(oldNode);
1156													}
1157												} catch (e) {}
1158
1159												markDone();
1160											}, MAX_WAIT_MS);
1161
1162											var replaceDone = function () {
1163												if (replaceDoneOnce) return;
1164												replaceDoneOnce = true;
1165												clearTimeout(replaceTimeout);
1166
1167												try {
1168													if (oldNode && oldNode.parentNode) {
1169														oldNode.parentNode.removeChild(oldNode);
1170													}
1171												} catch (e) {}
1172
1173												markDone();
1174											};
1175
1176											link.onload = replaceDone;
1177											link.onerror = replaceDone;
1178
1179											oldNode.parentNode.insertBefore(link, oldNode);
1180											return;
1181										}
1182
1183										// Default append for truly missing stylesheets
1184										var doneOnce = false;
1185										var t = setTimeout(function () {
1186											if (doneOnce) return;
1187											doneOnce = true;
1188											markDone();
1189										}, MAX_WAIT_MS);
1190
1191										var done = function () {
1192											if (doneOnce) return;
1193											doneOnce = true;
1194											clearTimeout(t);
1195											markDone();
1196										};
1197
1198										link.onload = done;
1199										link.onerror = done;
1200
1201										document.head.appendChild(link);
1202									});
1203
1204									// If nothing was appended, don't block
1205									if (appended === 0) finalize();
1206								}
1207
1208
1209								// Reinitialize elementor scripts
1210								if (typeof window.elementorFrontend === 'object') {
1211									elementorFrontend.init();
1212								}
1213
1214								/**
1215								 * @since  2.4
1216								 * Reload woocommerce scripts
1217								 */
1218								PRAjaxBenchmarkMeasure("WooCommerce scripts", "start");
1219								$.each( $('#qt-ajax-pageload-woocommerce-scripts').data(), function(index,scriptUrl){
1220									$.getScript(scriptUrl);
1221								});
1222								PRAjaxBenchmarkMeasure("WooCommerce scripts", "stop");
1223
1224								PRAjaxBenchmarkMeasure("Radio Feed data", "start");
1225								if (typeof $.qtmplayerRadioFeedObj === 'object') {
1226									if (typeof $.qtPlayerObj.songdata !== 'undefined') {
1227										$.qtmplayerRadioFeedObj.fn.pushFeed( $.qtPlayerObj.songdata );
1228									}
1229								}
1230								PRAjaxBenchmarkMeasure("Radio Feed data", "stop");
1231
1232								PRAjaxBenchmarkMeasure("QTags", "start");
1233								if('function' === typeof(QTags)){
1234									$.tinyMceItems = qtAplMaincontent.find("#bbp_reply_content");
1235									if( $.tinyMceItems.length > 0){
1236										var	qt_editor = new QTags( { 'id': 'bbp_reply_content' } );
1237									} 
1238									$.tinyMceItems = qtAplMaincontent.find("#bbp_topic_content");
1239									if( $.tinyMceItems.length > 0){
1240										var	qt_editor = new QTags( { 'id': 'bbp_topic_content' } );
1241									}
1242									QTags._buttonsInit();
1243								}
1244								PRAjaxBenchmarkMeasure("QTags", "stop");
1245								PRAjaxBenchmarkMeasure("ttgReaktionsInit", "start");
1246								if(typeof( $.fn.ttgReaktionsInit ) === 'function' ){
1247									// console.log('ReAktions Reinit');
1248									$.fn.ttgReaktionsInit();
1249								}
1250								PRAjaxBenchmarkMeasure("ttgReaktionsInit", "stop");
1251
1252								// Reinitialize Business Tools
1253								PRAjaxBenchmarkMeasure("Business Tools reinit", "start");
1254								if(typeof( $.ProRadioBtoolsObj ) === 'object' ){
1255									$.ProRadioBtoolsObj.fn.injectAds();	
1256								}
1257								PRAjaxBenchmarkMeasure("Business Tools reinit", "stop");
1258
1259								/**
1260								 * ============================================================
1261								 * Trigger global end of loading, used by other plugins    
1262								 * ============================================================                                                                                                                                         [description]
1263								 */
1264								$(window).trigger('qtAjaxpageLoadEnd'); 
1265								window.dispatchEvent(new Event('resize'));
1266
1267								/**
1268								 * ============================================================
1269								 * All done! If images are all loaded, show the content        
1270								 * ============================================================                                                                                                                                         [description]
1271								 */
1272								PRAjaxBenchmarkMeasure("End transition", "start");
1273								setTimeout(function(){
1274									$.ProradioAjaxPreloader.hide(' - Loading ended - ');
1275									
1276									PRAjaxBenchmarkMeasure("ajax success handler", "stop");
1277									// PRAjaxBenchmarkReport();
1278									var p = window.__prapl_images_promise;
1279									if (p && typeof p.then === "function") {
1280									  p.finally(function () {
1281									    PRAjaxBenchmarkReport();
1282									    window.__prapl_images_promise = null;
1283									  });
1284									} else {
1285									  PRAjaxBenchmarkReport();
1286									}
1287
1288									// Fix Elementor lazy "background-image:none" on our gradient caption widget
1289									document.querySelectorAll('.elementor-widget-proradio-elementor-gradient-caption').forEach(function (w) {
1290										var p = w.closest('.e-con.e-parent');
1291										if (p) p.classList.add('e-lazyloaded');
1292									});
1293
1294
1295									window.proradio_after_ajax_dom_swap(jQuery('body')[0]);
1296									console.log('dispatch resize event');
1297									window.dispatchEvent(new Event('resize'));
1298									
1299								},400);
1300
1301
1302								PRAjaxBenchmarkMeasure("End transition", "stop");
1303								
1304
1305
1306							}else{
1307								$.fn.qtAplFallbackLink(false, link, '004');
1308								return;
1309							}
1310						});   
1311						
1312
1313					}else{
1314						$.fn.qtAplFallbackLink(false, link, '005');
1315						return;
1316					}				},
1317				error: function () {
1318					//Go to the link normally
1319					$.fn.qtAplFallbackLink(false, link, '006');
1320					return;
1321				}
1322			});
1323			PRAjaxBenchmarkMeasure("qtAplExecuteAjaxLink", "stop");
1324
1325
1326			return true;
1327		}
1328		/**
1329		 * ============================================================
1330		 * Manage browser back and forward arrows
1331		 * ============================================================
1332		 */
1333		$(window).on("popstate", function(e) {
1334			var href;
1335			if (e.originalEvent.state !== null) {
1336				href = location.href;
1337				if(href !== undefined){
1338					if (!href.match(document.domain))    {
1339						$.fn.qtAplFallbackLink(false, link, '006');
1340						return;
1341					} else {
1342						qtAplMaincontent.fadeTo( 120 ,0, function() {
1343							$.fn.qtAplScrollTop();
1344						}).promise().done(function(){
1345							qtAplExecuteAjaxLink(href);
1346						});
1347					}
1348				}
1349			} else {
1350				href = location.href;
1351				if(href !== undefined){
1352
1353					if (!href.match(document.domain)){
1354						$.fn.qtAplFallbackLink(false, href, '007');
1355						return;
1356					} else {
1357
1358						// Since 2019 04 18 + support internal links
1359						var pageURL = $(location).attr("href"),
1360							pageURL_array = pageURL.split('#'),
1361							pageURL_naked = pageURL_array[0],
1362							href_array = href.split('#'),
1363							href_naked = href_array[0];
1364						if(href_naked === pageURL_naked) {
1365							if( undefined !== href_array[1]){
1366								var targetDiv = $('#'+href_array[1] );
1367
1368								if( targetDiv.length > 0 ){
1369
1370									// console.log( 'ProRadio Ajax - Browser arrow animation: link ' + link_array[1] );
1371									var point = targetDiv.offset().top;
1372									e.preventDefault();
1373									// console.log('Scroll To  V3 - post elaboration');
1374									$('html, body').stop(true, true).animate(
1375										{
1376										  scrollTop: point,
1377										},
1378										400
1379									  );
1380									$.ProradioAjaxPreloader.hide('debug 5');
1381									return false;
1382								} else {
1383									$.ProradioAjaxPreloader.hide('debug 6');
1384									return e;
1385								}
1386							}
1387						}
1388						qtAplMaincontent.fadeTo( 120 ,0, function() {
1389							$.fn.qtAplScrollTop();
1390						}).promise().done(function(){
1391							qtAplExecuteAjaxLink(href);
1392						});
1393					}
1394								
1395				}
1396			}
1397		});
1398	}; // $.fn.qtAplInitAjaxPageLoad
1399
1400	/**====================================================================
1401	 *
1402	 *	Page Ready Trigger
1403	 * 	This needs to call only $.fn.qtInitTheme
1404	 * 
1405	 ====================================================================*/
1406	jQuery(document).ready(function() {
1407
1408		if( $('body').hasClass('proradio-ajax-pageload') ){
1409			// console.log('%c Pro.Radio Ajax - Installed and active ', 'background: #030303; color: #bada55');
1410			$.fn.qtAplInitAjaxPageLoad();		
1411			$.ProradioAjaxPreloader.init();
1412		} else {
1413			console.error('%c Pro.Radio Ajax - Not installed or outdated plugin ', 'background: #ff0000; color: #ffffff');
1414			console.log('To fix, follow the instructions at https://shop.pro.radio/knowledgebase/11/2.1-Updating-the-theme-and-the-plugins.html');
1415		}
1416
1417	});
1418
1419})(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.