PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.18
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.18
4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.0.30 4.0.29 4.0.28 4.0.27 4.0.26 4.0.24 4.0.25 4.0.23 4.0.22 4.0.21 4.0.19 4.0.18 4.0.17 4.0.16 1.9.3 All 173 releases
searchpro / inc / photon / class-berqPageOptimizer.php

class-berqPageOptimizer.php in BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript 4.1.18, at inc/photon/class-berqPageOptimizer.php

1,575 lines 57.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit;
3
4 use BerqWP\Cache;
5 use BerqWP_Deps\voku\helper\HtmlDomParser;
6
7 class berqPageOptimizer {
8 public $page_slug = null;
9 public $page_url = null;
10 public $settings = null;
11 public $early_head_html = '';
12
13 function __construct() {
14
15 if ($this->settings === null) {
16 $this->settings = berqwp_get_page_params(home_url());
17 }
18
19 add_filter('berqwp_photon_before_closing_body', [$this, 'img_lazy_load_js']);
20 add_filter('berqwp_photon_before_closing_body', [$this, 'video_lazy_load_js']);
21 add_filter('berqwp_photon_before_closing_body', [$this, 'iframe_lazy_load_js']);
22 add_filter('berqwp_photon_before_closing_body', [$this, 'dynamic_js_loading_script']);
23 add_filter('berqwp_photon_before_closing_body', [$this, 'dynamic_css_loading_script']);
24 add_filter('berqwp_photon_before_closing_body', [$this, 'prerender_on_hover']);
25
26 }
27
28 function start_cache() {
29 add_action('template_redirect', [$this, 'buffer_start'], 2);
30 }
31
32 function set_slug($slug) {
33 $this->page_slug = $slug;
34 }
35
36 function set_page($page_url) {
37 $this->page_url = $page_url;
38 }
39
40
41 function buffer_start() {
42 ob_start([$this, 'buffer_end']);
43 }
44
45 function prerender_on_hover($script) {
46
47 if ($this->settings['prerender_link']) {
48 $script .= "
49 <script id='prefetch-links' defer>
50
51 function berq_prerender(url) {
52 const s = document.createElement('script');
53 s.type = 'speculationrules';
54 s.textContent = JSON.stringify({
55 prerender: [{ urls: [url] }]
56 });
57 document.head.appendChild(s);
58 }
59
60 // Set to keep track of prefetched links
61 const prefetchedLinks = new Set();
62
63 // Get all anchor tags on the page
64 const links = document.querySelectorAll('a:not([data-price-key])');
65
66 // Loop through each anchor tag
67 links.forEach(link => {
68 // Add mouseover event listener
69 link.addEventListener('mouseover', () => {
70 const excludes = ['tel:', 'mailto:', 'sms:', 'geo:'];
71
72 const isExcluded = excludes.some(prefix =>
73 link.href.startsWith(prefix)
74 );
75
76 // Check if the link has already been prefetched
77 if (!prefetchedLinks.has(link.href) && !isExcluded) {
78 berq_prerender(link.href);
79
80 // Add the link to the set of prefetched links
81 prefetchedLinks.add(link.href);
82 }
83 });
84 });
85
86 </script>";
87 }
88
89 return $script;
90 }
91
92 function video_lazy_load_js($script) {
93
94 if ($this->settings['lazy_load_videos']) {
95
96 $script .= "
97 <script async>
98 (function(){
99 // window.addEventListener('load', function() {
100 window.addEventListener('DOMContentLoaded', function() {
101 // Function to load video and its sources
102 function loadLazyVideo(video) {
103 // Set video src from data attribute if exists
104 const videoSrc = video.getAttribute('data-berqwpsrc');
105 if (videoSrc) {
106 video.setAttribute('src', videoSrc);
107 }
108
109 // Find all <source> tags inside the video and apply their data-berqwpsrc as src
110 const sources = video.querySelectorAll('source');
111 sources.forEach(source => {
112 const sourceSrc = source.getAttribute('data-berqwpsrc');
113 if (sourceSrc) {
114 source.setAttribute('src', sourceSrc);
115 }
116 });
117
118 // Load the video once the sources have been set
119 video.load();
120 }
121
122 // Use IntersectionObserver to detect when the video is about to enter the viewport
123 let lazyVideos = [].slice.call(document.querySelectorAll('video.berqwp-lazy-video'));
124
125 if ('IntersectionObserver' in window) {
126 let lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
127 entries.forEach(function(entry) {
128 if (entry.isIntersecting) {
129 let video = entry.target;
130 loadLazyVideo(video);
131 // Unobserve the video once it's loaded
132 lazyVideoObserver.unobserve(video);
133 }
134 });
135 });
136
137 // Observe each lazy video
138 lazyVideos.forEach(function(video) {
139 lazyVideoObserver.observe(video);
140 });
141 } else {
142 // Fallback for browsers that don't support IntersectionObserver
143 lazyVideos.forEach(function(video) {
144 loadLazyVideo(video);
145 });
146 }
147 });
148
149 })()
150 </script>
151 ";
152 }
153
154 return $script;
155 }
156
157 function iframe_lazy_load_js($script) {
158
159 if ($this->settings['youtube_lazyloading']) {
160
161 $script .= "
162 <script defer>
163 // document.addEventListener('DOMContentLoaded', function () {
164
165 (function(){
166
167 var options = {
168 root: null, // null means the viewport
169 rootMargin: '0px', // adjust as needed
170 threshold: 0.1 // adjust as needed
171 };
172
173 var bwp_iframe_observer = new IntersectionObserver(function (entries, observer) {
174 entries.forEach(function (entry) {
175 if (entry.isIntersecting) {
176 let item = entry.target;
177 let iframe = item.getAttribute('data-embed');
178
179 let wrapper = document.createElement('div');
180 wrapper.innerHTML = iframe;
181 let iframeElement = wrapper.firstChild;
182
183 // Insert the iframe next to the item
184 item.insertAdjacentElement('afterend', iframeElement);
185
186 // Remove the original item
187 item.remove();
188
189 bwp_iframe_observer.unobserve(item);
190 }
191 });
192 }, options);
193
194 let yt_em = document.querySelectorAll('.berqwp-lazy-youtube');
195 yt_em.forEach(function (item) {
196 bwp_iframe_observer.observe(item);
197 });
198
199 })()
200 // });
201 </script>
202 ";
203 }
204
205 return $script;
206 }
207
208 static function url_to_path($url)
209 {
210
211 if (strpos($url, '?') !== false) {
212 $url = explode('?', $url)[0];
213 }
214
215 if (strpos($url, '#') !== false) {
216 $url = explode('#', $url)[0];
217 }
218
219 // Handle relative URLs
220 if (strpos($url, '//') === 0) {
221 return realpath(ABSPATH . ltrim($url, '/'));
222 }
223
224 $content_url = content_url();
225 $content_dir = WP_CONTENT_DIR;
226
227 if (strpos($url, $content_url) === 0) {
228 // var_dump(realpath(
229 // str_replace($content_url, $content_dir, $url)
230 // ));
231 return str_replace($content_url, $content_dir, $url);
232 }
233
234 // Fallback for site root files
235 $site_url = get_site_url('/');
236
237 if (strpos($url, $site_url) === 0) {
238 return realpath(
239 str_replace($site_url, ABSPATH, $url)
240 );
241 }
242
243 return false;
244 }
245
246 function img_lazy_load_js($script) {
247
248 if ($this->settings['img_lazyloading']) {
249
250 $script .= "
251 <script async>
252 (function(){
253
254 document.addEventListener('DOMContentLoaded', function () {
255 var berq_img_lazy_options = {
256 root: null, // null means the viewport
257 rootMargin: '200px',
258 threshold: 0
259 };
260
261 var img_observer = new IntersectionObserver(function (entries, observer) {
262 entries.forEach(function (entry) {
263 if (entry.isIntersecting) {
264 let img = entry.target;
265 let imgSrc = img.getAttribute('data-berqwpsrc');
266 let imgSrcset = img.getAttribute('data-berqwp-srcset');
267
268 // Set the actual image source from data-berqwpsrc
269 if (imgSrc !== null) {
270 img.src = imgSrc;
271 }
272
273 if (imgSrcset !== null) {
274 img.srcset = imgSrcset;
275 }
276
277 if (img.getAttribute('data-srcset') !== null) {
278 img.srcset = img.getAttribute('data-srcset');
279 }
280
281 // You might want to remove the data-src attribute after loading
282 img.removeAttribute('data-berqwpsrc');
283 img.removeAttribute('data-berqwp-srcset');
284
285 img_observer.unobserve(img);
286 }
287 });
288 }, berq_img_lazy_options);
289
290 function berqwp_lazyload_images() {
291 let lazyImages = document.querySelectorAll('img[data-berqwpsrc]');
292 lazy_img_int = 1000;
293
294 lazyImages.forEach(function (img) {
295 img_observer.observe(img);
296 });
297 }
298
299 function berqwp_lazyload_source() {
300 let lazyImages = document.querySelectorAll('source[data-berqwp-srcset]');
301 lazy_img_int = 1000;
302
303 lazyImages.forEach(function (img) {
304 img_observer.observe(img);
305 });
306 }
307
308 berqwp_lazyload_images();
309 setInterval(berqwp_lazyload_images, 1000);
310 setInterval(berqwp_lazyload_source, 1000);
311
312 });
313
314 })()
315 </script>
316 ";
317 }
318
319 return $script;
320 }
321
322 function dynamic_css_loading_script($script) {
323
324 if ($this->settings['css_optimization'] == 'asynchronous' || $this->settings['css_optimization'] == 'delay') {
325
326 $script .= "
327 <script defer>
328 function berqwp_init_css() {
329 // Get all link tags containing data-berqwp-style-href
330 let berqwp_linkTags = document.querySelectorAll('link[data-berqwp-style-href]');
331
332 // Iterate through each link tag
333 berqwp_linkTags.forEach(function (linkTag, index) {
334 // Set the href attribute of each link tag
335 linkTag.setAttribute('href', linkTag.getAttribute('data-berqwp-style-href'));
336
337 });
338
339 document
340 .querySelectorAll('style[type=\"text/berqwp-style\"]')
341 .forEach(style => {
342 style.type = 'text/css';
343 });
344
345
346 }
347 </script>
348 ";
349
350 if ($this->settings['css_optimization'] == 'asynchronous') {
351 $script .= "
352 <script defer>
353 requestIdleCallback(() => {
354 berqwp_init_css();
355 });
356 </script>
357 ";
358 }
359
360 if ($this->settings['css_optimization'] == 'delay') {
361 $script .= "
362 <script defer>
363 (function() {
364
365 function berqwp_css_handleUserInteraction(event) {
366
367
368 berqwp_init_css();
369
370
371 // After running the function, remove all event listeners to ensure it runs only once
372 for (let eventType of berqwp_js_interactionEventTypes) {
373 window.removeEventListener(eventType, berqwp_css_handleUserInteraction);
374 }
375 }
376
377 let berqwp_js_interactionEventTypes = ['click', 'mousemove', 'keydown', 'touchstart', 'scroll', 'berqwpLoadJS', 'berqwp_interaction_event'];
378
379 for (let eventType of berqwp_js_interactionEventTypes) {
380 window.addEventListener(eventType, berqwp_css_handleUserInteraction, { passive: false });
381 }
382
383 })()
384 </script>
385 ";
386 }
387 }
388
389 return $script;
390 }
391
392 function dynamic_js_loading_script($script) {
393
394 if ($this->settings['js_optimization'] == 'asynchronous' || $this->settings['js_optimization'] == 'delay') {
395
396 $script .= "
397 <script id='berqwp-preloader' defer>
398 class berqwpPreloader {
399 jobId = 0;
400 pending = new Map();
401
402 _preloadAssets(assets, asType) {
403 const seen = new Set();
404 const unique = assets.filter(({ url }) => {
405 if (seen.has(url)) return false;
406 seen.add(url);
407 return true;
408 });
409
410 const id = ++this.jobId;
411
412 return new Promise((resolve) => {
413 if (unique.length === 0) {
414 console.log('[berqwp] empty — resolving immediately'); // Is it bailing here?
415 resolve({ status: 'done', failed: [] });
416 return;
417 }
418
419 this.pending.set(id, resolve);
420
421 let completed = 0;
422 const failed = [];
423
424 const onSettle = () => {
425 completed++;
426 if (completed === unique.length) {
427 this.pending.delete(id);
428 resolve({ status: 'done', failed });
429 }
430 };
431
432 unique.forEach(({ url, crossOrigin }) => {
433 const alreadyPreloaded = document.querySelector(`link[rel=\"preload\"][as=\"\${asType}\"][href=\"\${url}\"]`);
434 const alreadyScript = asType === 'script' && document.querySelector(`script[src=\"\${url}\"]:not([type=\"text/bwp-script\"])`);
435 const alreadyStyle = asType === 'style' && document.querySelector(`link[rel=\"stylesheet\"][href=\"\${url}\"]`);
436
437 if (alreadyPreloaded || alreadyScript || alreadyStyle) {
438 return onSettle();
439 }
440
441 const link = document.createElement('link');
442 link.rel = 'preload';
443 link.as = asType;
444 link.href = url;
445
446 if (crossOrigin) {
447 link.crossOrigin = crossOrigin;
448 }
449
450 link.onload = () => {
451 console.log('[berqwp] preloaded:', url); // Is onload firing?
452 onSettle();
453 };
454 link.onerror = () => {
455 console.warn('[berqwp] failed:', url); // Or is it erroring?
456 failed.push(url);
457 onSettle();
458 };
459
460 document.head.appendChild(link);
461 });
462 });
463 }
464
465 preload(assets) {
466 const normalized = assets.map(a =>
467 typeof a === 'string' ? { url: a, crossOrigin: null } : a
468 );
469 return this._preloadAssets(normalized, 'script');
470 }
471
472 preloadScripts() {
473 const elements = [...document.querySelectorAll('script[type=\"text/bwp-script\"]')];
474 const assets = elements
475 .filter(el => el.getAttribute('src'))
476 .map(el => ({
477 url: el.getAttribute('src'),
478 crossOrigin: el.getAttribute('crossorigin') || null
479 }));
480
481 return this._preloadAssets(assets, 'script');
482 }
483
484 preloadStyles() {
485 const elements = [...document.querySelectorAll('link[data-berqwp-style-href]')];
486 const assets = elements
487 .filter(el => el.getAttribute('data-berqwp-style-href'))
488 .map(el => ({
489 url: el.getAttribute('data-berqwp-style-href'),
490 crossOrigin: el.getAttribute('crossorigin') || null
491 }));
492
493 return this._preloadAssets(assets, 'style');
494 }
495
496 }
497
498 (function() {
499 window.bwpPreloader = new berqwpPreloader();
500 })()
501 </script>
502 <script defer>
503 (function(){
504 // Select all inline script tags with type=\"text/bwp-script\"
505 const inlineScripts = document.querySelectorAll('script[type=\"text/bwp-script\"]');
506
507 inlineScripts.forEach((script) => {
508 // Get the content of the inline script
509 const scriptContent = script.innerHTML;
510
511 if (!scriptContent) {
512 return;
513 }
514
515 if (script.getAttribute('data-type') == 'module') {
516 return;
517 }
518
519 // Create a Blob from the script content
520 const blob = new Blob([scriptContent], { type: 'application/javascript' });
521
522 // Create a URL for the Blob
523 const scriptURL = URL.createObjectURL(blob);
524
525 // Create a new external script tag
526 const newScript = document.createElement('script');
527 newScript.src = scriptURL;
528 newScript.type = 'text/bwp-script'; // Or 'text/bwp-script', but 'application/javascript' is more typical for JS
529 newScript.setAttribute('data-type', script.getAttribute('data-type'));
530
531 // Copy other attributes if necessary
532 Array.from(script.attributes).forEach(function(attr) {
533 if (attr.name !== 'type' && attr.name !== 'src') {
534 newScript.setAttribute(attr.name, attr.value);
535 }
536 });
537
538 // Replace the original inline script with the new external script tag
539 script.parentNode.replaceChild(newScript, script);
540 });
541
542 })();
543
544 </script>
545 <script data-mode='4' defer>
546 var berq_click = null;
547 function berqwpLoadJs() {
548
549 (function () {
550 'use strict';
551 const VERSION = '3.0';
552 let activated = false;
553 const pendingScripts = new Map();
554 let completedCount = 0;
555
556 // Custom Event System
557 const createTrackerEvent = (name, detail = {}) =>
558 new CustomEvent(`bwp:\${name}`, {
559 detail: {
560 version: VERSION,
561 timestamp: performance.now(),
562 ...detail
563 }
564 });
565
566 // Event Polyfills
567 (function () {
568 const originalAddEventListener = EventTarget.prototype.addEventListener;
569 const readyState = document.readyState;
570 const simulatedEvents = new WeakMap();
571
572 function createEvent(type) {
573 const event = new Event(type, { bubbles: false, cancelable: false });
574 event.isSimulated = true;
575 return event;
576 }
577
578 EventTarget.prototype.addEventListener = function (type, listener, options) {
579
580 if (typeof listener === 'function') {
581 if (type === 'DOMContentLoaded') {
582 if (readyState !== 'loading') {
583 setTimeout(() => listener.call(this, createEvent(type)), 0);
584 return;
585 }
586 } else if ((this === window || this instanceof HTMLScriptElement) && type === 'load') {
587 if (readyState === 'complete') {
588 setTimeout(() => listener.call(this, createEvent(type)), 0);
589 return;
590 }
591 }
592 }
593
594
595 originalAddEventListener.call(this, type, listener, options);
596 };
597
598 ['onDOMContentLoaded', 'onload'].forEach(prop => {
599 const target = prop === 'onload' ? window : document;
600 const descriptor = Object.getOwnPropertyDescriptor(target.constructor.prototype, prop.toLowerCase()) || {};
601
602 Object.defineProperty(target, prop, {
603 set(fn) {
604 if (typeof fn === 'function') {
605 const eventType = prop.replace('on', '').toLowerCase();
606 if ((eventType === 'domcontentloaded' && readyState !== 'loading') ||
607 (eventType === 'load' && readyState === 'complete')) {
608 setTimeout(() => fn.call(target, createEvent(eventType)), 0);
609 } else {
610 target.addEventListener(eventType, fn);
611 }
612 }
613 return descriptor.set ? descriptor.set.call(this, fn) : undefined;
614 },
615 get() {
616 return descriptor.get ? descriptor.get.call(this) : undefined;
617 },
618 configurable: true
619 });
620 });
621 })();
622
623 // Activation System
624 function activate() {
625 if (activated) return;
626 activated = true;
627 document.dispatchEvent(createTrackerEvent('activation'));
628
629 // Remove listeners
630 ['mousemove', 'click', 'scroll', 'keydown'].forEach(evt => {
631 window.removeEventListener(evt, activate, true);
632 });
633
634 processScripts();
635 setupObserver();
636 }
637
638 function processScripts() {
639 const scripts = Array.from(document.querySelectorAll('script[type=\"text/bwp-script\"]:not([data-earlyberqwp])'));
640 scripts.forEach(convertScript);
641 }
642
643 function setupObserver() {
644 new MutationObserver(mutations => {
645 mutations.forEach(mutation => {
646 mutation.addedNodes.forEach(node => {
647 if (node.tagName === 'SCRIPT' && node.type === 'text/bwp-script') {
648 convertScript(node);
649 }
650 });
651 });
652 }).observe(document, { childList: true, subtree: true });
653 }
654
655 // Script Conversion
656 function convertScript(original) {
657 if (original.dataset.processed) return;
658 original.dataset.processed = 'true';
659
660 const script = document.createElement('script');
661 const attrs = Array.from(original.attributes).filter(a => a.name !== 'type');
662 const id = `script-\${performance.now()}-\${Math.random().toString(36).slice(2)}`;
663
664 script.type = original.getAttribute('data-type');
665 try {
666 attrs.forEach(attr => script.setAttribute(attr.name, attr.value));
667 } catch (e) {
668 }
669 if (original.textContent) script.textContent = original.textContent;
670
671 pendingScripts.set(id, {
672 element: script,
673 src: script.src,
674 isExternal: !!script.src
675 });
676
677 document.dispatchEvent(createTrackerEvent('script-loading', {
678 scriptId: id,
679 element: script
680 }));
681
682 script.addEventListener('load', () => handleCompletion(id, true));
683 script.addEventListener('error', () => handleCompletion(id, false));
684
685 if (original.textContent) {
686 handleCompletion(id, true);
687 }
688
689 // script.async = false;
690 original.replaceWith(script);
691 }
692
693 function handleCompletion(id, success) {
694 const record = pendingScripts.get(id);
695 if (!record) return;
696
697 pendingScripts.delete(id);
698 completedCount++;
699
700 document.dispatchEvent(createTrackerEvent('script-complete', {
701 scriptId: id,
702 success,
703 duration: performance.now() - record.element.startTime
704 }));
705
706 if (pendingScripts.size === 0) {
707 document.dispatchEvent(createTrackerEvent('scripts-loaded', {
708 total: completedCount,
709 failed: completedCount - Array.from(pendingScripts.values())
710 .filter(r => r.success).length
711 }));
712 }
713 }
714
715 document.addEventListener('bwp:scripts-loaded', (e) => {
716 console.log('scripts loaded.')
717 window.dispatchEvent(new Event('berqwp_after_delay_js_loaded'));
718
719 });
720
721 // Initialization
722 activate();
723
724 window.addEventListener('berqwp_after_delay_js_loaded', function() {
725 let event = new Event('DOMContentLoaded', {
726 bubbles: true,
727 cancelable: true
728 });
729 document.dispatchEvent(event);
730 document.dispatchEvent(new Event('readystatechange'));
731 window.dispatchEvent(new Event('load'));
732
733
734 // Create a new resize event
735 var resizeEvent = new Event('resize');
736
737 // Dispatch the resize event
738 window.dispatchEvent(resizeEvent);
739
740
741 if (berq_click) {
742 setTimeout(function() {
743 console.log(berq_click);
744 const clickEvent = new MouseEvent('click', {
745 bubbles: true,
746 cancelable: false,
747 view: window
748 });
749 berq_click.dispatchEvent(clickEvent);
750 berq_click = null;
751 }, 500)
752 }
753
754 });
755 })();
756 }
757 </script>
758 ";
759
760 if ($this->settings['js_optimization'] == 'asynchronous') {
761 $script .= "
762 <script defer>
763 requestIdleCallback(() => {
764 berqwpLoadJs();
765 });
766 </script>
767 ";
768 }
769
770 if ($this->settings['js_optimization'] == 'delay') {
771 $script .= "
772 <script defer>
773 (function() {
774
775 let berqwpScriptTags = document.querySelectorAll('script[type=\"text/bwp-script\"]');
776 let jsUrls = Array.from(berqwpScriptTags).map(scriptTag => scriptTag.getAttribute('src')).filter(url => url);
777
778 function preloadJS(jsURLs) {
779 return window.bwpPreloader.preloadScripts();
780 }
781
782 let bwp_js_initialized = false;
783 function bwp_js_init(jsUrls) {
784 if (bwp_js_initialized) {
785 return;
786 }
787 bwp_js_initialized = true;
788
789 preloadJS(jsUrls)
790 .then(() => {
791 console.log('Preloading completed, invoking berqwpLoadJs...');
792 if (typeof berqwpLoadJs === 'function') {
793 berqwpLoadJs();
794 } else {
795 console.error('berqwpLoadJs is not defined or not a function.');
796 }
797 })
798 .catch(error => {
799 console.error('bwp_js_init failed during preloadJS:', error);
800 });
801 }
802
803 function berqwp_js_handleUserInteraction(event) {
804
805 if (event.type === 'click' || event.type === 'touchstart') {
806 berq_click = event.target;
807
808 // Traverse up the DOM to find the closest <a> ancestor
809 if (berq_click.closest('a')) {
810 berq_click = berq_click.closest('a');
811 }
812
813 // If click is done on a link
814 if (berq_click.tagName === 'A' && berq_click.href && /^https?:\/\//.test(berq_click.href)) {
815 console.log('Skipping JavaScript execution, a link was clicked.')
816 return;
817 }
818
819 console.log(event.type)
820
821 }
822
823 bwp_js_init(jsUrls);
824
825
826 // After running the function, remove all event listeners to ensure it runs only once
827 for (let eventType of berqwp_js_interactionEventTypes) {
828 window.removeEventListener(eventType, berqwp_js_handleUserInteraction);
829 }
830 }
831
832 let berqwp_js_interactionEventTypes = ['click', 'mousemove', 'keydown', 'touchstart', 'scroll', 'berqwpLoadJS', 'berqwp_interaction_event'];
833
834 for (let eventType of berqwp_js_interactionEventTypes) {
835 window.addEventListener(eventType, berqwp_js_handleUserInteraction, { passive: false });
836 }
837
838 })()
839 </script>
840 ";
841 }
842 }
843
844 return $script;
845 }
846
847 function optimize_js($buffer) {
848 $dom = HtmlDomParser::str_get_html($buffer);
849 $js_excludes = $this->settings['exclude_js'];
850 $js_excludes = array_map(function ($kw) {
851 return sanitize_text_field(trim($kw));
852 }, $js_excludes);
853
854 foreach ($dom->find('script') as $element) {
855 $script_src = $element->src;
856 $outerhtml = $element->outertext;
857
858 if (strpos($outerhtml, 'document.write(') !== false) {
859 continue;
860 }
861
862 if (strpos($outerhtml, '$zoho.salesiq = ') !== false) {
863 continue;
864 }
865
866 if (strpos($outerhtml, 'CRLeadStar.init') !== false) {
867 continue;
868 }
869
870 if (!empty($element->type) && $element->type !== 'text/javascript' && $element->type !== 'application/javascript' && $element->type !== 'module') {
871 continue;
872 }
873
874 if ($element->hasAttribute('data-berqwp-exclude')) {
875 continue;
876 }
877
878 // exclude script tags
879 if (!empty($js_excludes)) {
880 foreach ($js_excludes as $exclude_kw) {
881 if (strpos($outerhtml, $exclude_kw) !== false) {
882
883 if ($this->settings['defer_excluded_js'] && !$element->hasAttribute('async')) {
884 $element->setAttribute('defer', 'defer');
885 }
886
887 continue 2;
888 }
889 }
890 }
891
892 if ($this->settings['js_optimization'] == 'defer') {
893
894 if (!$element->hasAttribute('async')) {
895 $element->setAttribute('defer', 'defer');
896 }
897
898 continue;
899 }
900
901 if (empty($element->type) || $element->type == 'text/javascript') {
902 $element->setAttribute('data-type', 'text/javascript');
903 $element->type = 'text/bwp-script';
904 } else {
905 $element->setAttribute('data-type', esc_attr($element->type));
906 $element->type = 'text/bwp-script';
907 }
908
909 }
910
911 return (string) $dom;
912 }
913
914 function lazy_load_videos($buffer) {
915 $dom = HtmlDomParser::str_get_html($buffer);
916
917 foreach ($dom->find('video') as $element) {
918 $outerhtml = $element->outertext;
919 $parent = $element->parent();
920
921 if (strpos($outerhtml, 'video-js') !== false) {
922 continue;
923 }
924
925 if (strpos($outerhtml, 'mg_self-hosted-video') !== false) {
926 continue;
927 }
928
929 if (!empty($element->src)) {
930 $original_src = $element->src;
931 // $video->attr['data-berqwpsrc'] = $original_src;
932 $element->setAttribute('data-berqwpsrc', $original_src);
933 $element->removeAttribute('src');
934 }
935
936 // Handle each <source> element within the video tag
937 foreach ($element->find('source') as $source) {
938 if (!empty($source->src)) {
939 $original_src = $source->src;
940 // $source->attr['data-berqwpsrc'] = $original_src;
941 $source->setAttribute('data-berqwpsrc', $original_src);
942 $source->removeAttribute('src');
943 }
944 unset($source);
945 }
946
947 $class = $element->getAttribute('class');
948 $class .= ' berqwp-lazy-video';
949 $element->setAttribute('class', $class);
950 $element->setAttribute('preload', 'none');
951 // $element->attr[' preload'] = 'none';
952 }
953
954 return (string) $dom;
955 }
956
957 function lazy_load_images($buffer) {
958 $dom = HtmlDomParser::str_get_html($buffer);
959 $image_excludes = $this->settings['exclude_img_lazy_load'];
960 $image_excludes = array_map(function ($kw) {
961 return sanitize_text_field(trim($kw));
962 }, $image_excludes);
963
964 foreach ($dom->find('img') as $element) {
965
966 if (!$element->hasAttribute('src') || empty($element->src)) {
967 continue;
968 }
969
970 $img_src = $element->src;
971 $outerhtml = $element->outertext;
972 $img_path = self::url_to_path($img_src);
973
974 // exclude lazy load
975 if (!empty($image_excludes)) {
976 foreach ($image_excludes as $exclude_kw) {
977 if (strpos($outerhtml, $exclude_kw) !== false) {
978 continue 2;
979 }
980 }
981 }
982
983 if (strpos($img_src, 'data:') === 0) {
984 continue;
985 }
986
987 if (strpos($img_src, ';base64,') !== false) {
988 continue;
989 }
990
991 if (strpos($outerhtml, 'rs-lazyload') !== false) {
992 continue;
993 }
994
995 if (strpos($outerhtml, 'mfn-lazy') !== false) {
996 continue;
997 }
998
999 if (strpos($outerhtml, 'data-dbsrc=') !== false) {
1000 continue;
1001 }
1002
1003 if (strpos($outerhtml, 'data-orig-src=') !== false) {
1004 continue;
1005 }
1006
1007 if (strpos($outerhtml, 'facebook.com') !== false) {
1008 continue;
1009 }
1010
1011 if (strpos($img_src, '${') !== false) {
1012 continue;
1013 }
1014
1015 // Skip smush lazy loading
1016 if (strpos($img_src, '--smush-placeholder-width') !== false) {
1017 continue;
1018 }
1019
1020 // wpbackery background image
1021 if (strpos($img_src, 'class="background-image"') !== false) {
1022 continue;
1023 }
1024
1025 // lscache lazy load
1026 if (strpos($img_src, 'class="lazyload"') !== false) {
1027 continue;
1028 }
1029
1030 // salient lazy load
1031 if (strpos($img_src, 'data-nectar-img-src') !== false) {
1032 continue;
1033 }
1034
1035 $width = $element->width;
1036 $height = $element->height;
1037
1038 if (!empty($img_path) && file_exists($img_path) && !$element->hasAttribute('width') && !$element->hasAttribute('height')) {
1039 list($width, $height) = getimagesize($img_path);
1040 }
1041
1042 $element->removeAttribute('src');
1043 $element->setAttribute('data-berqwpsrc', $img_src);
1044
1045 if ($element->hasAttribute('srcset')) {
1046 $srcset = $element->srcset;
1047 $element->removeAttribute('srcset');
1048 $element->setAttribute('data-berqwp-srcset', $srcset);
1049 }
1050
1051 if (!empty($width) && !empty($height)) {
1052 $svg = '<svg width="' . $width . '" height="' . $height . '" xmlns="http://www.w3.org/2000/svg" version="1.1">';
1053 $svg .= '<rect width="100%" height="100%" fill="none" />';
1054 $svg .= '</svg>';
1055
1056 $base64Svg = base64_encode($svg);
1057
1058 // Create data URI
1059 $ph_webp_url = 'data:image/svg+xml;base64,' . $base64Svg;
1060 } else {
1061 $ph_webp_url = 'data:image/gif;placeholder=MjQxOjM1NQ==-1;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAICTAEAOw==';
1062 }
1063
1064 $element->setAttribute('src', $ph_webp_url);
1065 $element->setAttribute('decoding', 'async');
1066
1067 }
1068
1069 return (string) $dom;
1070 }
1071
1072 private function extract_template_scripts( $buffer ) {
1073 $placeholders = [];
1074
1075 $pattern = '/<script(?=[^>]*\btype=["\'](?:text\/template|text\/x-template|text\/x-handlebars-template|text\/x-handlebars|text\/ng-template)["\'])[^>]*>.*?<\/script>/is';
1076
1077 $result = preg_match_all( $pattern, $buffer, $matches );
1078
1079 if ( $result === false || $result === 0 ) {
1080 return [ $buffer, $placeholders ];
1081 }
1082
1083 foreach ( $matches[0] as $index => $script_block ) {
1084 $placeholder = '<!--BERQWP_TEMPLATE_' . $index . '-->';
1085 $placeholders[ $placeholder ] = $script_block;
1086 $buffer = str_replace( $script_block, $placeholder, $buffer );
1087 }
1088
1089 return [ $buffer, $placeholders ];
1090 }
1091
1092 private function restore_template_scripts( $buffer, $placeholders ) {
1093 if ( empty( $placeholders ) ) {
1094 return $buffer;
1095 }
1096
1097 foreach ( $placeholders as $placeholder => $original_block ) {
1098 $buffer = str_replace( $placeholder, $original_block, $buffer );
1099 }
1100
1101 return $buffer;
1102 }
1103
1104 function lazy_load_iframes($buffer) {
1105 $dom = HtmlDomParser::str_get_html($buffer);
1106
1107 foreach ($dom->find('iframe') as $element) {
1108 $outerhtml = $element->outertext;
1109 $outerhtml = apply_filters('photon_before_iframe_optimization', $outerhtml);
1110 $isyoutube = false;
1111
1112 if ($this->settings['preload_yt_poster']) {
1113
1114 if (preg_match('/youtube\.com\/embed\/([a-zA-Z0-9_-]+)/', $element->src, $matches)) {
1115 $videoId = $matches[1];
1116 $isyoutube = true;
1117
1118 // Generate the thumbnail URL
1119 $thumbnailUrl = "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg";
1120 $highresthumbnailUrl = "https://img.youtube.com/vi/{$videoId}/maxresdefault.jpg";
1121
1122
1123 if (strpos($element->src, 'autoplay=1') !== false && strpos($element->src, 'mute=') === false) {
1124 $element->setAttribute('src', $element->src . "&mute=1");
1125 }
1126
1127 // Generate lazy-load HTML content for srcdoc
1128 $srcdocContent = <<<HTML
1129 <style>
1130 *{padding:0;margin:0;overflow:hidden}
1131 .play-button {
1132 width: 70px;
1133 position: absolute;
1134 top: 50%;
1135 left: 50%;
1136 transform: translate(-50%, -50%);
1137 cursor: pointer;
1138 }
1139 </style>
1140 <a href='{$element->src}' style='position: absolute; width: 100%; height: 100%; background: url({$thumbnailUrl}) no-repeat center center; background-size: cover;'>
1141 <picture>
1142 <source srcset='{$highresthumbnailUrl}' media='(min-width: 999px)'>
1143 <img src='{$thumbnailUrl}' style='position: absolute; width: 100%; height: 100%;object-fit: cover;'>
1144 </picture>
1145 <div class='play-button'>
1146 <svg height='100%' version='1.1' viewBox='0 0 68 48' width='100%'>><path class='ytp-large-play-button-bg' d='M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z' fill='#f00'></path><path d='M 45,24 27,14 27,34' fill='#fff'></path></svg>
1147 </div>
1148 </a>
1149 HTML;
1150
1151 // Update the iframe attributes
1152 $element->setAttribute('srcdoc', $srcdocContent);
1153
1154 $element->setAttribute('src', $element->src); // Remove immediate src loading
1155 }
1156
1157 if ($isyoutube && $element->hasAttribute('data-berqWPexclude')) {
1158 continue;
1159 }
1160
1161 }
1162
1163 if (strpos($outerhtml, 'youtube.com') !== false && !$element->hasAttribute('referrerpolicy')) {
1164 $element->setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
1165 }
1166
1167 if ($element->hasAttribute('data-berqWPexclude')) {
1168 continue;
1169 }
1170
1171 $class = $element->getAttribute('class');
1172
1173 if (!empty($class)) {
1174 $classes = explode(' ', $class ?? '');
1175 $classes = array_filter($classes, fn($c) => $c !== 'lazyload');
1176 $class = trim(implode(' ', $classes));
1177 $element->setAttribute('class', $class);
1178 }
1179
1180 if ($element->hasAttribute('data-src')) {
1181 $element->setAttribute('rm-src', $element->src);
1182 $element->removeAttribute('src');
1183 }
1184
1185 $outerhtml = esc_attr($element->outertext);
1186
1187 $element->outertext = '<div class="berqwp-lazy-youtube" data-embed="' . $outerhtml . '"></div>';
1188
1189 }
1190
1191 return (string) $dom;
1192 }
1193
1194 function store_cache($buffer) {
1195 // Define the cache directory
1196 $cache_directory = bwp_get_cache_dir();
1197 $url = bwp_canonicalize_page_url($this->page_url);
1198
1199 // Create the cache directory if it doesn't exist
1200 if (!file_exists($cache_directory)) {
1201 mkdir($cache_directory, 0755, true);
1202 }
1203
1204 $cache = new Cache(null, bwp_get_cache_dir());
1205 $cache->store_cache($url, $buffer);
1206
1207
1208 do_action('berqwp_stored_page_cache', $this->page_slug);
1209
1210 global $berq_log;
1211 $berq_log->info("Stored cache for $url from PageOptimizer class");
1212 }
1213
1214 function preload_images($buffer) {
1215 $dom = HtmlDomParser::str_get_html($buffer);
1216 $count = 0;
1217 $candidates = [];
1218
1219 foreach ($dom->find('img') as $element) {
1220
1221 if ($count >= 9) {
1222 break;
1223 }
1224
1225 if (!$element->hasAttribute('src') || empty($element->src)) {
1226 continue;
1227 }
1228
1229 $img_src = $element->src;
1230 $outerhtml = $element->outertext;
1231 $img_path = self::url_to_path($img_src);
1232
1233 // exclude lazy load
1234 // if (!empty($image_excludes)) {
1235 // foreach ($image_excludes as $exclude_kw) {
1236 // if (strpos($outerhtml, $exclude_kw) !== false) {
1237 // continue 2;
1238 // }
1239 // }
1240 // }
1241
1242 if (strpos($img_src, 'data:') === 0) {
1243 continue;
1244 }
1245
1246 if (strpos($img_src, ';base64,') !== false) {
1247 continue;
1248 }
1249
1250 if (strpos($outerhtml, 'rs-lazyload') !== false) {
1251 continue;
1252 }
1253
1254 if (strpos($outerhtml, 'mfn-lazy') !== false) {
1255 continue;
1256 }
1257
1258 if (strpos($outerhtml, 'data-dbsrc=') !== false) {
1259 continue;
1260 }
1261
1262 if (strpos($outerhtml, 'data-orig-src=') !== false) {
1263 continue;
1264 }
1265
1266 if (strpos($outerhtml, 'facebook.com') !== false) {
1267 continue;
1268 }
1269
1270 $width = $element->width;
1271 $height = $element->height;
1272
1273 if (!empty($img_path) && file_exists($img_path) && !$element->hasAttribute('width') && !$element->hasAttribute('height')) {
1274 list($width, $height) = getimagesize($img_path);
1275 }
1276
1277 $candidate = [
1278 'width' => $width,
1279 'height' => $height,
1280 'area' => (int) $width * (int) $height,
1281 'src' => $img_src,
1282 ];
1283
1284 if ($element->hasAttribute('loading')) {
1285 $candidate['loading'] = $element->loading;
1286 }
1287
1288 if ($element->hasAttribute('srcset')) {
1289 $candidate['srcset'] = $element->srcset;
1290 }
1291
1292 if ($element->hasAttribute('sizes')) {
1293 $candidate['sizes'] = $element->sizes;
1294 }
1295
1296 $candidates[] = $candidate;
1297 $count++;
1298
1299
1300 }
1301
1302 $buffer = (string) $dom;
1303
1304 if (!empty($candidates)) {
1305
1306 $processed = [];
1307
1308 // unique src
1309 $candidates = array_filter($candidates, function ($item) use (&$processed) {
1310 if (in_array($item['src'], $processed)) {
1311 return false;
1312 }
1313
1314 $processed[] = $item['src'];
1315
1316 return true;
1317 });
1318
1319 usort($candidates, function ($a, $b) {
1320 return $b['area'] <=> $a['area']; // DESC
1321 });
1322
1323 $candidates = array_slice($candidates, 0, 2);
1324 $prelaod_html = '';
1325
1326 foreach ($candidates as $preload_img) {
1327 $prelaod_html .= '<link rel="preload" as="image" ';
1328 $prelaod_html .= ' fetchpriority="high" ';
1329 $prelaod_html .= 'href="'.$preload_img['src'].'" ';
1330
1331 if (!empty($preload_img['srcset'])) {
1332 $prelaod_html .= 'imagesrcset="'.$preload_img['srcset'].'" ';
1333 }
1334
1335 if (!empty($preload_img['sizes'])) {
1336 $prelaod_html .= 'imagesizes="'.$preload_img['sizes'].'" ';
1337 }
1338
1339 $prelaod_html .= '>'.PHP_EOL;
1340 }
1341
1342 /* $buffer = berqwp_prependHtmlToHead($buffer, $prelaod_html); */
1343 $this->early_head_html .= $preload_html;
1344
1345 }
1346
1347
1348
1349 return $buffer;
1350 }
1351
1352 function preload_stylesheet($buffer) {
1353 $dom = HtmlDomParser::str_get_html($buffer);
1354
1355 foreach ($dom->find('link') as $element) {
1356
1357 if ($element->rel == 'stylesheet'){
1358 $element->setAttribute('rel', 'preload');
1359 $element->setAttribute('as', 'style');
1360 $element->removeAttribute('media');
1361 // $element->setAttribute('media', 'print');
1362 // $element->setAttribute('onload', "this.media='all'");
1363 $element->setAttribute('onload', "this.rel='stylesheet'");
1364 }
1365
1366 }
1367
1368 return (string) $dom;
1369
1370 }
1371
1372 function delay_styles($buffer) {
1373
1374 if ( !empty($this->settings['css_optimization']) && $this->settings['css_optimization'] !== 'delay' && $this->settings['css_optimization'] !== 'asynchronous' ) {
1375 return $buffer;
1376 }
1377
1378 $dom = HtmlDomParser::str_get_html($buffer);
1379
1380 $style_excludes = array_map(function ($kw) {
1381 return sanitize_text_field(trim($kw));
1382 }, $this->settings['exclude_css']);
1383
1384 foreach ($dom->find('link') as $element) {
1385
1386 $outerhtml = $element->outertext;
1387
1388 if ($element->rel == 'stylesheet' || $element->as == 'style'){
1389
1390 if (!$element->hasAttribute('href')) {
1391 continue;
1392 }
1393
1394 if ($element->hasAttribute('data-berqwp-exclude')) {
1395 continue;
1396 }
1397
1398 // exclude style
1399 if (!empty($style_excludes)) {
1400 foreach ($style_excludes as $exclude_kw) {
1401 if (!empty($exclude_kw) && strpos($outerhtml, $exclude_kw) !== false) {
1402 continue;
1403 }
1404 }
1405 }
1406
1407 $element->setAttribute('rel', 'stylesheet');
1408 $element->setAttribute('as', 'style');
1409 $element->setAttribute('data-berqwp-style-href', $element->href);
1410 $element->removeAttribute('href');
1411
1412 }
1413
1414 }
1415
1416 foreach ($dom->find('style') as $element) {
1417
1418 $outerhtml = $element->outertext;
1419
1420 if ($element->hasAttribute('data-berqwp-exclude')) {
1421 continue;
1422 }
1423
1424 // exclude style
1425 if (!empty($style_excludes)) {
1426 foreach ($style_excludes as $exclude_kw) {
1427 if (!empty($exclude_kw) && strpos($outerhtml, $exclude_kw) !== false) {
1428 continue;
1429 }
1430 }
1431 }
1432
1433 $element->setAttribute('type', 'text/berqwp-style');
1434 }
1435
1436 return (string) $dom;
1437
1438 }
1439
1440 function buffer_end($buffer) {
1441
1442 // $buffer = file_get_contents('/Users/hamzamairaj/Local Sites/plugin-berqwp/app/public/wp-content/plugins/searchpro/inc/photon/page.html');
1443
1444 if (empty($buffer)) {
1445 return $buffer;
1446 }
1447
1448 $buffer = $buffer.'<!-- Optimized with BerqWP\'s instant cache. --->';
1449
1450 $script = "
1451 <script defer>
1452 var comment = document.createComment(' This website is optimized using the BerqWP plugin. @".time()." ');
1453 document.documentElement.insertBefore(comment, document.documentElement.firstChild);
1454
1455 function isMobileDevice() {
1456 return /Mobi|Android|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i.test(navigator.userAgent);
1457 }
1458
1459 function astraHeaderClass() {
1460 if (isMobileDevice() && window.screen.width <= 999) {
1461 if ((document.body.classList.contains('theme-astra') || document.body.classList.contains('ast-page-builder-template')) && !document.body.classList.contains('ast-header-break-point')) {
1462 document.body.classList.add('ast-header-break-point');
1463 }
1464 }
1465 }
1466
1467 function divimobilemenu() {
1468 const divimenuele = document.querySelector('#et_mobile_nav_menu .mobile_menu_bar.mobile_menu_bar_toggle');
1469
1470 if (isMobileDevice() && divimenuele) {
1471 divimenuele.innerHTML = '<div class=\"dipi_hamburger hamburger hamburger--spring\"> <div class=\"hamburger-box\"> <div class=\"hamburger-inner\"></div> </div> </div>';
1472 }
1473 }
1474
1475 divimobilemenu();
1476 astraHeaderClass();
1477 window.addEventListener('resize', function() {
1478 astraHeaderClass();
1479 });
1480
1481 window.dispatchEvent(new Event('berqwp_js_initialized'));
1482 </script>
1483
1484 ";
1485
1486 [ $buffer, $template_placeholders ] = $this->extract_template_scripts( $buffer );
1487
1488 $buffer = $this->preload_images($buffer);
1489 // $buffer = $this->preload_stylesheet($buffer);
1490
1491 if ($this->settings['img_lazyloading']) {
1492 $buffer = $this->lazy_load_images($buffer);
1493 }
1494
1495 if ($this->settings['js_optimization'] == 'auto') {
1496 if ($this->settings['opt_mode'] == 'basic') {
1497 $this->settings['js_optimization'] = 'defer';
1498 }
1499
1500 if ($this->settings['opt_mode'] == 'medium') {
1501 $this->settings['js_optimization'] = 'asynchronous';
1502 }
1503
1504 if ($this->settings['opt_mode'] == 'blaze') {
1505 $this->settings['js_optimization'] = 'delay';
1506 }
1507
1508 if ($this->settings['opt_mode'] == 'aggressive') {
1509 $this->settings['js_optimization'] = 'delay';
1510 }
1511 }
1512
1513 if ($this->settings['js_optimization'] !== 'disable') {
1514 $buffer = $this->optimize_js($buffer);
1515 }
1516
1517 if ($this->settings['lazy_load_videos']) {
1518 $buffer = $this->lazy_load_videos($buffer);
1519 }
1520
1521 if ($this->settings['youtube_lazyloading']) {
1522 // $buffer = $this->lazy_load_iframes($buffer);
1523 }
1524
1525 if ($this->settings['css_optimization'] == 'auto') {
1526 if ($this->settings['opt_mode'] == 'basic') {
1527 $this->settings['css_optimization'] = 'disable';
1528 }
1529
1530 if ($this->settings['opt_mode'] == 'medium') {
1531 $this->settings['css_optimization'] = 'asynchronous';
1532 }
1533
1534 if ($this->settings['opt_mode'] == 'blaze') {
1535 $this->settings['css_optimization'] = 'asynchronous';
1536 }
1537
1538 if ($this->settings['opt_mode'] == 'aggressive') {
1539 $this->settings['css_optimization'] = 'delay';
1540 }
1541 }
1542
1543 $enable_used_css = (bool) apply_filters(
1544 'berqwp_local_used_css',
1545 apply_filters('berqwp_local_critical_css', (bool) get_option('berqwp_enable_used_css'))
1546 );
1547
1548 // $enable_used_css = false; // disable
1549
1550 if ($enable_used_css && $this->settings['css_optimization'] !== 'disable') {
1551 $critical_css = new berqUsedCSS(get_option('home'));
1552 $critical_css->forceInclude($this->settings['force_include_critical_css']);
1553 $critical_css = $critical_css->process_css($buffer);
1554 $critical_css = sprintf('<style data-berqwp-exclude id="berqwp-used-css">%s</style>', $critical_css);
1555
1556 /* $buffer = berqwp_prependHtmlToHead($buffer, $critical_css); */
1557 $this->early_head_html .= $critical_css;
1558 $buffer = $this->delay_styles($buffer);
1559
1560 }
1561
1562 $buffer = berqwp_earlyHeadHtml($buffer, $this->early_head_html);
1563
1564 $buffer = $this->restore_template_scripts( $buffer, $template_placeholders );
1565
1566 $script = apply_filters('berqwp_photon_before_closing_body', $script);
1567 $buffer = berqwp_appendHtmlToBody($buffer, $script);
1568
1569 $buffer = apply_filters( 'berqwp_cache_buffer', $buffer );
1570 $this->store_cache($buffer);
1571
1572 return $buffer;
1573 }
1574 }
1575