PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 4.5.3
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v4.5.3
4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / Core / AssetManager.php
embedpress / Core Last commit date
AssetManager.php 1 month ago LocalizationManager.php 1 month ago init.php 9 months ago
AssetManager.php
1705 lines
1 <?php
2
3 namespace EmbedPress\Core;
4
5 // Include LocalizationManager
6 // require_once __DIR__ . '/LocalizationManager.php';
7
8 use Embedpress\Core\LocalizationManager;
9 use EmbedPress\Includes\Classes\Helper;
10
11 /**
12 * EmbedPress Asset Manager
13 *
14 * Centralized asset management for JS/CSS files across blocks, Elementor, admin, and frontend
15 * Handles new build files from Vite build system
16 */
17 class AssetManager
18 {
19 /**
20 * Track which handles should be treated as ES modules
21 */
22 private static $module_handles = [];
23
24 /**
25 * Track if the global module filter has been added
26 */
27 private static $module_filter_added = false;
28
29 /**
30 * Cache for content detection to avoid multiple checks
31 */
32 private static $has_embedpress_content = null;
33
34 /**
35 * Cache for custom player detection
36 */
37 private static $custom_player_enabled = null;
38
39 /**
40 * Cache for detected embed types on current page
41 */
42 private static $detected_embed_types = null;
43
44 /**
45 * Asset definitions with context-based loading
46 */
47 private static $assets = [
48
49 // 🔧 Scripts (Ordered by Priority)
50 // ----------------------------------
51
52 // Vendor assets (copied to assets folder for consistency)
53 // Priority 1-5: Core vendor libraries
54 'plyr-css' => [
55 'file' => 'css/plyr.css',
56 'deps' => [],
57 'contexts' => ['frontend', 'elementor', 'editor'],
58 'type' => 'style',
59 'handle' => 'embedpress-plyr-css',
60 'priority' => 1,
61 'condition' => 'custom_player', // Only load if custom player is enabled
62 'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers
63 ],
64 'carousel-vendor-css' => [
65 'file' => 'css/carousel.min.css',
66 'deps' => [],
67 'contexts' => ['frontend', 'elementor'],
68 'type' => 'style',
69 'handle' => 'embedpress-carousel-vendor-css',
70 'priority' => 1,
71 'condition' => 'has_content', // Load whenever EmbedPress content is present since front.js depends on it
72 ],
73 'glider-css' => [
74 'file' => 'css/glider.min.css',
75 'deps' => [],
76 'contexts' => ['frontend', 'elementor'],
77 'type' => 'style',
78 'handle' => 'embedpress-glider-css',
79 'priority' => 1,
80 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
81 ],
82 'plyr-js' => [
83 'file' => 'js/vendor/plyr.js',
84 'deps' => ['jquery'],
85 'contexts' => ['frontend', 'elementor', 'editor'],
86 'type' => 'script',
87 'footer' => true,
88 'handle' => 'embedpress-plyr',
89 'priority' => 2,
90 'condition' => 'custom_player', // Only load if custom player is enabled
91 'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers
92 ],
93 'carousel-vendor-js' => [
94 'file' => 'js/vendor/carousel.min.js',
95 'deps' => ['jquery'],
96 'contexts' => ['frontend', 'elementor'],
97 'type' => 'script',
98 'footer' => true,
99 'handle' => 'embedpress-carousel-vendor',
100 'priority' => 2,
101 'condition' => 'has_content', // Load whenever EmbedPress content is present since front.js depends on it
102 ],
103 'glider-js' => [
104 'file' => 'js/vendor/glider.min.js',
105 'deps' => ['jquery'],
106 'contexts' => ['frontend', 'elementor'],
107 'type' => 'script',
108 'footer' => true,
109 'handle' => 'embedpress-glider',
110 'priority' => 2,
111 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
112 ],
113 'pdfobject-js' => [
114 'file' => 'js/vendor/pdfobject.js',
115 'deps' => [],
116 'contexts' => ['frontend', 'elementor', 'editor'],
117 'type' => 'script',
118 'footer' => true,
119 'handle' => 'embedpress-pdfobject',
120 'priority' => 2,
121 'providers' => ['pdf', 'document'], // Only for PDF/document embeds
122 ],
123 'vimeo-player-js' => [
124 'file' => 'js/vendor/vimeo-player.js',
125 'deps' => [],
126 'contexts' => ['frontend', 'elementor'],
127 'type' => 'script',
128 'footer' => true,
129 'handle' => 'embedpress-vimeo-player',
130 'priority' => 2,
131 'providers' => ['vimeo'], // Only for Vimeo embeds
132 ],
133 'ytiframeapi-js' => [
134 'file' => 'js/vendor/ytiframeapi.js',
135 'deps' => [],
136 'contexts' => ['frontend', 'elementor'],
137 'type' => 'script',
138 'footer' => true,
139 'handle' => 'embedpress-ytiframeapi',
140 'priority' => 2,
141 'providers' => ['youtube', 'youtube-channel', 'youtube-live', 'youtube-shorts'], // Only for YouTube embeds
142 ],
143
144 // Priority 5-6: Main application build assets
145 'admin-js' => [
146 'file' => 'js/admin.build.js',
147 'deps' => [],
148 'contexts' => ['admin'],
149 'type' => 'script',
150 'footer' => true,
151 'handle' => 'embedpress-admin',
152 'priority' => 5,
153 'page' => 'embedpress'
154 ],
155 'onboarding-js' => [
156 'file' => 'js/onboarding.build.js',
157 'deps' => [],
158 'contexts' => ['admin'],
159 'type' => 'script',
160 'footer' => true,
161 'handle' => 'embedpress-onboarding',
162 'priority' => 5,
163 'page' => 'embedpress-onboarding'
164 ],
165 'custom-player-js' => [
166 'file' => 'js/custom-player.build.js',
167 'deps' => [],
168 'contexts' => ['admin'],
169 'type' => 'script',
170 'footer' => true,
171 'handle' => 'embedpress-custom-player',
172 'priority' => 5,
173 'page' => 'embedpress-player-engagement'
174 ],
175 // Priority 7-10: Blocks
176 'blocks-js' => [
177 'file' => 'js/blocks.build.js',
178 'deps' => ['wp-blocks', 'wp-i18n', 'wp-element', 'wp-api-fetch', 'wp-is-shallow-equal', 'wp-editor', 'wp-components'],
179 'contexts' => ['editor'],
180 'type' => 'script',
181 'footer' => true,
182 'handle' => 'embedpress-blocks-editor',
183 'priority' => 10,
184 ],
185 'blocks-editor-style' => [
186 'file' => 'css/blocks.build.css',
187 'deps' => [],
188 'contexts' => ['editor'],
189 'type' => 'style',
190 'handle' => 'embedpress-blocks-editor-style',
191 'priority' => 10,
192 ],
193 'blocks-style' => [
194 'file' => 'css/blocks.build.css',
195 'deps' => [],
196 'contexts' => ['frontend', 'editor'],
197 'type' => 'style',
198 'handle' => 'embedpress-blocks-style',
199 'priority' => 10,
200 ],
201 'lazy-load-css' => [
202 'file' => 'css/lazy-load.css',
203 'deps' => [],
204 'contexts' => ['frontend', 'elementor'],
205 'type' => 'style',
206 'handle' => 'embedpress-lazy-load-css',
207 'priority' => 10,
208 'condition' => 'lazy_load',
209 ],
210
211 // Priority 15-20: Legacy JS files
212 'admin-legacy-js' => [
213 'file' => 'js/admin.js',
214 'deps' => ['jquery'],
215 'contexts' => ['admin'],
216 'type' => 'script',
217 'footer' => true,
218 'handle' => 'embedpress-admin-legacy',
219 'priority' => 15,
220 'page' => 'embedpress'
221 ],
222 'ads-js' => [
223 'file' => 'js/sponsored.js',
224 'deps' => ['jquery', 'embedpress-front'],
225 'contexts' => ['editor', 'frontend', 'elementor'],
226 'type' => 'script',
227 'footer' => true,
228 'handle' => 'embedpress-ads',
229 'priority' => 16,
230 'condition' => 'has_content', // Load for any EmbedPress content (ads can be on any embed)
231 ],
232 'lazy-load-js' => [
233 'file' => 'js/lazy-load.js',
234 'deps' => [],
235 'contexts' => ['frontend', 'elementor'],
236 'type' => 'script',
237 'footer' => true,
238 'handle' => 'embedpress-lazy-load',
239 'priority' => 16,
240 'condition' => 'lazy_load',
241 ],
242 'analytics-tracker-js' => [
243 'file' => 'js/analytics-tracker.js',
244 'deps' => ['jquery'],
245 'contexts' => ['frontend', 'elementor'],
246 'type' => 'script',
247 'footer' => true,
248 'handle' => 'embedpress-analytics-tracker',
249 'priority' => 15,
250 'condition' => 'has_content', // Load for any EmbedPress content (analytics track all embeds)
251 ],
252 'carousel-js' => [
253 'file' => 'js/carousel.js',
254 'deps' => ['jquery', 'embedpress-carousel-vendor'],
255 'contexts' => ['frontend', 'elementor'],
256 'type' => 'script',
257 'footer' => true,
258 'handle' => 'embedpress-carousel',
259 'priority' => 15,
260 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
261 ],
262 'documents-viewer-js' => [
263 'file' => 'js/documents-viewer-script.js',
264 'deps' => ['jquery'],
265 'contexts' => ['frontend', 'elementor'],
266 'type' => 'script',
267 'footer' => true,
268 'handle' => 'embedpress-documents-viewer',
269 'priority' => 15,
270 'providers' => ['document', 'google-docs', 'google-sheets', 'google-slides'], // Only for document embeds
271 ],
272 'front-js' => [
273 'file' => 'js/front.js',
274 'deps' => ['jquery', 'embedpress-carousel-vendor'],
275 'contexts' => ['frontend', 'editor', 'elementor'],
276 'type' => 'script',
277 'footer' => true,
278 'handle' => 'embedpress-front',
279 'priority' => 15,
280 'condition' => 'has_content', // Core script - load for any EmbedPress content
281 ],
282 'gallery-justify-js' => [
283 'file' => 'js/gallery-justify.js',
284 'deps' => ['jquery'],
285 'contexts' => ['editor', 'frontend', 'elementor'],
286 'type' => 'script',
287 'footer' => true,
288 'handle' => 'embedpress-gallery-justify',
289 'priority' => 15,
290 'providers' => ['google-photos'],
291 ],
292 'pdf-gallery-js' => [
293 'file' => 'js/pdf-gallery.js',
294 'deps' => ['jquery'],
295 'contexts' => ['frontend', 'elementor', 'elementor-editor'],
296 'type' => 'script',
297 'footer' => true,
298 'handle' => 'embedpress-pdf-gallery',
299 'priority' => 15,
300 'providers' => ['pdf-gallery'],
301 ],
302 'pdf-lightbox-js' => [
303 'file' => 'js/ep-pdf-lightbox.js',
304 'deps' => [],
305 'contexts' => ['frontend', 'elementor'],
306 'type' => 'script',
307 'footer' => true,
308 'handle' => 'embedpress-pdf-lightbox',
309 'priority' => 15,
310 'providers' => ['pdf'],
311 ],
312 'meetup-timezone-js' => [
313 'file' => 'js/meetup-timezone.js',
314 'deps' => [],
315 'contexts' => ['frontend', 'elementor'],
316 'type' => 'script',
317 'footer' => true,
318 'handle' => 'embedpress-meetup-timezone',
319 'priority' => 15,
320 'providers' => ['meetup'], // Only for Meetup embeds
321 ],
322 'gutenberg-script-js' => [
323 'file' => 'js/gutneberg-script.js',
324 'deps' => ['wp-blocks', 'wp-element'],
325 'contexts' => ['editor'],
326 'type' => 'script',
327 'footer' => true,
328 'handle' => 'embedpress-gutenberg-script',
329 'priority' => 15,
330 ],
331 'init-plyr-js' => [
332 'file' => 'js/initplyr.js',
333 'deps' => ['jquery', 'embedpress-plyr'],
334 'contexts' => ['editor', 'frontend', 'elementor'],
335 'type' => 'script',
336 'footer' => true,
337 'handle' => 'embedpress-init-plyr',
338 'priority' => 15,
339 'condition' => 'custom_player', // Only load if custom player is enabled
340 'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers
341 ],
342 'instafeed-js' => [
343 'file' => 'js/instafeed.js',
344 'deps' => ['jquery'],
345 'contexts' => ['frontend', 'elementor'],
346 'type' => 'script',
347 'footer' => true,
348 'handle' => 'embedpress-instafeed',
349 'priority' => 15,
350 'providers' => ['instagram'], // Only for Instagram embeds
351 ],
352 'license-js' => [
353 'file' => 'js/license.js',
354 'deps' => ['jquery', 'wp-url'],
355 'contexts' => ['admin'],
356 'type' => 'script',
357 'footer' => true,
358 'handle' => 'embedpress-license',
359 'priority' => 15,
360 'page' => 'embedpress'
361 ],
362 'feature-notices-js' => [
363 'file' => 'js/feature-notices.js',
364 'deps' => ['jquery'],
365 'contexts' => ['admin'],
366 'type' => 'script',
367 'footer' => true,
368 'handle' => 'embedpress-feature-notices',
369 'priority' => 15,
370 // 'page' => 'embedpress'
371 ],
372 'preview-js' => [
373 'file' => 'js/preview.js',
374 'deps' => ['jquery'],
375 'contexts' => ['classic_editor'],
376 'type' => 'script',
377 'footer' => true,
378 'handle' => 'embedpress-preview',
379 'priority' => 15,
380 ],
381 'settings-js' => [
382 'file' => 'js/settings.js',
383 'deps' => ['jquery', 'wp-color-picker'],
384 'contexts' => ['admin'],
385 'type' => 'script',
386 'footer' => true,
387 'handle' => 'embedpress-settings',
388 'priority' => 15,
389 'page' => 'embedpress'
390 ],
391 'instagram-shortcode-generator-js' => [
392 'file' => 'js/instagram-shortcode-generator.js',
393 'deps' => [],
394 'contexts' => ['admin'],
395 'type' => 'script',
396 'footer' => true,
397 'handle' => 'embedpress-instagram-shortcode-generator',
398 'priority' => 15,
399 'page' => 'embedpress'
400 ],
401
402 // 🎨 Styles (Ordered by Priority)
403 // ----------------------------------
404
405 // Build CSS files (analytics.build.css is handled by Analytics.php)
406
407 // Legacy CSS files
408 'admin-notices-css' => [
409 'file' => 'css/admin-notices.css',
410 'deps' => [],
411 'contexts' => ['admin'],
412 'type' => 'style',
413 'handle' => 'embedpress-admin-notices',
414 'priority' => 5,
415 // 'page' => 'embedpress'
416 ],
417 'feature-notices-css' => [
418 'file' => 'css/feature-notices.css',
419 'deps' => [],
420 'contexts' => ['admin'],
421 'type' => 'style',
422 'handle' => 'embedpress-feature-notices',
423 'priority' => 5,
424 ],
425
426 'el-icon-css' => [
427 'file' => 'css/el-icon.css',
428 'deps' => [],
429 'contexts' => ['elementor-editor'],
430 'type' => 'style',
431 'handle' => 'embedpress-el-icon',
432 'priority' => 5,
433 ],
434 'embedpress-elementor-css' => [
435 'file' => 'css/embedpress-elementor.css',
436 'deps' => [],
437 'contexts' => ['elementor'],
438 'type' => 'style',
439 'handle' => 'embedpress-elementor-css',
440 'priority' => 5,
441 ],
442 'embedpress-css' => [
443 'file' => 'css/embedpress.css',
444 'deps' => [],
445 'contexts' => ['editor', 'frontend', 'elementor'],
446 'type' => 'style',
447 'handle' => 'embedpress-css',
448 'priority' => 5,
449 ],
450 'pdf-gallery-css' => [
451 'file' => 'css/pdf-gallery.css',
452 'deps' => ['embedpress-css'],
453 'contexts' => ['frontend', 'elementor', 'editor', 'elementor-editor'],
454 'type' => 'style',
455 'handle' => 'embedpress-pdf-gallery-css',
456 'priority' => 6,
457 'providers' => ['pdf-gallery'],
458 ],
459 'pdf-lightbox-css' => [
460 'file' => 'css/ep-pdf-lightbox.css',
461 'deps' => ['embedpress-css'],
462 'contexts' => ['frontend', 'elementor'],
463 'type' => 'style',
464 'handle' => 'embedpress-pdf-lightbox-css',
465 'priority' => 6,
466 'providers' => ['pdf'],
467 ],
468 'modal-css' => [
469 'file' => 'css/modal.css',
470 'deps' => [],
471 'contexts' => ['editor', 'classic_editor'],
472 'type' => 'style',
473 'handle' => 'embedpress-classic-editor-modal',
474 'priority' => 6,
475 ],
476 'meetup-events-css' => [
477 'file' => 'css/meetup-events.css',
478 'deps' => ['embedpress-css'],
479 'contexts' => ['frontend', 'editor', 'elementor'],
480 'type' => 'style',
481 'handle' => 'embedpress-meetup-events',
482 'providers' => ['meetup'], // Only for Meetup embeds
483 'priority' => 6,
484 ],
485 'settings-icons-css' => [
486 'file' => 'css/settings-icons.css',
487 'deps' => [],
488 'contexts' => ['admin'],
489 'type' => 'style',
490 'handle' => 'embedpress-settings-icons',
491 'priority' => 5,
492 'page' => 'embedpress'
493 ],
494 'settings-css' => [
495 'file' => 'css/settings.css',
496 'deps' => [],
497 'contexts' => ['admin'],
498 'type' => 'style',
499 'handle' => 'embedpress-settings-css',
500 'priority' => 5,
501 'page' => 'embedpress'
502 ],
503 'instagram-shortcode-generator-css' => [
504 'file' => 'css/instagram-shortcode-generator.css',
505 'deps' => [],
506 'contexts' => ['admin'],
507 'type' => 'style',
508 'handle' => 'embedpress-instagram-shortcode-generator',
509 'priority' => 6,
510 'page' => 'embedpress'
511 ],
512 'admin-css' => [
513 'file' => 'css/admin.css',
514 'deps' => [],
515 'contexts' => ['admin'],
516 'type' => 'style',
517 'handle' => 'embedpress-admin-css',
518 'priority' => 5,
519 'page' => 'embedpress'
520 ],
521 'admin-build-css' => [
522 'file' => 'css/admin.build.css',
523 'deps' => [],
524 'contexts' => ['admin'],
525 'type' => 'style',
526 'handle' => 'embedpress-admin-build-css',
527 'priority' => 6,
528 'page' => 'embedpress'
529 ],
530 'onboarding-build-css' => [
531 'file' => 'css/onboarding.build.css',
532 'deps' => [],
533 'contexts' => ['admin'],
534 'type' => 'style',
535 'handle' => 'embedpress-onboarding-build-css',
536 'priority' => 6,
537 'page' => 'embedpress-onboarding'
538 ],
539 'custom-player-build-css' => [
540 'file' => 'css/custom-player.build.css',
541 'deps' => [],
542 'contexts' => ['admin'],
543 'type' => 'style',
544 'handle' => 'embedpress-custom-player-css',
545 'priority' => 6,
546 'page' => 'embedpress-player-engagement'
547 ],
548 ];
549
550 /**
551 * Initialize asset manager
552 */
553 public static function init()
554 {
555 // Register all assets early so they're available as dependencies for Elementor and other plugins
556 add_action('wp_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1);
557 add_action('admin_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1);
558
559 // Use proper priorities to ensure correct load order
560 add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_frontend_assets'], 5);
561 add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_admin_assets'], 5);
562 add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_classic_editor_assets'], 5);
563 add_action('enqueue_block_assets', [__CLASS__, 'enqueue_block_assets'], 5);
564
565
566 add_action('enqueue_block_editor_assets', [__CLASS__, 'enqueue_editor_assets'], 5);
567
568 // Elementor preview (frontend iframe) - enqueue after scripts are enqueued
569 add_action('elementor/frontend/after_enqueue_scripts', [__CLASS__, 'enqueue_elementor_assets'], 5);
570
571 // In Elementor editor, WP_Scripts registry is reset; re-register our assets before enqueuing
572 add_action('elementor/editor/before_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1);
573 // Elementor editor panel (admin) - enqueue after editor scripts are enqueued
574 add_action('elementor/editor/after_enqueue_scripts', [__CLASS__, 'enqueue_elementor_editor_assets'], 5);
575 }
576
577 /**
578 * Register all assets early so they're available as dependencies
579 * This is crucial for Elementor widgets that declare script/style dependencies
580 */
581 public static function register_all_assets()
582 {
583 foreach (self::$assets as $key => $asset) {
584 $file_url = EMBEDPRESS_PLUGIN_DIR_URL . 'assets/' . $asset['file'];
585 $file_path = EMBEDPRESS_PLUGIN_DIR_PATH . '/assets/' . $asset['file'];
586
587 if (!file_exists($file_path)) {
588 continue;
589 }
590
591 $version = filemtime($file_path);
592
593 // Register (not enqueue) all assets
594 if ($asset['type'] === 'script') {
595 wp_register_script(
596 $asset['handle'],
597 $file_url,
598 $asset['deps'],
599 $version,
600 !empty($asset['footer'])
601 );
602
603 // Add module attribute for ES modules (only build files)
604 if (strpos($asset['file'], '.build.js') !== false) {
605 // Track this handle as a module
606 self::$module_handles[] = $asset['handle'];
607
608 // Add the global filter only once
609 if (!self::$module_filter_added) {
610 self::$module_filter_added = true;
611 add_filter('script_loader_tag', [__CLASS__, 'add_module_attribute'], 10, 2);
612 }
613 }
614 } elseif ($asset['type'] === 'style') {
615 wp_register_style(
616 $asset['handle'],
617 $file_url,
618 $asset['deps'],
619 $version,
620 $asset['media'] ?? 'all'
621 );
622 }
623 }
624 }
625
626 /**
627 * Enqueue frontend assets
628 */
629 public static function enqueue_frontend_assets()
630 {
631 self::enqueue_assets_for_context('frontend');
632
633 // Setup frontend localization
634 LocalizationManager::setup_frontend_localization();
635 }
636
637 /**
638 * Enqueue admin assets
639 */
640 public static function enqueue_admin_assets($hook = '')
641 {
642 self::enqueue_assets_for_context('admin', $hook);
643
644 // Load settings assets only on EmbedPress settings pages (not onboarding)
645 $current_page = isset($_GET['page']) ? $_GET['page'] : '';
646 if (strpos($hook, 'embedpress') !== false && $current_page !== 'embedpress-onboarding' && $current_page !== 'embedpress-player-engagement') {
647 self::enqueue_assets_for_context('settings', $hook);
648
649 // Ensure wp-color-picker is loaded for settings page
650 wp_enqueue_style('wp-color-picker');
651
652 // Ensure media scripts are loaded
653 if (!did_action('wp_enqueue_media')) {
654 wp_enqueue_media();
655 }
656 }
657
658 // Setup admin localization
659 LocalizationManager::setup_admin_localization($hook);
660 }
661
662 /**
663 * Enqueue block assets (both editor and frontend)
664 */
665 public static function enqueue_block_assets()
666 {
667 // This runs on both frontend and editor for blocks
668 // For frontend, we don't need the editor scripts
669 if (is_admin()) {
670 self::enqueue_assets_for_context('editor');
671 }
672 }
673
674 /**
675 * Enqueue editor-only assets
676 */
677 public static function enqueue_editor_assets()
678 {
679 // Ensure editor assets are loaded
680 self::enqueue_assets_for_context('editor');
681
682 // Setup editor localization
683 LocalizationManager::setup_editor_localization();
684 }
685
686 public static function enqueue_classic_editor_assets()
687 {
688
689 // Ensure editor assets are loaded
690 self::enqueue_assets_for_context('classic_editor');
691
692 // Setup editor localization
693 LocalizationManager::setup_editor_localization();
694 }
695
696 /**
697 * Enqueue Elementor frontend assets
698 */
699 public static function enqueue_elementor_assets()
700 {
701 self::enqueue_assets_for_context('elementor');
702
703 // Setup Elementor localization
704 LocalizationManager::setup_elementor_localization();
705 }
706
707 /**
708 * Enqueue Elementor editor assets
709 */
710 public static function enqueue_elementor_editor_assets()
711 {
712 // In Elementor editor, load only elementor and elementor-editor contexts
713 // Do NOT load 'editor' context - that's for Gutenberg only
714 self::enqueue_assets_for_context('elementor');
715 self::enqueue_assets_for_context('elementor-editor');
716
717 // Setup Elementor editor localization
718 LocalizationManager::setup_elementor_localization();
719 }
720
721 /**
722 * Enqueue assets for a specific context
723 */
724 private static function enqueue_assets_for_context($context, $hook = '')
725 {
726
727 $assets_to_enqueue = [];
728
729 // Collect assets for this context
730 foreach (self::$assets as $key => $asset) {
731 if (in_array($context, $asset['contexts'])) {
732 // Check if asset has page restriction
733 if (isset($asset['page']) && !empty($asset['page'])) {
734 // Only enqueue if we're on the specified page
735 if (strpos($hook, $asset['page']) !== false) {
736 $assets_to_enqueue[] = array_merge($asset, ['key' => $key]);
737 }
738 // If page doesn't match, don't enqueue this asset
739 } else {
740 // No page restriction, enqueue normally
741 $assets_to_enqueue[] = array_merge($asset, ['key' => $key]);
742 }
743 }
744 }
745
746 // Sort by priority
747 usort($assets_to_enqueue, function ($a, $b) {
748 return $a['priority'] - $b['priority'];
749 });
750
751 // Enqueue assets
752 foreach ($assets_to_enqueue as $asset) {
753 self::enqueue_single_asset($asset);
754 }
755 }
756
757 /**
758 * Enqueue a single asset (assumes asset is already registered)
759 */
760 private static function enqueue_single_asset($asset)
761 {
762 $file_path = EMBEDPRESS_PLUGIN_DIR_PATH . '/assets/' . $asset['file'];
763
764 if (! file_exists($file_path)) {
765 return;
766 }
767
768 // Check if we should load this asset based on current context
769 if (!self::should_load_asset($asset)) {
770 return;
771 }
772
773 // Enqueue the already-registered asset
774 if ($asset['type'] === 'script') {
775 wp_enqueue_script($asset['handle']);
776 } elseif ($asset['type'] === 'style') {
777 wp_enqueue_style($asset['handle']);
778 }
779 }
780
781 /**
782 * Determine if an asset should be loaded based on current context
783 */
784 private static function should_load_asset($asset)
785 {
786 // Check conditional loading requirements first
787 if (isset($asset['condition'])) {
788 if (!self::check_asset_condition($asset['condition'])) {
789 return false;
790 }
791
792 // When a condition like 'custom_player' already passed, skip the
793 // provider check — the condition itself proves these scripts are
794 // needed. Provider detection is fragile (missing URL attrs,
795 // widget-name typos, etc.) and should not block explicitly-enabled
796 // features.
797 if ($asset['condition'] === 'custom_player') {
798 // Provider check not needed; fall through to context check
799 } elseif (isset($asset['providers']) && !empty($asset['providers'])) {
800 if (!self::check_provider_match($asset['providers'])) {
801 return false;
802 }
803 }
804 } elseif (isset($asset['providers']) && !empty($asset['providers'])) {
805 // No condition set — still check providers
806 if (!self::check_provider_match($asset['providers'])) {
807 return false;
808 }
809 }
810
811 // Get current environment state
812 $is_admin = is_admin();
813 $is_elementor_editor = false;
814 $is_elementor_preview = false;
815 $is_gutenberg_editor = false;
816
817 // Check Elementor states
818 if (class_exists('\Elementor\Plugin')) {
819 $elementor = \Elementor\Plugin::$instance;
820
821 if (isset($elementor->editor)) {
822 $is_elementor_editor = $elementor->editor->is_edit_mode();
823 }
824
825 if (isset($elementor->preview)) {
826 $is_elementor_preview = $elementor->preview->is_preview_mode();
827 }
828 }
829
830 // Check if we're in Gutenberg editor
831 if ($is_admin) {
832 global $pagenow;
833 $is_gutenberg_editor = (
834 $pagenow === 'post.php' ||
835 $pagenow === 'post-new.php' ||
836 $pagenow === 'site-editor.php'
837 ) && function_exists('use_block_editor_for_post_type');
838
839 // Check if we're in classic editor (not Gutenberg)
840 $is_classic_editor = false;
841 if ($pagenow === 'post.php' || $pagenow === 'post-new.php') {
842 // Check if classic editor is being used
843 if (
844 isset($_GET['classic-editor']) ||
845 (function_exists('use_block_editor_for_post_type') &&
846 isset($_GET['post']) &&
847 !use_block_editor_for_post_type(get_post_type($_GET['post'])))
848 ) {
849 $is_classic_editor = true;
850 }
851 // Also check if Classic Editor plugin is active and set to classic mode
852 if (
853 class_exists('Classic_Editor') &&
854 get_option('classic-editor-replace') === 'classic'
855 ) {
856 $is_classic_editor = true;
857 }
858 }
859 }
860
861 // Asset loading logic based on contexts
862 foreach ($asset['contexts'] as $context) {
863
864
865 switch ($context) {
866 case 'frontend':
867 // Load on frontend (not in any editor or admin)
868 if (!$is_admin && !$is_elementor_editor && !$is_elementor_preview) {
869 return true;
870 }
871 break;
872
873 case 'admin':
874 // Load in WordPress admin (but not in Elementor editor)
875 if ($is_admin && !$is_elementor_editor && !$is_elementor_preview) {
876 // Check if asset has page restriction
877 if (isset($asset['page'])) {
878 return self::is_embedpress_admin_page($asset['page']);
879 }
880 return true;
881 }
882 break;
883
884 case 'editor':
885 // Load ONLY in Gutenberg editor (not in Elementor editor or other admin pages)
886 if ($is_gutenberg_editor && !$is_elementor_editor) {
887 return true;
888 }
889 break;
890 case 'classic_editor':
891 // Load only in classic editor (TinyMCE)
892 if ($is_classic_editor) {
893 return true;
894 }
895 break;
896 case 'elementor':
897 // Load in Elementor editor, preview, or frontend when Elementor is rendering
898 if ($is_elementor_editor || $is_elementor_preview) {
899 return true;
900 }
901 // Also load on frontend if Elementor content is present
902 if (!$is_admin && self::has_elementor_content()) {
903 return true;
904 }
905 break;
906
907 case 'elementor-editor':
908
909 // Load only in Elementor editor (not preview or frontend)
910 if ($is_elementor_editor) {
911 return true;
912 }
913 break;
914
915 case 'settings':
916 // Load only on EmbedPress settings pages
917 if ($is_admin && !$is_elementor_editor && !$is_elementor_preview) {
918 return true;
919 }
920 break;
921 }
922 }
923
924 // Check if this is an individual block script and if it should be loaded
925 if (strpos($asset['handle'], 'embedpress-block-') === 0) {
926 return self::should_load_individual_block($asset['handle']);
927 }
928
929 return false;
930 }
931
932 /**
933 * Check if we're on an EmbedPress admin page
934 */
935 private static function is_embedpress_admin_page($page_type)
936 {
937 global $pagenow;
938
939 // Get current page
940 $current_page = isset($_GET['page']) ? $_GET['page'] : '';
941
942 switch ($page_type) {
943 case 'embedpress':
944 // Check if we're on any EmbedPress admin page (except onboarding which has its own assets)
945 return (
946 $current_page === 'embedpress' ||
947 (strpos($current_page, 'embedpress') !== false && $current_page !== 'embedpress-onboarding')
948 );
949 case 'embedpress-analytics':
950 return $current_page === 'embedpress-analytics';
951 case 'embedpress-onboarding':
952 return $current_page === 'embedpress-onboarding';
953 case 'embedpress-player-engagement':
954 return $current_page === 'embedpress-player-engagement';
955 default:
956 return false;
957 }
958 }
959
960 /**
961 * Check if individual block should be loaded based on active blocks
962 */
963 private static function should_load_individual_block($handle)
964 {
965 // Get active blocks from settings
966 $elements = (array) get_option(EMBEDPRESS_PLG_NAME . ":elements", []);
967 $active_blocks = isset($elements['gutenberg']) ? (array) $elements['gutenberg'] : [];
968
969 // Map handles to block names
970 $block_map = [
971 'embedpress-block-embedpress' => 'embedpress',
972 'embedpress-block-document' => 'document',
973 'embedpress-block-pdf' => 'embedpress-pdf',
974 'embedpress-block-calendar' => 'embedpress-calendar',
975 'embedpress-block-google-docs' => 'google-docs',
976 'embedpress-block-google-drawings' => 'google-drawings',
977 'embedpress-block-google-forms' => 'google-forms',
978 'embedpress-block-google-maps' => 'google-maps',
979 'embedpress-block-google-sheets' => 'google-sheets',
980 'embedpress-block-google-slides' => 'google-slides',
981 'embedpress-block-twitch' => 'twitch',
982 'embedpress-block-wistia' => 'wistia',
983 'embedpress-block-youtube' => 'youtube'
984 ];
985
986 $block_name = isset($block_map[$handle]) ? $block_map[$handle] : '';
987
988 // If no block name found or no active blocks set, load all blocks (default behavior)
989 if (empty($block_name) || empty($active_blocks)) {
990 return true;
991 }
992
993 // Check if this specific block is active
994 return in_array($block_name, $active_blocks);
995 }
996
997 /**
998 * Check if current page has Elementor content
999 */
1000 private static function has_elementor_content()
1001 {
1002 if (! class_exists('\Elementor\Plugin')) {
1003 return false;
1004 }
1005
1006 if (is_singular()) {
1007 $post_id = get_the_ID();
1008 if (empty($post_id) || ! is_numeric($post_id)) {
1009 return false;
1010 }
1011
1012 $document = \Elementor\Plugin::$instance->documents->get($post_id);
1013
1014 if ($document && method_exists($document, 'is_built_with_elementor')) {
1015 return (bool) $document->is_built_with_elementor();
1016 }
1017 }
1018
1019 return false;
1020 }
1021
1022
1023 /**
1024 * Get asset URL
1025 */
1026 public static function get_asset_url($file)
1027 {
1028 return EMBEDPRESS_PLUGIN_DIR_URL . 'assets/' . $file;
1029 }
1030
1031
1032
1033 /**
1034 * Add module attribute to script tags for ES modules
1035 */
1036 public static function add_module_attribute($tag, $handle)
1037 {
1038 if (in_array($handle, self::$module_handles)) {
1039 // Only add type="module" if it doesn't already exist
1040 if (strpos($tag, 'type="module"') === false) {
1041 return str_replace('<script ', '<script type="module" ', $tag);
1042 }
1043 }
1044 return $tag;
1045 }
1046
1047 /**
1048 * Check if asset exists
1049 */
1050 public static function asset_exists($file)
1051 {
1052 $plugin_path = dirname(dirname(dirname(__DIR__)));
1053 return file_exists($plugin_path . '/assets/' . $file);
1054 }
1055
1056 /**
1057 * Check if an asset condition is met
1058 *
1059 * @param string $condition The condition to check
1060 * @return bool
1061 */
1062 private static function check_asset_condition($condition)
1063 {
1064 switch ($condition) {
1065 case 'custom_player':
1066 return self::is_custom_player_enabled();
1067
1068 case 'has_content':
1069 // In Elementor editor, always load core scripts with has_content condition
1070 // because we can't detect unsaved content
1071 if (class_exists('\Elementor\Plugin')) {
1072 $elementor = \Elementor\Plugin::$instance;
1073 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
1074 return true;
1075 }
1076 }
1077 return self::has_embedpress_content();
1078
1079 case 'lazy_load':
1080 // In Elementor editor, always load lazy load scripts
1081 // because we can't detect unsaved content
1082 if (class_exists('\Elementor\Plugin')) {
1083 $elementor = \Elementor\Plugin::$instance;
1084 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
1085 return true;
1086 }
1087 }
1088 return self::has_lazy_load_enabled();
1089
1090 case 'always':
1091 default:
1092 return true;
1093 }
1094 }
1095
1096 /**
1097 * Check if custom player is enabled on the current page
1098 *
1099 * @return bool
1100 */
1101 private static function is_custom_player_enabled()
1102 {
1103 // Cache the result to avoid multiple checks
1104 if (self::$custom_player_enabled !== null) {
1105 return self::$custom_player_enabled;
1106 }
1107
1108 // In the Gutenberg block editor and Elementor editor, always load
1109 // custom-player scripts so live preview keeps working as the user
1110 // toggles options — the saved post content doesn't yet reflect
1111 // unsaved inspector / widget changes, so the per-post detection
1112 // below would return false and Plyr would never get enqueued.
1113 if (is_admin() && function_exists('get_current_screen')) {
1114 $screen = get_current_screen();
1115 if ($screen && method_exists($screen, 'is_block_editor') && $screen->is_block_editor()) {
1116 self::$custom_player_enabled = true;
1117 return true;
1118 }
1119 }
1120 if (class_exists('\Elementor\Plugin')) {
1121 $elementor = \Elementor\Plugin::$instance;
1122 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
1123 self::$custom_player_enabled = true;
1124 return true;
1125 }
1126 }
1127
1128 global $post;
1129
1130 if (!$post) {
1131 self::$custom_player_enabled = false;
1132 return false;
1133 }
1134
1135 $content = $post->post_content;
1136
1137 // Check for custom player in Gutenberg blocks
1138 if (function_exists('has_blocks') && has_blocks($content)) {
1139 $blocks = parse_blocks($content);
1140 if (self::has_custom_player_in_blocks($blocks)) {
1141 self::$custom_player_enabled = true;
1142 return true;
1143 }
1144 }
1145
1146 // Check for custom player in Elementor
1147 if (class_exists('\Elementor\Plugin')) {
1148 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
1149 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
1150 // Check Elementor meta for custom player settings
1151 $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
1152 if ($elementor_data && is_string($elementor_data) && (strpos($elementor_data, 'emberpress_custom_player') !== false || strpos($elementor_data, '"customPlayer":true') !== false)) {
1153 self::$custom_player_enabled = true;
1154 return true;
1155 }
1156 }
1157 }
1158
1159 // Check for custom player in shortcodes (look for customPlayer attribute)
1160 if (has_shortcode($content, 'embedpress')) {
1161 if (is_string($content) && (strpos($content, 'customPlayer') !== false || strpos($content, 'custom_player') !== false)) {
1162 self::$custom_player_enabled = true;
1163 return true;
1164 }
1165 }
1166
1167 self::$custom_player_enabled = false;
1168 return false;
1169 }
1170
1171 /**
1172 * Check if blocks contain custom player settings
1173 *
1174 * @param array $blocks
1175 * @return bool
1176 */
1177 private static function has_custom_player_in_blocks($blocks)
1178 {
1179 foreach ($blocks as $block) {
1180 // Check if this is an EmbedPress block with custom player enabled
1181 $block_name = $block['blockName'] ?? '';
1182 if ($block_name && strpos($block_name, 'embedpress/') === 0) {
1183 if (isset($block['attrs']['customPlayer']) && $block['attrs']['customPlayer']) {
1184 return true;
1185 }
1186 }
1187
1188 // Recursively check inner blocks
1189 if (!empty($block['innerBlocks'])) {
1190 if (self::has_custom_player_in_blocks($block['innerBlocks'])) {
1191 return true;
1192 }
1193 }
1194 }
1195
1196 return false;
1197 }
1198
1199 /**
1200 * Check if lazy loading is enabled in any embed on the page
1201 *
1202 * @return bool
1203 */
1204 private static function has_lazy_load_enabled()
1205 {
1206 // Check global lazy load setting first - if enabled globally,
1207 // load lazy-load assets whenever EmbedPress content is present
1208 $g_settings = get_option(EMBEDPRESS_PLG_NAME, []);
1209 if (isset($g_settings['g_lazyload']) && $g_settings['g_lazyload'] == 1) {
1210 return self::has_embedpress_content();
1211 }
1212
1213 global $post;
1214
1215 if (!$post) {
1216 return false;
1217 }
1218
1219 $content = $post->post_content;
1220
1221 // Check if post content contains lazy load attributes in blocks
1222 if (function_exists('has_blocks') && has_blocks($content)) {
1223 $blocks = parse_blocks($content);
1224 if (self::has_lazy_load_in_blocks($blocks)) {
1225 return true;
1226 }
1227 }
1228
1229 // Check for Elementor meta (if Elementor is active)
1230 if (class_exists('\Elementor\Plugin')) {
1231 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
1232 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
1233 $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
1234 if ($elementor_data && is_string($elementor_data) && strpos($elementor_data, '"enable_lazy_load":"yes"') !== false) {
1235 return true;
1236 }
1237 }
1238 }
1239
1240 return false;
1241 }
1242
1243 /**
1244 * Check if blocks contain lazy load settings
1245 *
1246 * @param array $blocks
1247 * @return bool
1248 */
1249 private static function has_lazy_load_in_blocks($blocks)
1250 {
1251 foreach ($blocks as $block) {
1252 // Check if this is an EmbedPress block with lazy load enabled
1253 $block_name = $block['blockName'] ?? '';
1254 if ($block_name && strpos($block_name, 'embedpress/') === 0) {
1255 if (isset($block['attrs']['enableLazyLoad']) && $block['attrs']['enableLazyLoad']) {
1256 return true;
1257 }
1258 }
1259
1260 // Recursively check inner blocks
1261 if (!empty($block['innerBlocks'])) {
1262 if (self::has_lazy_load_in_blocks($block['innerBlocks'])) {
1263 return true;
1264 }
1265 }
1266 }
1267
1268 return false;
1269 }
1270
1271
1272 /**
1273 * Check if current page has EmbedPress content
1274 *
1275 * @return bool
1276 */
1277 private static function has_embedpress_content()
1278 {
1279 // Cache the result to avoid multiple checks
1280 if (self::$has_embedpress_content !== null) {
1281 return self::$has_embedpress_content;
1282 }
1283
1284 global $post;
1285
1286 if (!$post) {
1287 self::$has_embedpress_content = false;
1288 return false;
1289 }
1290
1291 $content = $post->post_content;
1292
1293 // Check for EmbedPress shortcodes
1294 if (has_shortcode($content, 'embedpress') || has_shortcode($content, 'embedpress_pdf_gallery')) {
1295 self::$has_embedpress_content = true;
1296 return true;
1297 }
1298
1299 // Check for EmbedPress Gutenberg blocks
1300 $embedpress_blocks = [
1301 'embedpress/embedpress',
1302 'embedpress/google-docs-block',
1303 'embedpress/google-sheets-block',
1304 'embedpress/google-slides-block',
1305 'embedpress/google-forms-block',
1306 'embedpress/google-drawings-block',
1307 'embedpress/google-maps-block',
1308 'embedpress/youtube-block',
1309 'embedpress/vimeo-block',
1310 'embedpress/wistia-block',
1311 'embedpress/twitch-block',
1312 'embedpress/embedpress-pdf',
1313 'embedpress/pdf-gallery',
1314 'embedpress/document',
1315 'embedpress/embedpress-calendar'
1316 ];
1317
1318 foreach ($embedpress_blocks as $block_name) {
1319 if (has_block($block_name, $post)) {
1320 self::$has_embedpress_content = true;
1321 return true;
1322 }
1323 }
1324
1325 // Check for Elementor EmbedPress widgets
1326 if (class_exists('\Elementor\Plugin')) {
1327 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
1328 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
1329 $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
1330 if ($elementor_data && is_string($elementor_data) && (strpos($elementor_data, 'embedpress') !== false || strpos($elementor_data, 'Embedpress') !== false)) {
1331 self::$has_embedpress_content = true;
1332 return true;
1333 }
1334 }
1335 }
1336
1337 self::$has_embedpress_content = false;
1338 return false;
1339 }
1340
1341 /**
1342 * Check if any of the required providers match the detected embed types
1343 *
1344 * @param array $required_providers List of providers this asset needs
1345 * @return bool
1346 */
1347 private static function check_provider_match($required_providers)
1348 {
1349 // In Elementor editor or preview, always load provider scripts to allow live preview
1350 // because we can't detect unsaved widgets from _elementor_data
1351 if (class_exists('\Elementor\Plugin')) {
1352 $elementor = \Elementor\Plugin::$instance;
1353 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
1354 return true;
1355 }
1356 if (isset($elementor->preview) && $elementor->preview->is_preview_mode()) {
1357 return true;
1358 }
1359 }
1360
1361 // In Gutenberg editor, always load provider assets to allow live preview
1362 // because we can't detect unsaved blocks from post_content
1363 if (function_exists('get_current_screen')) {
1364 $screen = get_current_screen();
1365 if ($screen && $screen->is_block_editor()) {
1366 return true;
1367 }
1368 }
1369
1370 $detected_types = self::detect_embed_types();
1371
1372 // If no embeds detected, don't load
1373 if (empty($detected_types)) {
1374 return false;
1375 }
1376
1377 // Check if any required provider matches detected types
1378 foreach ($required_providers as $provider) {
1379 if (in_array($provider, $detected_types)) {
1380 return true;
1381 }
1382 }
1383
1384 return false;
1385 }
1386
1387 /**
1388 * Detect all embed types on the current page
1389 *
1390 * @return array List of detected embed types
1391 */
1392 private static function detect_embed_types()
1393 {
1394 // Cache the result to avoid multiple checks
1395 if (self::$detected_embed_types !== null) {
1396 return self::$detected_embed_types;
1397 }
1398
1399 self::$detected_embed_types = [];
1400
1401 global $post;
1402
1403 if (!$post) {
1404 return self::$detected_embed_types;
1405 }
1406
1407 $content = $post->post_content;
1408
1409 // Detect from Gutenberg blocks
1410 if (function_exists('has_blocks') && has_blocks($content)) {
1411 $blocks = parse_blocks($content);
1412 self::$detected_embed_types = array_merge(
1413 self::$detected_embed_types,
1414 self::detect_types_from_blocks($blocks)
1415 );
1416 }
1417
1418 // Detect from shortcodes
1419 self::$detected_embed_types = array_merge(
1420 self::$detected_embed_types,
1421 self::detect_types_from_shortcodes($content)
1422 );
1423
1424 // Detect from Elementor
1425 if (class_exists('\Elementor\Plugin')) {
1426 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
1427 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
1428 self::$detected_embed_types = array_merge(
1429 self::$detected_embed_types,
1430 self::detect_types_from_elementor($post->ID)
1431 );
1432 }
1433 }
1434
1435 // Remove duplicates
1436 self::$detected_embed_types = array_unique(self::$detected_embed_types);
1437
1438 return self::$detected_embed_types;
1439 }
1440
1441 /**
1442 * Detect embed types from Gutenberg blocks
1443 *
1444 * @param array $blocks
1445 * @return array
1446 */
1447 private static function detect_types_from_blocks($blocks)
1448 {
1449 $types = [];
1450
1451 foreach ($blocks as $block) {
1452 // Map block names to embed types
1453 $block_name = $block['blockName'] ?? '';
1454
1455 if ($block_name && strpos($block_name, 'embedpress/') === 0) {
1456 // Extract type from block name
1457 if ($block_name === 'embedpress/embedpress') {
1458 // Generic block - detect from URL
1459 $url = $block['attrs']['url'] ?? '';
1460 $types = array_merge($types, self::detect_type_from_url($url));
1461 } elseif ($block_name === 'embedpress/embedpress-pdf') {
1462 $types[] = 'pdf';
1463 } elseif ($block_name === 'embedpress/pdf-gallery') {
1464 $types[] = 'pdf-gallery';
1465 $types[] = 'pdf';
1466 } elseif ($block_name === 'embedpress/document') {
1467 $types[] = 'document';
1468 } elseif ($block_name === 'embedpress/youtube-block') {
1469 $types[] = 'youtube';
1470 } elseif ($block_name === 'embedpress/vimeo-block') {
1471 $types[] = 'vimeo';
1472 } elseif ($block_name === 'embedpress/google-docs-block') {
1473 $types[] = 'google-docs';
1474 } elseif ($block_name === 'embedpress/google-sheets-block') {
1475 $types[] = 'google-sheets';
1476 } elseif ($block_name === 'embedpress/google-slides-block') {
1477 $types[] = 'google-slides';
1478 } elseif ($block_name === 'embedpress/wistia-block') {
1479 $types[] = 'wistia';
1480 } elseif ($block_name === 'embedpress/twitch-block') {
1481 $types[] = 'twitch';
1482 }
1483 }
1484
1485 // Recursively check inner blocks
1486 if (!empty($block['innerBlocks'])) {
1487 $types = array_merge($types, self::detect_types_from_blocks($block['innerBlocks']));
1488 }
1489 }
1490
1491 return $types;
1492 }
1493
1494 /**
1495 * Detect embed types from shortcodes
1496 *
1497 * @param string $content
1498 * @return array
1499 */
1500 private static function detect_types_from_shortcodes($content)
1501 {
1502 $types = [];
1503
1504 if (!is_string($content)) {
1505 return $types;
1506 }
1507
1508 // Find all embedpress shortcodes with URL attribute (with or without quotes)
1509 // Matches: [embedpress url="..."], [embedpress url='...'], [embedpress url=...]
1510 if (preg_match_all('/\[embedpress[^\]]*url=["\']?([^"\'\s\]]+)["\']?[^\]]*\]/i', $content, $matches)) {
1511 foreach ($matches[1] as $url) {
1512 $types = array_merge($types, self::detect_type_from_url($url));
1513 }
1514 }
1515
1516 // Detect PDF gallery shortcode
1517 if (has_shortcode($content, 'embedpress_pdf_gallery')) {
1518 $types[] = 'pdf-gallery';
1519 $types[] = 'pdf';
1520 }
1521
1522 // Find embedpress shortcodes with URL between tags
1523 // Matches: [embedpress]URL[/embedpress]
1524 if (preg_match_all('/\[embedpress[^\]]*\]([^\[]+)\[\/embedpress\]/i', $content, $matches)) {
1525 foreach ($matches[1] as $url) {
1526 $url = trim($url);
1527 if (!empty($url)) {
1528 $types = array_merge($types, self::detect_type_from_url($url));
1529 }
1530 }
1531 }
1532
1533 return $types;
1534 }
1535
1536 /**
1537 * Detect embed types from Elementor
1538 *
1539 * @param int $post_id
1540 * @return array
1541 */
1542 private static function detect_types_from_elementor($post_id)
1543 {
1544 $types = [];
1545 $elementor_data = get_post_meta($post_id, '_elementor_data', true);
1546
1547 if (!$elementor_data || !is_string($elementor_data)) {
1548 return $types;
1549 }
1550
1551 // Decode JSON data
1552 $data = json_decode($elementor_data, true);
1553 if (!$data || !is_array($data)) {
1554 return $types;
1555 }
1556
1557 // Recursively search for EmbedPress widgets
1558 $types = self::detect_types_from_elementor_data($data);
1559
1560 return $types;
1561 }
1562
1563 /**
1564 * Recursively detect types from Elementor data
1565 *
1566 * @param array $data
1567 * @return array
1568 */
1569 private static function detect_types_from_elementor_data($data)
1570 {
1571 $types = [];
1572
1573 if (!is_array($data)) {
1574 return $types;
1575 }
1576
1577 foreach ($data as $element) {
1578 if (!is_array($element)) {
1579 continue;
1580 }
1581
1582 // Check if this is an EmbedPress widget
1583 // Note: widget name is 'embedpres_elementor' (legacy typo without double 's')
1584 $widget_type = $element['widgetType'] ?? '';
1585 if ($widget_type && (strpos($widget_type, 'embedpres') !== false || strpos($widget_type, 'Embedpress') !== false)) {
1586 // Get the embed source
1587 $settings = $element['settings'] ?? [];
1588 $source = $settings['embedpress_pro_embeded_source'] ?? '';
1589 $url = $settings['embedpress_embeded_link'] ?? '';
1590
1591 if ($source) {
1592 $types[] = $source;
1593 } elseif ($url) {
1594 $types = array_merge($types, self::detect_type_from_url($url));
1595 }
1596
1597 // Detect PDF widget specifically (uses different setting names)
1598 if ($widget_type === 'embedpress_pdf') {
1599 $pdf_url = $settings['embedpress_pdf_Uploader']['url'] ?? '';
1600 $pdf_link = $settings['embedpress_pdf_file_link']['url'] ?? '';
1601 if ($pdf_url || $pdf_link) {
1602 $types[] = 'pdf';
1603 }
1604 }
1605 }
1606
1607 // Recursively check elements
1608 if (isset($element['elements'])) {
1609 $types = array_merge($types, self::detect_types_from_elementor_data($element['elements']));
1610 }
1611 }
1612
1613 return $types;
1614 }
1615
1616 /**
1617 * Detect embed type from URL using Embera's provider detection
1618 *
1619 * @param string $url
1620 * @return array
1621 */
1622 private static function detect_type_from_url($url)
1623 {
1624 $types = [];
1625
1626 if (empty($url) || !is_string($url)) {
1627 return $types;
1628 }
1629
1630 // Use Helper class which leverages Embera's built-in provider detection
1631 if (class_exists('\EmbedPress\Includes\Classes\Helper')) {
1632 $provider_name = Helper::get_provider_name($url);
1633
1634 if (!empty($provider_name)) {
1635 // Normalize provider name to lowercase for consistency
1636 $provider_name = strtolower($provider_name);
1637
1638 // Map provider names to asset provider keys
1639 $provider_map = [
1640 'youtube' => 'youtube',
1641 'youtubechannel' => 'youtube-channel',
1642 'vimeo' => 'vimeo',
1643 'instagram' => 'instagram',
1644 'instagramfeed' => 'instagram',
1645 'opensea' => 'opensea',
1646 'wistia' => 'wistia',
1647 'twitch' => 'twitch',
1648 'meetup' => 'meetup',
1649 'googledocs' => 'google-docs',
1650 'googlesheets' => 'google-sheets',
1651 'googleslides' => 'google-slides',
1652 ];
1653
1654 // Check if provider name matches our map
1655 if (isset($provider_map[$provider_name])) {
1656 $types[] = $provider_map[$provider_name];
1657 return $types;
1658 }
1659
1660 // Check for document types from Helper's response
1661 if (strpos($provider_name, 'document_') === 0) {
1662 $types[] = 'document';
1663 return $types;
1664 }
1665 }
1666 }
1667
1668 // Fallback to manual detection for special cases not handled by Embera
1669 $url_lower = strtolower($url);
1670
1671 // YouTube special cases (channel, live, shorts)
1672 if (strpos($url_lower, 'youtube.com') !== false || strpos($url_lower, 'youtu.be') !== false) {
1673 if (preg_match('#/(channel|c|user)/[\w-]+/live$|/@[\w-]+/live$#i', $url_lower)) {
1674 $types[] = 'youtube-live';
1675 } elseif (strpos($url_lower, '/channel/') !== false || strpos($url_lower, '/c/') !== false || strpos($url_lower, '/@') !== false) {
1676 $types[] = 'youtube-channel';
1677 } elseif (strpos($url_lower, '/live') !== false) {
1678 $types[] = 'youtube-live';
1679 } elseif (strpos($url_lower, '/shorts/') !== false) {
1680 $types[] = 'youtube-shorts';
1681 } else {
1682 $types[] = 'youtube';
1683 }
1684 }
1685 // PDF detection
1686 elseif (preg_match('/\.pdf$/i', $url)) {
1687 $types[] = 'pdf';
1688 }
1689 // Document detection
1690 elseif (preg_match('/\.(doc|docx|ppt|pptx|xls|xlsx)$/i', $url)) {
1691 $types[] = 'document';
1692 }
1693 // Self-hosted video
1694 elseif (preg_match('/\.(mp4|mov|avi|wmv|flv|mkv|webm|mpeg|mpg)$/i', $url)) {
1695 $types[] = 'video';
1696 }
1697 // Self-hosted audio
1698 elseif (preg_match('/\.(mp3|wav|ogg|aac)$/i', $url)) {
1699 $types[] = 'audio';
1700 }
1701
1702 return $types;
1703 }
1704 }
1705