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