PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 4.4.4
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v4.4.4
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 7 months ago LocalizationManager.php 7 months ago init.php 9 months ago
AssetManager.php
1426 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
10 /**
11 * EmbedPress Asset Manager
12 *
13 * Centralized asset management for JS/CSS files across blocks, Elementor, admin, and frontend
14 * Handles new build files from Vite build system
15 */
16 class AssetManager
17 {
18 /**
19 * Track which handles should be treated as ES modules
20 */
21 private static $module_handles = [];
22
23 /**
24 * Track if the global module filter has been added
25 */
26 private static $module_filter_added = false;
27
28 /**
29 * Cache for content detection to avoid multiple checks
30 */
31 private static $has_embedpress_content = null;
32
33 /**
34 * Cache for custom player detection
35 */
36 private static $custom_player_enabled = null;
37
38 /**
39 * Cache for detected embed types on current page
40 */
41 private static $detected_embed_types = null;
42
43 /**
44 * Asset definitions with context-based loading
45 */
46 private static $assets = [
47
48 // 🔧 Scripts (Ordered by Priority)
49 // ----------------------------------
50
51 // Vendor assets (copied to assets folder for consistency)
52 // Priority 1-5: Core vendor libraries
53 'plyr-css' => [
54 'file' => 'css/plyr.css',
55 'deps' => [],
56 'contexts' => ['frontend', 'elementor', 'editor'],
57 'type' => 'style',
58 'handle' => 'embedpress-plyr-css',
59 'priority' => 1,
60 'condition' => 'custom_player', // Only load if custom player is enabled
61 'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers
62 ],
63 'carousel-vendor-css' => [
64 'file' => 'css/carousel.min.css',
65 'deps' => [],
66 'contexts' => ['frontend', 'elementor'],
67 'type' => 'style',
68 'handle' => 'embedpress-carousel-vendor-css',
69 'priority' => 1,
70 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
71 ],
72 'glider-css' => [
73 'file' => 'css/glider.min.css',
74 'deps' => [],
75 'contexts' => ['frontend', 'elementor'],
76 'type' => 'style',
77 'handle' => 'embedpress-glider-css',
78 'priority' => 1,
79 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
80 ],
81 'plyr-js' => [
82 'file' => 'js/vendor/plyr.js',
83 'deps' => ['jquery'],
84 'contexts' => ['frontend', 'elementor', 'editor'],
85 'type' => 'script',
86 'footer' => true,
87 'handle' => 'embedpress-plyr',
88 'priority' => 2,
89 'condition' => 'custom_player', // Only load if custom player is enabled
90 'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers
91 ],
92 'carousel-vendor-js' => [
93 'file' => 'js/vendor/carousel.min.js',
94 'deps' => ['jquery'],
95 'contexts' => ['frontend', 'elementor'],
96 'type' => 'script',
97 'footer' => true,
98 'handle' => 'embedpress-carousel-vendor',
99 'priority' => 2,
100 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
101 ],
102 'glider-js' => [
103 'file' => 'js/vendor/glider.min.js',
104 'deps' => ['jquery'],
105 'contexts' => ['frontend', 'elementor'],
106 'type' => 'script',
107 'footer' => true,
108 'handle' => 'embedpress-glider',
109 'priority' => 2,
110 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
111 ],
112 'pdfobject-js' => [
113 'file' => 'js/vendor/pdfobject.js',
114 'deps' => [],
115 'contexts' => ['frontend', 'elementor', 'editor'],
116 'type' => 'script',
117 'footer' => true,
118 'handle' => 'embedpress-pdfobject',
119 'priority' => 2,
120 'providers' => ['pdf', 'document'], // Only for PDF/document embeds
121 ],
122 'vimeo-player-js' => [
123 'file' => 'js/vendor/vimeo-player.js',
124 'deps' => [],
125 'contexts' => ['frontend', 'elementor'],
126 'type' => 'script',
127 'footer' => true,
128 'handle' => 'embedpress-vimeo-player',
129 'priority' => 2,
130 'providers' => ['vimeo'], // Only for Vimeo embeds
131 ],
132 'ytiframeapi-js' => [
133 'file' => 'js/vendor/ytiframeapi.js',
134 'deps' => [],
135 'contexts' => ['frontend', 'elementor'],
136 'type' => 'script',
137 'footer' => true,
138 'handle' => 'embedpress-ytiframeapi',
139 'priority' => 2,
140 'providers' => ['youtube', 'youtube-channel', 'youtube-live', 'youtube-shorts'], // Only for YouTube embeds
141 ],
142
143 // Priority 5-6: Main application build assets
144 'admin-js' => [
145 'file' => 'js/admin.build.js',
146 'deps' => [],
147 'contexts' => ['admin'],
148 'type' => 'script',
149 'footer' => true,
150 'handle' => 'embedpress-admin',
151 'priority' => 5,
152 'page' => 'embedpress'
153 ],
154 // Priority 7-10: Blocks
155 'blocks-js' => [
156 'file' => 'js/blocks.build.js',
157 'deps' => ['wp-blocks', 'wp-i18n', 'wp-element', 'wp-api-fetch', 'wp-is-shallow-equal', 'wp-editor', 'wp-components'],
158 'contexts' => ['editor'],
159 'type' => 'script',
160 'footer' => true,
161 'handle' => 'embedpress-blocks-editor',
162 'priority' => 10,
163 ],
164 'blocks-editor-style' => [
165 'file' => 'css/blocks.build.css',
166 'deps' => [],
167 'contexts' => ['editor'],
168 'type' => 'style',
169 'handle' => 'embedpress-blocks-editor-style',
170 'priority' => 10,
171 ],
172 'blocks-style' => [
173 'file' => 'css/blocks.build.css',
174 'deps' => [],
175 'contexts' => ['frontend', 'editor'],
176 'type' => 'style',
177 'handle' => 'embedpress-blocks-style',
178 'priority' => 10,
179 ],
180
181 // Priority 15-20: Legacy JS files
182 'admin-legacy-js' => [
183 'file' => 'js/admin.js',
184 'deps' => ['jquery'],
185 'contexts' => ['admin'],
186 'type' => 'script',
187 'footer' => true,
188 'handle' => 'embedpress-admin-legacy',
189 'priority' => 15,
190 'page' => 'embedpress'
191 ],
192 'ads-js' => [
193 'file' => 'js/sponsored.js',
194 'deps' => ['jquery', 'embedpress-front'],
195 'contexts' => ['editor', 'frontend', 'elementor'],
196 'type' => 'script',
197 'footer' => true,
198 'handle' => 'embedpress-ads',
199 'priority' => 16,
200 'condition' => 'has_content', // Load for any EmbedPress content (ads can be on any embed)
201 ],
202 'analytics-tracker-js' => [
203 'file' => 'js/analytics-tracker.js',
204 'deps' => ['jquery'],
205 'contexts' => ['frontend', 'elementor'],
206 'type' => 'script',
207 'footer' => true,
208 'handle' => 'embedpress-analytics-tracker',
209 'priority' => 15,
210 'condition' => 'has_content', // Load for any EmbedPress content (analytics track all embeds)
211 ],
212 'carousel-js' => [
213 'file' => 'js/carousel.js',
214 'deps' => ['jquery', 'embedpress-carousel-vendor'],
215 'contexts' => ['frontend', 'elementor'],
216 'type' => 'script',
217 'footer' => true,
218 'handle' => 'embedpress-carousel',
219 'priority' => 15,
220 'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds
221 ],
222 'documents-viewer-js' => [
223 'file' => 'js/documents-viewer-script.js',
224 'deps' => ['jquery'],
225 'contexts' => ['frontend', 'elementor'],
226 'type' => 'script',
227 'footer' => true,
228 'handle' => 'embedpress-documents-viewer',
229 'priority' => 15,
230 'providers' => ['document', 'google-docs', 'google-sheets', 'google-slides'], // Only for document embeds
231 ],
232 'front-js' => [
233 'file' => 'js/front.js',
234 'deps' => ['jquery'],
235 'contexts' => ['frontend', 'editor', 'elementor'],
236 'type' => 'script',
237 'footer' => true,
238 'handle' => 'embedpress-front',
239 'priority' => 15,
240 'condition' => 'has_content', // Core script - load for any EmbedPress content
241 ],
242 'gallery-justify-js' => [
243 'file' => 'js/gallery-justify.js',
244 'deps' => ['jquery'],
245 'contexts' => ['editor', 'frontend', 'elementor'],
246 'type' => 'script',
247 'footer' => true,
248 'handle' => 'embedpress-gallery-justify',
249 'priority' => 15,
250 'providers' => ['google-photos', 'instagram'], // Only for gallery-based embeds
251 ],
252 'gutenberg-script-js' => [
253 'file' => 'js/gutneberg-script.js',
254 'deps' => ['wp-blocks', 'wp-element'],
255 'contexts' => ['editor'],
256 'type' => 'script',
257 'footer' => true,
258 'handle' => 'embedpress-gutenberg-script',
259 'priority' => 15,
260 ],
261 'init-plyr-js' => [
262 'file' => 'js/initplyr.js',
263 'deps' => ['jquery', 'embedpress-plyr'],
264 'contexts' => ['frontend', 'elementor'],
265 'type' => 'script',
266 'footer' => true,
267 'handle' => 'embedpress-init-plyr',
268 'priority' => 15,
269 'condition' => 'custom_player', // Only load if custom player is enabled
270 'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers
271 ],
272 'instafeed-js' => [
273 'file' => 'js/instafeed.js',
274 'deps' => ['jquery'],
275 'contexts' => ['frontend', 'elementor'],
276 'type' => 'script',
277 'footer' => true,
278 'handle' => 'embedpress-instafeed',
279 'priority' => 15,
280 'providers' => ['instagram'], // Only for Instagram embeds
281 ],
282 'license-js' => [
283 'file' => 'js/license.js',
284 'deps' => ['jquery'],
285 'contexts' => ['admin'],
286 'type' => 'script',
287 'footer' => true,
288 'handle' => 'embedpress-license',
289 'priority' => 15,
290 'page' => 'embedpress'
291 ],
292 'feature-notices-js' => [
293 'file' => 'js/feature-notices.js',
294 'deps' => ['jquery'],
295 'contexts' => ['admin'],
296 'type' => 'script',
297 'footer' => true,
298 'handle' => 'embedpress-feature-notices',
299 'priority' => 15,
300 // 'page' => 'embedpress'
301 ],
302 'preview-js' => [
303 'file' => 'js/preview.js',
304 'deps' => ['jquery'],
305 'contexts' => ['classic_editor'],
306 'type' => 'script',
307 'footer' => true,
308 'handle' => 'embedpress-preview',
309 'priority' => 15,
310 ],
311 'settings-js' => [
312 'file' => 'js/settings.js',
313 'deps' => ['jquery', 'wp-color-picker'],
314 'contexts' => ['admin'],
315 'type' => 'script',
316 'footer' => true,
317 'handle' => 'embedpress-settings',
318 'priority' => 15,
319 'page' => 'embedpress'
320 ],
321
322 // 🎨 Styles (Ordered by Priority)
323 // ----------------------------------
324
325 // Build CSS files (analytics.build.css is handled by Analytics.php)
326
327 // Legacy CSS files
328 'admin-notices-css' => [
329 'file' => 'css/admin-notices.css',
330 'deps' => [],
331 'contexts' => ['admin'],
332 'type' => 'style',
333 'handle' => 'embedpress-admin-notices',
334 'priority' => 5,
335 // 'page' => 'embedpress'
336 ],
337 'feature-notices-css' => [
338 'file' => 'css/feature-notices.css',
339 'deps' => [],
340 'contexts' => ['admin'],
341 'type' => 'style',
342 'handle' => 'embedpress-feature-notices',
343 'priority' => 5,
344 ],
345
346 'el-icon-css' => [
347 'file' => 'css/el-icon.css',
348 'deps' => [],
349 'contexts' => ['elementor-editor'],
350 'type' => 'style',
351 'handle' => 'embedpress-el-icon',
352 'priority' => 5,
353 ],
354 'embedpress-elementor-css' => [
355 'file' => 'css/embedpress-elementor.css',
356 'deps' => [],
357 'contexts' => ['elementor'],
358 'type' => 'style',
359 'handle' => 'embedpress-elementor-css',
360 'priority' => 5,
361 ],
362 'embedpress-css' => [
363 'file' => 'css/embedpress.css',
364 'deps' => [],
365 'contexts' => ['editor', 'frontend', 'elementor'],
366 'type' => 'style',
367 'handle' => 'embedpress-css',
368 'priority' => 5,
369 ],
370 'modal-css' => [
371 'file' => 'css/modal.css',
372 'deps' => [],
373 'contexts' => ['editor', 'classic_editor'],
374 'type' => 'style',
375 'handle' => 'embedpress-classic-editor-modal',
376 'priority' => 6,
377 ],
378 'meetup-events-css' => [
379 'file' => 'css/meetup-events.css',
380 'deps' => ['embedpress-css'],
381 'contexts' => ['frontend', 'editor', 'elementor'],
382 'type' => 'style',
383 'handle' => 'embedpress-meetup-events',
384 'providers' => ['meetup'], // Only for Meetup embeds
385 'priority' => 6,
386 ],
387 'settings-icons-css' => [
388 'file' => 'css/settings-icons.css',
389 'deps' => [],
390 'contexts' => ['admin'],
391 'type' => 'style',
392 'handle' => 'embedpress-settings-icons',
393 'priority' => 5,
394 'page' => 'embedpress'
395 ],
396 'settings-css' => [
397 'file' => 'css/settings.css',
398 'deps' => [],
399 'contexts' => ['admin'],
400 'type' => 'style',
401 'handle' => 'embedpress-settings-css',
402 'priority' => 5,
403 'page' => 'embedpress'
404 ],
405 'admin-css' => [
406 'file' => 'css/admin.css',
407 'deps' => [],
408 'contexts' => ['admin'],
409 'type' => 'style',
410 'handle' => 'embedpress-admin-css',
411 'priority' => 5,
412 'page' => 'embedpress'
413 ],
414 ];
415
416 /**
417 * Initialize asset manager
418 */
419 public static function init()
420 {
421 // Register all assets early so they're available as dependencies for Elementor and other plugins
422 add_action('wp_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1);
423 add_action('admin_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1);
424
425 // Use proper priorities to ensure correct load order
426 add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_frontend_assets'], 5);
427 add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_admin_assets'], 5);
428 add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_classic_editor_assets'], 5);
429 add_action('enqueue_block_assets', [__CLASS__, 'enqueue_block_assets'], 5);
430
431
432 add_action('enqueue_block_editor_assets', [__CLASS__, 'enqueue_editor_assets'], 5);
433
434 // Elementor preview (frontend iframe) - enqueue after scripts are enqueued
435 add_action('elementor/frontend/after_enqueue_scripts', [__CLASS__, 'enqueue_elementor_assets'], 5);
436
437 // In Elementor editor, WP_Scripts registry is reset; re-register our assets before enqueuing
438 add_action('elementor/editor/before_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1);
439 // Elementor editor panel (admin) - enqueue after editor scripts are enqueued
440 add_action('elementor/editor/after_enqueue_scripts', [__CLASS__, 'enqueue_elementor_editor_assets'], 5);
441 }
442
443 /**
444 * Register all assets early so they're available as dependencies
445 * This is crucial for Elementor widgets that declare script/style dependencies
446 */
447 public static function register_all_assets()
448 {
449 foreach (self::$assets as $key => $asset) {
450 $file_url = EMBEDPRESS_PLUGIN_DIR_URL . 'assets/' . $asset['file'];
451 $file_path = EMBEDPRESS_PLUGIN_DIR_PATH . '/assets/' . $asset['file'];
452
453 if (!file_exists($file_path)) {
454 continue;
455 }
456
457 $version = filemtime($file_path);
458
459 // Register (not enqueue) all assets
460 if ($asset['type'] === 'script') {
461 wp_register_script(
462 $asset['handle'],
463 $file_url,
464 $asset['deps'],
465 $version,
466 !empty($asset['footer'])
467 );
468
469 // Add module attribute for ES modules (only build files)
470 if (strpos($asset['file'], '.build.js') !== false) {
471 // Track this handle as a module
472 self::$module_handles[] = $asset['handle'];
473
474 // Add the global filter only once
475 if (!self::$module_filter_added) {
476 self::$module_filter_added = true;
477 add_filter('script_loader_tag', [__CLASS__, 'add_module_attribute'], 10, 2);
478 }
479 }
480 } elseif ($asset['type'] === 'style') {
481 wp_register_style(
482 $asset['handle'],
483 $file_url,
484 $asset['deps'],
485 $version,
486 $asset['media'] ?? 'all'
487 );
488 }
489 }
490 }
491
492 /**
493 * Enqueue frontend assets
494 */
495 public static function enqueue_frontend_assets()
496 {
497 self::enqueue_assets_for_context('frontend');
498
499 // Setup frontend localization
500 LocalizationManager::setup_frontend_localization();
501 }
502
503 /**
504 * Enqueue admin assets
505 */
506 public static function enqueue_admin_assets($hook = '')
507 {
508 self::enqueue_assets_for_context('admin', $hook);
509
510 // Load settings assets only on EmbedPress settings pages
511 if (strpos($hook, 'embedpress') !== false) {
512 self::enqueue_assets_for_context('settings', $hook);
513
514 // Ensure wp-color-picker is loaded for settings page
515 wp_enqueue_style('wp-color-picker');
516
517 // Ensure media scripts are loaded
518 if (!did_action('wp_enqueue_media')) {
519 wp_enqueue_media();
520 }
521 }
522
523 // Setup admin localization
524 LocalizationManager::setup_admin_localization($hook);
525 }
526
527 /**
528 * Enqueue block assets (both editor and frontend)
529 */
530 public static function enqueue_block_assets()
531 {
532 // This runs on both frontend and editor for blocks
533 // For frontend, we don't need the editor scripts
534 if (is_admin()) {
535 self::enqueue_assets_for_context('editor');
536 }
537 }
538
539 /**
540 * Enqueue editor-only assets
541 */
542 public static function enqueue_editor_assets()
543 {
544 // Ensure editor assets are loaded
545 self::enqueue_assets_for_context('editor');
546
547 // Setup editor localization
548 LocalizationManager::setup_editor_localization();
549 }
550
551 public static function enqueue_classic_editor_assets()
552 {
553
554 // Ensure editor assets are loaded
555 self::enqueue_assets_for_context('classic_editor');
556
557 // Setup editor localization
558 LocalizationManager::setup_editor_localization();
559 }
560
561 /**
562 * Enqueue Elementor frontend assets
563 */
564 public static function enqueue_elementor_assets()
565 {
566 self::enqueue_assets_for_context('elementor');
567
568 // Setup Elementor localization
569 LocalizationManager::setup_elementor_localization();
570 }
571
572 /**
573 * Enqueue Elementor editor assets
574 */
575 public static function enqueue_elementor_editor_assets()
576 {
577 // In Elementor editor, load only elementor and elementor-editor contexts
578 // Do NOT load 'editor' context - that's for Gutenberg only
579 self::enqueue_assets_for_context('elementor');
580 self::enqueue_assets_for_context('elementor-editor');
581
582 // Setup Elementor editor localization
583 LocalizationManager::setup_elementor_localization();
584 }
585
586 /**
587 * Enqueue assets for a specific context
588 */
589 private static function enqueue_assets_for_context($context, $hook = '')
590 {
591
592 $assets_to_enqueue = [];
593
594 // Collect assets for this context
595 foreach (self::$assets as $key => $asset) {
596 if (in_array($context, $asset['contexts'])) {
597 // Check if asset has page restriction
598 if (isset($asset['page']) && !empty($asset['page'])) {
599 // Only enqueue if we're on the specified page
600 if (strpos($hook, $asset['page']) !== false) {
601 $assets_to_enqueue[] = array_merge($asset, ['key' => $key]);
602 }
603 // If page doesn't match, don't enqueue this asset
604 } else {
605 // No page restriction, enqueue normally
606 $assets_to_enqueue[] = array_merge($asset, ['key' => $key]);
607 }
608 }
609 }
610
611 // Sort by priority
612 usort($assets_to_enqueue, function ($a, $b) {
613 return $a['priority'] - $b['priority'];
614 });
615
616 // Enqueue assets
617 foreach ($assets_to_enqueue as $asset) {
618 self::enqueue_single_asset($asset);
619 }
620 }
621
622 /**
623 * Enqueue a single asset (assumes asset is already registered)
624 */
625 private static function enqueue_single_asset($asset)
626 {
627 $file_path = EMBEDPRESS_PLUGIN_DIR_PATH . '/assets/' . $asset['file'];
628
629 if (! file_exists($file_path)) {
630 return;
631 }
632
633 // Check if we should load this asset based on current context
634 if (!self::should_load_asset($asset)) {
635 return;
636 }
637
638 // Enqueue the already-registered asset
639 if ($asset['type'] === 'script') {
640 wp_enqueue_script($asset['handle']);
641 } elseif ($asset['type'] === 'style') {
642 wp_enqueue_style($asset['handle']);
643 }
644 }
645
646 /**
647 * Determine if an asset should be loaded based on current context
648 */
649 private static function should_load_asset($asset)
650 {
651 // Check conditional loading requirements first
652 if (isset($asset['condition'])) {
653 if (!self::check_asset_condition($asset['condition'])) {
654 return false;
655 }
656 }
657
658 // Check provider-specific loading
659 if (isset($asset['providers']) && !empty($asset['providers'])) {
660 if (!self::check_provider_match($asset['providers'])) {
661 return false;
662 }
663 }
664
665 // Get current environment state
666 $is_admin = is_admin();
667 $is_elementor_editor = false;
668 $is_elementor_preview = false;
669 $is_gutenberg_editor = false;
670
671 // Check Elementor states
672 if (class_exists('\Elementor\Plugin')) {
673 $elementor = \Elementor\Plugin::$instance;
674
675 if (isset($elementor->editor)) {
676 $is_elementor_editor = $elementor->editor->is_edit_mode();
677 }
678
679 if (isset($elementor->preview)) {
680 $is_elementor_preview = $elementor->preview->is_preview_mode();
681 }
682 }
683
684 // Check if we're in Gutenberg editor
685 if ($is_admin) {
686 global $pagenow;
687 $is_gutenberg_editor = (
688 $pagenow === 'post.php' ||
689 $pagenow === 'post-new.php' ||
690 $pagenow === 'site-editor.php'
691 ) && function_exists('use_block_editor_for_post_type');
692
693 // Check if we're in classic editor (not Gutenberg)
694 $is_classic_editor = false;
695 if ($pagenow === 'post.php' || $pagenow === 'post-new.php') {
696 // Check if classic editor is being used
697 if (
698 isset($_GET['classic-editor']) ||
699 (function_exists('use_block_editor_for_post_type') &&
700 isset($_GET['post']) &&
701 !use_block_editor_for_post_type(get_post_type($_GET['post'])))
702 ) {
703 $is_classic_editor = true;
704 }
705 // Also check if Classic Editor plugin is active and set to classic mode
706 if (
707 class_exists('Classic_Editor') &&
708 get_option('classic-editor-replace') === 'classic'
709 ) {
710 $is_classic_editor = true;
711 }
712 }
713 }
714
715 // Asset loading logic based on contexts
716 foreach ($asset['contexts'] as $context) {
717
718
719 switch ($context) {
720 case 'frontend':
721 // Load on frontend (not in any editor or admin)
722 if (!$is_admin && !$is_elementor_editor && !$is_elementor_preview) {
723 return true;
724 }
725 break;
726
727 case 'admin':
728 // Load in WordPress admin (but not in Elementor editor)
729 if ($is_admin && !$is_elementor_editor && !$is_elementor_preview) {
730 // Check if asset has page restriction
731 if (isset($asset['page'])) {
732 return self::is_embedpress_admin_page($asset['page']);
733 }
734 return true;
735 }
736 break;
737
738 case 'editor':
739 // Load ONLY in Gutenberg editor (not in Elementor editor or other admin pages)
740 if ($is_gutenberg_editor && !$is_elementor_editor) {
741 return true;
742 }
743 break;
744 case 'classic_editor':
745 // Load only in classic editor (TinyMCE)
746 if ($is_classic_editor) {
747 return true;
748 }
749 break;
750 case 'elementor':
751 // Load in Elementor editor, preview, or frontend when Elementor is rendering
752 if ($is_elementor_editor || $is_elementor_preview) {
753 return true;
754 }
755 // Also load on frontend if Elementor content is present
756 if (!$is_admin && self::has_elementor_content()) {
757 return true;
758 }
759 break;
760
761 case 'elementor-editor':
762
763 // Load only in Elementor editor (not preview or frontend)
764 if ($is_elementor_editor) {
765 return true;
766 }
767 break;
768
769 case 'settings':
770 // Load only on EmbedPress settings pages
771 if ($is_admin && !$is_elementor_editor && !$is_elementor_preview) {
772 return true;
773 }
774 break;
775 }
776 }
777
778 // Check if this is an individual block script and if it should be loaded
779 if (strpos($asset['handle'], 'embedpress-block-') === 0) {
780 return self::should_load_individual_block($asset['handle']);
781 }
782
783 return false;
784 }
785
786 /**
787 * Check if we're on an EmbedPress admin page
788 */
789 private static function is_embedpress_admin_page($page_type)
790 {
791 global $pagenow;
792
793 // Get current page
794 $current_page = isset($_GET['page']) ? $_GET['page'] : '';
795
796 switch ($page_type) {
797 case 'embedpress':
798 // Check if we're on any EmbedPress admin page
799 return (
800 strpos($current_page, 'embedpress') !== false ||
801 $pagenow === 'admin.php' && strpos($current_page, 'embedpress') !== false
802 );
803 case 'embedpress-analytics':
804 return $current_page === 'embedpress-analytics';
805 default:
806 return false;
807 }
808 }
809
810 /**
811 * Check if individual block should be loaded based on active blocks
812 */
813 private static function should_load_individual_block($handle)
814 {
815 // Get active blocks from settings
816 $elements = (array) get_option(EMBEDPRESS_PLG_NAME . ":elements", []);
817 $active_blocks = isset($elements['gutenberg']) ? (array) $elements['gutenberg'] : [];
818
819 // Map handles to block names
820 $block_map = [
821 'embedpress-block-embedpress' => 'embedpress',
822 'embedpress-block-document' => 'document',
823 'embedpress-block-pdf' => 'embedpress-pdf',
824 'embedpress-block-calendar' => 'embedpress-calendar',
825 'embedpress-block-google-docs' => 'google-docs',
826 'embedpress-block-google-drawings' => 'google-drawings',
827 'embedpress-block-google-forms' => 'google-forms',
828 'embedpress-block-google-maps' => 'google-maps',
829 'embedpress-block-google-sheets' => 'google-sheets',
830 'embedpress-block-google-slides' => 'google-slides',
831 'embedpress-block-twitch' => 'twitch',
832 'embedpress-block-wistia' => 'wistia',
833 'embedpress-block-youtube' => 'youtube'
834 ];
835
836 $block_name = isset($block_map[$handle]) ? $block_map[$handle] : '';
837
838 // If no block name found or no active blocks set, load all blocks (default behavior)
839 if (empty($block_name) || empty($active_blocks)) {
840 return true;
841 }
842
843 // Check if this specific block is active
844 return in_array($block_name, $active_blocks);
845 }
846
847 /**
848 * Check if current page has Elementor content
849 */
850 private static function has_elementor_content()
851 {
852 if (! class_exists('\Elementor\Plugin')) {
853 return false;
854 }
855
856 if (is_singular()) {
857 $post_id = get_the_ID();
858 if (empty($post_id) || ! is_numeric($post_id)) {
859 return false;
860 }
861
862 $document = \Elementor\Plugin::$instance->documents->get($post_id);
863
864 if ($document && method_exists($document, 'is_built_with_elementor')) {
865 return (bool) $document->is_built_with_elementor();
866 }
867 }
868
869 return false;
870 }
871
872
873 /**
874 * Get asset URL
875 */
876 public static function get_asset_url($file)
877 {
878 return EMBEDPRESS_PLUGIN_DIR_URL . 'assets/' . $file;
879 }
880
881
882
883 /**
884 * Add module attribute to script tags for ES modules
885 */
886 public static function add_module_attribute($tag, $handle)
887 {
888 if (in_array($handle, self::$module_handles)) {
889 // Only add type="module" if it doesn't already exist
890 if (strpos($tag, 'type="module"') === false) {
891 return str_replace('<script ', '<script type="module" ', $tag);
892 }
893 }
894 return $tag;
895 }
896
897 /**
898 * Check if asset exists
899 */
900 public static function asset_exists($file)
901 {
902 $plugin_path = dirname(dirname(dirname(__DIR__)));
903 return file_exists($plugin_path . '/assets/' . $file);
904 }
905
906 /**
907 * Check if an asset condition is met
908 *
909 * @param string $condition The condition to check
910 * @return bool
911 */
912 private static function check_asset_condition($condition)
913 {
914 switch ($condition) {
915 case 'custom_player':
916 return self::is_custom_player_enabled();
917
918 case 'has_content':
919 // In Elementor editor, always load core scripts with has_content condition
920 // because we can't detect unsaved content
921 if (class_exists('\Elementor\Plugin')) {
922 $elementor = \Elementor\Plugin::$instance;
923 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
924 return true;
925 }
926 }
927 return self::has_embedpress_content();
928
929 case 'always':
930 default:
931 return true;
932 }
933 }
934
935 /**
936 * Check if custom player is enabled on the current page
937 *
938 * @return bool
939 */
940 private static function is_custom_player_enabled()
941 {
942 // Cache the result to avoid multiple checks
943 if (self::$custom_player_enabled !== null) {
944 return self::$custom_player_enabled;
945 }
946
947 // In Elementor editor, always load custom player scripts to allow live preview
948 // because we can't detect unsaved widget settings
949 if (class_exists('\Elementor\Plugin')) {
950 $elementor = \Elementor\Plugin::$instance;
951 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
952 self::$custom_player_enabled = true;
953 return true;
954 }
955 }
956
957 global $post;
958
959 if (!$post) {
960 self::$custom_player_enabled = false;
961 return false;
962 }
963
964 $content = $post->post_content;
965
966 // Check for custom player in Gutenberg blocks
967 if (function_exists('has_blocks') && has_blocks($content)) {
968 $blocks = parse_blocks($content);
969 if (self::has_custom_player_in_blocks($blocks)) {
970 self::$custom_player_enabled = true;
971 return true;
972 }
973 }
974
975 // Check for custom player in Elementor
976 if (class_exists('\Elementor\Plugin')) {
977 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
978 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
979 // Check Elementor meta for custom player settings
980 $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
981 if ($elementor_data && is_string($elementor_data) && (strpos($elementor_data, 'emberpress_custom_player') !== false || strpos($elementor_data, '"customPlayer":true') !== false)) {
982 self::$custom_player_enabled = true;
983 return true;
984 }
985 }
986 }
987
988 // Check for custom player in shortcodes (look for customPlayer attribute)
989 if (has_shortcode($content, 'embedpress')) {
990 if (is_string($content) && (strpos($content, 'customPlayer') !== false || strpos($content, 'custom_player') !== false)) {
991 self::$custom_player_enabled = true;
992 return true;
993 }
994 }
995
996 self::$custom_player_enabled = false;
997 return false;
998 }
999
1000 /**
1001 * Check if blocks contain custom player settings
1002 *
1003 * @param array $blocks
1004 * @return bool
1005 */
1006 private static function has_custom_player_in_blocks($blocks)
1007 {
1008 foreach ($blocks as $block) {
1009 // Check if this is an EmbedPress block with custom player enabled
1010 $block_name = $block['blockName'] ?? '';
1011 if ($block_name && strpos($block_name, 'embedpress/') === 0) {
1012 if (isset($block['attrs']['customPlayer']) && $block['attrs']['customPlayer']) {
1013 return true;
1014 }
1015 }
1016
1017 // Recursively check inner blocks
1018 if (!empty($block['innerBlocks'])) {
1019 if (self::has_custom_player_in_blocks($block['innerBlocks'])) {
1020 return true;
1021 }
1022 }
1023 }
1024
1025 return false;
1026 }
1027
1028 /**
1029 * Check if current page has EmbedPress content
1030 *
1031 * @return bool
1032 */
1033 private static function has_embedpress_content()
1034 {
1035 // Cache the result to avoid multiple checks
1036 if (self::$has_embedpress_content !== null) {
1037 return self::$has_embedpress_content;
1038 }
1039
1040 global $post;
1041
1042 if (!$post) {
1043 self::$has_embedpress_content = false;
1044 return false;
1045 }
1046
1047 $content = $post->post_content;
1048
1049 // Check for EmbedPress shortcodes
1050 if (has_shortcode($content, 'embedpress')) {
1051 self::$has_embedpress_content = true;
1052 return true;
1053 }
1054
1055 // Check for EmbedPress Gutenberg blocks
1056 $embedpress_blocks = [
1057 'embedpress/embedpress',
1058 'embedpress/google-docs-block',
1059 'embedpress/google-sheets-block',
1060 'embedpress/google-slides-block',
1061 'embedpress/google-forms-block',
1062 'embedpress/google-drawings-block',
1063 'embedpress/google-maps-block',
1064 'embedpress/youtube-block',
1065 'embedpress/vimeo-block',
1066 'embedpress/wistia-block',
1067 'embedpress/twitch-block',
1068 'embedpress/embedpress-pdf',
1069 'embedpress/document',
1070 'embedpress/embedpress-calendar'
1071 ];
1072
1073 foreach ($embedpress_blocks as $block_name) {
1074 if (has_block($block_name, $post)) {
1075 self::$has_embedpress_content = true;
1076 return true;
1077 }
1078 }
1079
1080 // Check for Elementor EmbedPress widgets
1081 if (class_exists('\Elementor\Plugin')) {
1082 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
1083 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
1084 $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
1085 if ($elementor_data && is_string($elementor_data) && (strpos($elementor_data, 'embedpress') !== false || strpos($elementor_data, 'Embedpress') !== false)) {
1086 self::$has_embedpress_content = true;
1087 return true;
1088 }
1089 }
1090 }
1091
1092 self::$has_embedpress_content = false;
1093 return false;
1094 }
1095
1096 /**
1097 * Check if any of the required providers match the detected embed types
1098 *
1099 * @param array $required_providers List of providers this asset needs
1100 * @return bool
1101 */
1102 private static function check_provider_match($required_providers)
1103 {
1104 // In Elementor editor, always load provider scripts to allow live preview
1105 // because we can't detect unsaved widgets from _elementor_data
1106 if (class_exists('\Elementor\Plugin')) {
1107 $elementor = \Elementor\Plugin::$instance;
1108 if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) {
1109 return true;
1110 }
1111 }
1112
1113 $detected_types = self::detect_embed_types();
1114
1115 // If no embeds detected, don't load
1116 if (empty($detected_types)) {
1117 return false;
1118 }
1119
1120 // Check if any required provider matches detected types
1121 foreach ($required_providers as $provider) {
1122 if (in_array($provider, $detected_types)) {
1123 return true;
1124 }
1125 }
1126
1127 return false;
1128 }
1129
1130 /**
1131 * Detect all embed types on the current page
1132 *
1133 * @return array List of detected embed types
1134 */
1135 private static function detect_embed_types()
1136 {
1137 // Cache the result to avoid multiple checks
1138 if (self::$detected_embed_types !== null) {
1139 return self::$detected_embed_types;
1140 }
1141
1142 self::$detected_embed_types = [];
1143
1144 global $post;
1145
1146 if (!$post) {
1147 return self::$detected_embed_types;
1148 }
1149
1150 $content = $post->post_content;
1151
1152 // Detect from Gutenberg blocks
1153 if (function_exists('has_blocks') && has_blocks($content)) {
1154 $blocks = parse_blocks($content);
1155 self::$detected_embed_types = array_merge(
1156 self::$detected_embed_types,
1157 self::detect_types_from_blocks($blocks)
1158 );
1159 }
1160
1161 // Detect from shortcodes
1162 self::$detected_embed_types = array_merge(
1163 self::$detected_embed_types,
1164 self::detect_types_from_shortcodes($content)
1165 );
1166
1167 // Detect from Elementor
1168 if (class_exists('\Elementor\Plugin')) {
1169 $document = \Elementor\Plugin::$instance->documents->get($post->ID);
1170 if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) {
1171 self::$detected_embed_types = array_merge(
1172 self::$detected_embed_types,
1173 self::detect_types_from_elementor($post->ID)
1174 );
1175 }
1176 }
1177
1178 // Remove duplicates
1179 self::$detected_embed_types = array_unique(self::$detected_embed_types);
1180
1181 return self::$detected_embed_types;
1182 }
1183
1184 /**
1185 * Detect embed types from Gutenberg blocks
1186 *
1187 * @param array $blocks
1188 * @return array
1189 */
1190 private static function detect_types_from_blocks($blocks)
1191 {
1192 $types = [];
1193
1194 foreach ($blocks as $block) {
1195 // Map block names to embed types
1196 $block_name = $block['blockName'] ?? '';
1197
1198 if ($block_name && strpos($block_name, 'embedpress/') === 0) {
1199 // Extract type from block name
1200 if ($block_name === 'embedpress/embedpress') {
1201 // Generic block - detect from URL
1202 $url = $block['attrs']['url'] ?? '';
1203 $types = array_merge($types, self::detect_type_from_url($url));
1204 } elseif ($block_name === 'embedpress/embedpress-pdf') {
1205 $types[] = 'pdf';
1206 } elseif ($block_name === 'embedpress/document') {
1207 $types[] = 'document';
1208 } elseif ($block_name === 'embedpress/youtube-block') {
1209 $types[] = 'youtube';
1210 } elseif ($block_name === 'embedpress/vimeo-block') {
1211 $types[] = 'vimeo';
1212 } elseif ($block_name === 'embedpress/google-docs-block') {
1213 $types[] = 'google-docs';
1214 } elseif ($block_name === 'embedpress/google-sheets-block') {
1215 $types[] = 'google-sheets';
1216 } elseif ($block_name === 'embedpress/google-slides-block') {
1217 $types[] = 'google-slides';
1218 } elseif ($block_name === 'embedpress/wistia-block') {
1219 $types[] = 'wistia';
1220 } elseif ($block_name === 'embedpress/twitch-block') {
1221 $types[] = 'twitch';
1222 }
1223 }
1224
1225 // Recursively check inner blocks
1226 if (!empty($block['innerBlocks'])) {
1227 $types = array_merge($types, self::detect_types_from_blocks($block['innerBlocks']));
1228 }
1229 }
1230
1231 return $types;
1232 }
1233
1234 /**
1235 * Detect embed types from shortcodes
1236 *
1237 * @param string $content
1238 * @return array
1239 */
1240 private static function detect_types_from_shortcodes($content)
1241 {
1242 $types = [];
1243
1244 if (!is_string($content)) {
1245 return $types;
1246 }
1247
1248 // Find all embedpress shortcodes with URL attribute (with or without quotes)
1249 // Matches: [embedpress url="..."], [embedpress url='...'], [embedpress url=...]
1250 if (preg_match_all('/\[embedpress[^\]]*url=["\']?([^"\'\s\]]+)["\']?[^\]]*\]/i', $content, $matches)) {
1251 foreach ($matches[1] as $url) {
1252 $types = array_merge($types, self::detect_type_from_url($url));
1253 }
1254 }
1255
1256 // Find embedpress shortcodes with URL between tags
1257 // Matches: [embedpress]URL[/embedpress]
1258 if (preg_match_all('/\[embedpress[^\]]*\]([^\[]+)\[\/embedpress\]/i', $content, $matches)) {
1259 foreach ($matches[1] as $url) {
1260 $url = trim($url);
1261 if (!empty($url)) {
1262 $types = array_merge($types, self::detect_type_from_url($url));
1263 }
1264 }
1265 }
1266
1267 return $types;
1268 }
1269
1270 /**
1271 * Detect embed types from Elementor
1272 *
1273 * @param int $post_id
1274 * @return array
1275 */
1276 private static function detect_types_from_elementor($post_id)
1277 {
1278 $types = [];
1279 $elementor_data = get_post_meta($post_id, '_elementor_data', true);
1280
1281 if (!$elementor_data || !is_string($elementor_data)) {
1282 return $types;
1283 }
1284
1285 // Decode JSON data
1286 $data = json_decode($elementor_data, true);
1287 if (!$data || !is_array($data)) {
1288 return $types;
1289 }
1290
1291 // Recursively search for EmbedPress widgets
1292 $types = self::detect_types_from_elementor_data($data);
1293
1294 return $types;
1295 }
1296
1297 /**
1298 * Recursively detect types from Elementor data
1299 *
1300 * @param array $data
1301 * @return array
1302 */
1303 private static function detect_types_from_elementor_data($data)
1304 {
1305 $types = [];
1306
1307 if (!is_array($data)) {
1308 return $types;
1309 }
1310
1311 foreach ($data as $element) {
1312 if (!is_array($element)) {
1313 continue;
1314 }
1315
1316 // Check if this is an EmbedPress widget
1317 $widget_type = $element['widgetType'] ?? '';
1318 if ($widget_type && (strpos($widget_type, 'embedpress') !== false || strpos($widget_type, 'Embedpress') !== false)) {
1319 // Get the embed source
1320 $settings = $element['settings'] ?? [];
1321 $source = $settings['embedpress_pro_embeded_source'] ?? '';
1322 $url = $settings['embedpress_embeded_link'] ?? '';
1323
1324 if ($source) {
1325 $types[] = $source;
1326 } elseif ($url) {
1327 $types = array_merge($types, self::detect_type_from_url($url));
1328 }
1329 }
1330
1331 // Recursively check elements
1332 if (isset($element['elements'])) {
1333 $types = array_merge($types, self::detect_types_from_elementor_data($element['elements']));
1334 }
1335 }
1336
1337 return $types;
1338 }
1339
1340 /**
1341 * Detect embed type from URL using Embera's provider detection
1342 *
1343 * @param string $url
1344 * @return array
1345 */
1346 private static function detect_type_from_url($url)
1347 {
1348 $types = [];
1349
1350 if (empty($url) || !is_string($url)) {
1351 return $types;
1352 }
1353
1354 // Use Helper class which leverages Embera's built-in provider detection
1355 if (class_exists('\EmbedPress\Includes\Classes\Helper')) {
1356 $provider_name = \EmbedPress\Includes\Classes\Helper::get_provider_name($url);
1357
1358 if (!empty($provider_name)) {
1359 // Normalize provider name to lowercase for consistency
1360 $provider_name = strtolower($provider_name);
1361
1362 // Map provider names to asset provider keys
1363 $provider_map = [
1364 'youtube' => 'youtube',
1365 'youtubechannel' => 'youtube-channel',
1366 'vimeo' => 'vimeo',
1367 'instagram' => 'instagram',
1368 'opensea' => 'opensea',
1369 'wistia' => 'wistia',
1370 'twitch' => 'twitch',
1371 'meetup' => 'meetup',
1372 'googledocs' => 'google-docs',
1373 'googlesheets' => 'google-sheets',
1374 'googleslides' => 'google-slides',
1375 ];
1376
1377 // Check if provider name matches our map
1378 if (isset($provider_map[$provider_name])) {
1379 $types[] = $provider_map[$provider_name];
1380 return $types;
1381 }
1382
1383 // Check for document types from Helper's response
1384 if (strpos($provider_name, 'document_') === 0) {
1385 $types[] = 'document';
1386 return $types;
1387 }
1388 }
1389 }
1390
1391 // Fallback to manual detection for special cases not handled by Embera
1392 $url_lower = strtolower($url);
1393
1394 // YouTube special cases (channel, live, shorts)
1395 if (strpos($url_lower, 'youtube.com') !== false || strpos($url_lower, 'youtu.be') !== false) {
1396 if (strpos($url_lower, '/channel/') !== false || strpos($url_lower, '/c/') !== false || strpos($url_lower, '/@') !== false) {
1397 $types[] = 'youtube-channel';
1398 } elseif (strpos($url_lower, '/live') !== false) {
1399 $types[] = 'youtube-live';
1400 } elseif (strpos($url_lower, '/shorts/') !== false) {
1401 $types[] = 'youtube-shorts';
1402 } else {
1403 $types[] = 'youtube';
1404 }
1405 }
1406 // PDF detection
1407 elseif (preg_match('/\.pdf$/i', $url)) {
1408 $types[] = 'pdf';
1409 }
1410 // Document detection
1411 elseif (preg_match('/\.(doc|docx|ppt|pptx|xls|xlsx)$/i', $url)) {
1412 $types[] = 'document';
1413 }
1414 // Self-hosted video
1415 elseif (preg_match('/\.(mp4|mov|avi|wmv|flv|mkv|webm|mpeg|mpg)$/i', $url)) {
1416 $types[] = 'video';
1417 }
1418 // Self-hosted audio
1419 elseif (preg_match('/\.(mp3|wav|ogg|aac)$/i', $url)) {
1420 $types[] = 'audio';
1421 }
1422
1423 return $types;
1424 }
1425 }
1426