PluginProbe
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript / 4.1.17
BerqWP – All-In-One Optimization for Core Web Vitals, Cache, CDN, Images, CSS & JavaScript v4.1.17
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
← All changes | inc/photon/class-berqPageOptimizer.php +1537 -22 1.9.3 → 4.1.17 View file →
@@ -1,9 +1,31 @@
1 1 <?php
2 +if (!defined('ABSPATH')) exit;
2 3
4 +use BerqWP\Cache;
5 +use BerqWP_Deps\voku\helper\HtmlDomParser;
6 +
3 7 class berqPageOptimizer {
4 8 public $page_slug = null;
5 -
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 +
6 28 function start_cache() {
7 29 add_action('template_redirect', [$this, 'buffer_start'], 2);
8 30 }
9 31
@@ -10,15 +32,1170 @@
10 32 function set_slug($slug) {
11 33 $this->page_slug = $slug;
12 34 }
13 35
36 + function set_page($page_url) {
37 + $this->page_url = $page_url;
38 + }
39 +
40 +
14 41 function buffer_start() {
15 42 ob_start([$this, 'buffer_end']);
16 43 }
17 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 +
18 1194 function store_cache($buffer) {
19 1195 // Define the cache directory
20 - $cache_directory = optifer_cache . '/html/';
1196 + $cache_directory = bwp_get_cache_dir();
1197 + $url = $this->page_url;
21 1198
22 1199 // Create the cache directory if it doesn't exist
23 1200 if (!file_exists($cache_directory)) {
24 1201 mkdir($cache_directory, 0755, true);
@@ -23,37 +1200,375 @@
23 1200 if (!file_exists($cache_directory)) {
24 1201 mkdir($cache_directory, 0755, true);
25 1202 }
26 1203
27 - $cache_file = $cache_directory . md5($this->page_slug) . '.html';
28 -
29 - // update_option( md5($slug), $key );
30 - file_put_contents($cache_file, $buffer);
31 -
32 - if (bwp_is_gzip_supported()) {
33 - $cache_file = $cache_directory . md5($this->page_slug) . '.gz';
34 - $buffer = gzencode($buffer, 9);
35 - file_put_contents($cache_file, $buffer);
36 - }
37 -
38 -
1204 + $cache = new Cache(null, bwp_get_cache_dir());
1205 + $cache->store_cache($url, $buffer);
1206 +
1207 +
39 1208 do_action('berqwp_stored_page_cache', $this->page_slug);
40 -
1209 +
41 1210 global $berq_log;
42 - $berq_log->info("Stored cache for $this->page_slug from PageOptimizer class");
1211 + $berq_log->info("Stored cache for $url from PageOptimizer class");
43 1212 }
44 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 +
45 1440 function buffer_end($buffer) {
46 1441
47 - require_once optifer_PATH . '/simplehtmldom/simple_html_dom.php';
1442 + // $buffer = file_get_contents('/Users/hamzamairaj/Local Sites/plugin-berqwp/app/public/wp-content/plugins/searchpro/inc/photon/page.html');
48 1443
1444 + if (empty($buffer)) {
1445 + return $buffer;
1446 + }
1447 +
49 1448 $buffer = $buffer.'<!-- Optimized with BerqWP\'s instant cache. --->';
50 -
51 - $berqBufferOptimize = new berqBufferOptimize();
52 - $berqBufferOptimize->optimize_buffer($buffer, $this->page_slug);
53 -
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 +
54 1569 $buffer = apply_filters( 'berqwp_cache_buffer', $buffer );
55 1570 $this->store_cache($buffer);
56 1571
57 1572 return $buffer;
58 1573 }
59 -}
1574 +}