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.