| 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' => ['wp-i18n'], |
| 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' => ['wp-i18n'], |
| 168 |
'contexts' => ['admin'], |
| 169 |
'type' => 'script', |
| 170 |
'footer' => true, |
| 171 |
'handle' => 'embedpress-custom-player', |
| 172 |
'priority' => 5, |
| 173 |
'page' => 'embedpress-player-engagement' |
| 174 |
], |
| 175 |
'google-reviews-js' => [ |
| 176 |
'file' => 'js/google-reviews.build.js', |
| 177 |
'deps' => [], |
| 178 |
'contexts' => ['admin'], |
| 179 |
'type' => 'script', |
| 180 |
'footer' => true, |
| 181 |
'handle' => 'embedpress-google-reviews-admin', |
| 182 |
'priority' => 5, |
| 183 |
'page' => 'embedpress-google-reviews' |
| 184 |
], |
| 185 |
'google-reviews-css' => [ |
| 186 |
'file' => 'css/google-reviews.build.css', |
| 187 |
'deps' => [], |
| 188 |
'contexts' => ['admin'], |
| 189 |
'type' => 'style', |
| 190 |
'handle' => 'embedpress-google-reviews-admin-css', |
| 191 |
'priority' => 5, |
| 192 |
'page' => 'embedpress-google-reviews' |
| 193 |
], |
| 194 |
// GR FRONTEND assets (the block/shortcode render CSS + read-more JS). |
| 195 |
// Registered at priority 1 (register_all_assets) so the handle |
| 196 |
// `embedpress-google-reviews` ALWAYS exists early — Pro's |
| 197 |
// `embedpress-google-reviews-pro` style declares it as a dependency on |
| 198 |
// `wp_enqueue_scripts`, which fires before the block renders. Previously |
| 199 |
// this was registered lazily inside GoogleReviewsRenderer at render time, |
| 200 |
// so Pro's dependency was "not registered" on the frontend → WP 6.9.1 |
| 201 |
// "dependencies that are not registered" notice. `has_content` keeps the |
| 202 |
// actual ENQUEUE gated to pages that have a GR block/shortcode. |
| 203 |
'google-reviews-frontend-css' => [ |
| 204 |
'file' => 'css/google-reviews.css', |
| 205 |
'deps' => [], |
| 206 |
'contexts' => ['frontend', 'editor', 'elementor'], |
| 207 |
'type' => 'style', |
| 208 |
'handle' => 'embedpress-google-reviews', |
| 209 |
'priority' => 1, |
| 210 |
'condition' => 'has_content', |
| 211 |
], |
| 212 |
'google-reviews-frontend-js' => [ |
| 213 |
'file' => 'js/google-reviews.js', |
| 214 |
'deps' => [], |
| 215 |
'contexts' => ['frontend', 'editor', 'elementor'], |
| 216 |
'type' => 'script', |
| 217 |
'footer' => true, |
| 218 |
'handle' => 'embedpress-google-reviews', |
| 219 |
'priority' => 1, |
| 220 |
'condition' => 'has_content', |
| 221 |
], |
| 222 |
// Priority 7-10: Blocks |
| 223 |
'blocks-js' => [ |
| 224 |
'file' => 'js/blocks.build.js', |
| 225 |
// wp-hooks is required: blocks call wp.hooks.applyFilters() to expose |
| 226 |
// Pro extension slots (e.g. Google Reviews Pro controls). Without it, |
| 227 |
// load order isn't guaranteed and Pro's addFilter() can register after |
| 228 |
// the block first renders, leaving the free upsell placeholder showing. |
| 229 |
'deps' => ['wp-blocks', 'wp-i18n', 'wp-element', 'wp-api-fetch', 'wp-is-shallow-equal', 'wp-editor', 'wp-components', 'wp-hooks'], |
| 230 |
'contexts' => ['editor'], |
| 231 |
'type' => 'script', |
| 232 |
'footer' => true, |
| 233 |
'handle' => 'embedpress-blocks-editor', |
| 234 |
'priority' => 10, |
| 235 |
], |
| 236 |
'blocks-editor-style' => [ |
| 237 |
'file' => 'css/blocks.build.css', |
| 238 |
'deps' => [], |
| 239 |
'contexts' => ['editor'], |
| 240 |
'type' => 'style', |
| 241 |
'handle' => 'embedpress-blocks-editor-style', |
| 242 |
'priority' => 10, |
| 243 |
], |
| 244 |
'blocks-style' => [ |
| 245 |
'file' => 'css/blocks.build.css', |
| 246 |
'deps' => [], |
| 247 |
'contexts' => ['frontend', 'editor'], |
| 248 |
'type' => 'style', |
| 249 |
'handle' => 'embedpress-blocks-style', |
| 250 |
'priority' => 10, |
| 251 |
], |
| 252 |
'lazy-load-css' => [ |
| 253 |
'file' => 'css/lazy-load.css', |
| 254 |
'deps' => [], |
| 255 |
'contexts' => ['frontend', 'elementor'], |
| 256 |
'type' => 'style', |
| 257 |
'handle' => 'embedpress-lazy-load-css', |
| 258 |
'priority' => 10, |
| 259 |
'condition' => 'lazy_load', |
| 260 |
], |
| 261 |
|
| 262 |
// Priority 15-20: Legacy JS files |
| 263 |
'admin-legacy-js' => [ |
| 264 |
'file' => 'js/admin.js', |
| 265 |
'deps' => ['jquery'], |
| 266 |
'contexts' => ['admin'], |
| 267 |
'type' => 'script', |
| 268 |
'footer' => true, |
| 269 |
'handle' => 'embedpress-admin-legacy', |
| 270 |
'priority' => 15, |
| 271 |
'page' => 'embedpress' |
| 272 |
], |
| 273 |
'ads-js' => [ |
| 274 |
'file' => 'js/sponsored.js', |
| 275 |
'deps' => ['jquery', 'embedpress-front'], |
| 276 |
'contexts' => ['editor', 'frontend', 'elementor'], |
| 277 |
'type' => 'script', |
| 278 |
'footer' => true, |
| 279 |
'handle' => 'embedpress-ads', |
| 280 |
'priority' => 16, |
| 281 |
'condition' => 'has_content', // Load for any EmbedPress content (ads can be on any embed) |
| 282 |
], |
| 283 |
'lazy-load-js' => [ |
| 284 |
'file' => 'js/lazy-load.js', |
| 285 |
'deps' => [], |
| 286 |
'contexts' => ['frontend', 'elementor'], |
| 287 |
'type' => 'script', |
| 288 |
'footer' => true, |
| 289 |
'handle' => 'embedpress-lazy-load', |
| 290 |
'priority' => 16, |
| 291 |
'condition' => 'lazy_load', |
| 292 |
], |
| 293 |
'analytics-tracker-js' => [ |
| 294 |
'file' => 'js/analytics-tracker.js', |
| 295 |
'deps' => ['jquery'], |
| 296 |
'contexts' => ['frontend', 'elementor'], |
| 297 |
'type' => 'script', |
| 298 |
'footer' => true, |
| 299 |
'handle' => 'embedpress-analytics-tracker', |
| 300 |
'priority' => 15, |
| 301 |
'condition' => 'analytics_enabled', // Only when the analytics tracking toggle is on AND EmbedPress content exists |
| 302 |
], |
| 303 |
'carousel-js' => [ |
| 304 |
'file' => 'js/carousel.js', |
| 305 |
'deps' => ['jquery', 'embedpress-carousel-vendor'], |
| 306 |
'contexts' => ['frontend', 'elementor'], |
| 307 |
'type' => 'script', |
| 308 |
'footer' => true, |
| 309 |
'handle' => 'embedpress-carousel', |
| 310 |
'priority' => 15, |
| 311 |
'providers' => ['youtube-channel', 'youtube-live', 'instagram', 'opensea'], // Only for carousel-based embeds |
| 312 |
], |
| 313 |
'documents-viewer-js' => [ |
| 314 |
'file' => 'js/documents-viewer-script.js', |
| 315 |
'deps' => ['jquery'], |
| 316 |
'contexts' => ['frontend', 'elementor'], |
| 317 |
'type' => 'script', |
| 318 |
'footer' => true, |
| 319 |
'handle' => 'embedpress-documents-viewer', |
| 320 |
'priority' => 15, |
| 321 |
'providers' => ['document', 'google-docs', 'google-sheets', 'google-slides'], // Only for document embeds |
| 322 |
], |
| 323 |
'front-js' => [ |
| 324 |
'file' => 'js/front.js', |
| 325 |
'deps' => ['jquery', 'embedpress-carousel-vendor'], |
| 326 |
'contexts' => ['frontend', 'editor', 'elementor'], |
| 327 |
'type' => 'script', |
| 328 |
'footer' => true, |
| 329 |
'handle' => 'embedpress-front', |
| 330 |
'priority' => 15, |
| 331 |
'condition' => 'has_content', // Core script - load for any EmbedPress content |
| 332 |
], |
| 333 |
'yt-queue-js' => [ |
| 334 |
'file' => 'js/ep-yt-queue.js', |
| 335 |
'deps' => [], |
| 336 |
'contexts' => ['frontend', 'editor', 'elementor'], |
| 337 |
'type' => 'script', |
| 338 |
'footer' => true, |
| 339 |
'handle' => 'embedpress-yt-queue', |
| 340 |
'priority' => 15, |
| 341 |
'condition' => 'has_content', // Lightweight; loads with any EmbedPress content |
| 342 |
], |
| 343 |
'gallery-justify-js' => [ |
| 344 |
'file' => 'js/gallery-justify.js', |
| 345 |
'deps' => ['jquery'], |
| 346 |
'contexts' => ['editor', 'frontend', 'elementor'], |
| 347 |
'type' => 'script', |
| 348 |
'footer' => true, |
| 349 |
'handle' => 'embedpress-gallery-justify', |
| 350 |
'priority' => 15, |
| 351 |
'providers' => ['google-photos'], |
| 352 |
], |
| 353 |
'pdf-gallery-js' => [ |
| 354 |
'file' => 'js/pdf-gallery.js', |
| 355 |
'deps' => ['jquery'], |
| 356 |
'contexts' => ['frontend', 'elementor', 'elementor-editor'], |
| 357 |
'type' => 'script', |
| 358 |
'footer' => true, |
| 359 |
'handle' => 'embedpress-pdf-gallery', |
| 360 |
'priority' => 15, |
| 361 |
'providers' => ['pdf-gallery'], |
| 362 |
], |
| 363 |
'pdf-lightbox-js' => [ |
| 364 |
'file' => 'js/ep-pdf-lightbox.js', |
| 365 |
'deps' => [], |
| 366 |
'contexts' => ['frontend', 'elementor'], |
| 367 |
'type' => 'script', |
| 368 |
'footer' => true, |
| 369 |
'handle' => 'embedpress-pdf-lightbox', |
| 370 |
'priority' => 15, |
| 371 |
'providers' => ['pdf'], |
| 372 |
], |
| 373 |
'meetup-timezone-js' => [ |
| 374 |
'file' => 'js/meetup-timezone.js', |
| 375 |
'deps' => [], |
| 376 |
'contexts' => ['frontend', 'elementor'], |
| 377 |
'type' => 'script', |
| 378 |
'footer' => true, |
| 379 |
'handle' => 'embedpress-meetup-timezone', |
| 380 |
'priority' => 15, |
| 381 |
'providers' => ['meetup'], // Only for Meetup embeds |
| 382 |
], |
| 383 |
'gutenberg-script-js' => [ |
| 384 |
'file' => 'js/gutneberg-script.js', |
| 385 |
'deps' => ['wp-blocks', 'wp-element'], |
| 386 |
'contexts' => ['editor'], |
| 387 |
'type' => 'script', |
| 388 |
'footer' => true, |
| 389 |
'handle' => 'embedpress-gutenberg-script', |
| 390 |
'priority' => 15, |
| 391 |
], |
| 392 |
'init-plyr-js' => [ |
| 393 |
'file' => 'js/initplyr.js', |
| 394 |
'deps' => ['jquery', 'embedpress-plyr'], |
| 395 |
'contexts' => ['editor', 'frontend', 'elementor'], |
| 396 |
'type' => 'script', |
| 397 |
'footer' => true, |
| 398 |
'handle' => 'embedpress-init-plyr', |
| 399 |
'priority' => 15, |
| 400 |
'condition' => 'custom_player', // Only load if custom player is enabled |
| 401 |
'providers' => ['youtube', 'vimeo', 'video', 'audio'], // Only for these providers |
| 402 |
], |
| 403 |
'instafeed-js' => [ |
| 404 |
'file' => 'js/instafeed.js', |
| 405 |
'deps' => ['jquery'], |
| 406 |
'contexts' => ['frontend', 'elementor'], |
| 407 |
'type' => 'script', |
| 408 |
'footer' => true, |
| 409 |
'handle' => 'embedpress-instafeed', |
| 410 |
'priority' => 15, |
| 411 |
'providers' => ['instagram'], // Only for Instagram embeds |
| 412 |
], |
| 413 |
'license-js' => [ |
| 414 |
'file' => 'js/license.js', |
| 415 |
'deps' => ['jquery', 'wp-url'], |
| 416 |
'contexts' => ['admin'], |
| 417 |
'type' => 'script', |
| 418 |
'footer' => true, |
| 419 |
'handle' => 'embedpress-license', |
| 420 |
'priority' => 15, |
| 421 |
'page' => 'embedpress' |
| 422 |
], |
| 423 |
'feature-notices-js' => [ |
| 424 |
'file' => 'js/feature-notices.js', |
| 425 |
'deps' => ['jquery'], |
| 426 |
'contexts' => ['admin'], |
| 427 |
'type' => 'script', |
| 428 |
'footer' => true, |
| 429 |
'handle' => 'embedpress-feature-notices', |
| 430 |
'priority' => 15, |
| 431 |
// 'page' => 'embedpress' |
| 432 |
], |
| 433 |
'preview-js' => [ |
| 434 |
'file' => 'js/preview.js', |
| 435 |
'deps' => ['jquery'], |
| 436 |
'contexts' => ['classic_editor'], |
| 437 |
'type' => 'script', |
| 438 |
'footer' => true, |
| 439 |
'handle' => 'embedpress-preview', |
| 440 |
'priority' => 15, |
| 441 |
], |
| 442 |
'settings-js' => [ |
| 443 |
'file' => 'js/settings.js', |
| 444 |
'deps' => ['jquery', 'wp-color-picker'], |
| 445 |
'contexts' => ['admin'], |
| 446 |
'type' => 'script', |
| 447 |
'footer' => true, |
| 448 |
'handle' => 'embedpress-settings', |
| 449 |
'priority' => 15, |
| 450 |
'page' => 'embedpress' |
| 451 |
], |
| 452 |
'instagram-shortcode-generator-js' => [ |
| 453 |
'file' => 'js/instagram-shortcode-generator.js', |
| 454 |
'deps' => [], |
| 455 |
'contexts' => ['admin'], |
| 456 |
'type' => 'script', |
| 457 |
'footer' => true, |
| 458 |
'handle' => 'embedpress-instagram-shortcode-generator', |
| 459 |
'priority' => 15, |
| 460 |
'page' => 'embedpress' |
| 461 |
], |
| 462 |
|
| 463 |
// 🎨 Styles (Ordered by Priority) |
| 464 |
// ---------------------------------- |
| 465 |
|
| 466 |
// Build CSS files (analytics.build.css is handled by Analytics.php) |
| 467 |
|
| 468 |
// Legacy CSS files |
| 469 |
'admin-notices-css' => [ |
| 470 |
'file' => 'css/admin-notices.css', |
| 471 |
'deps' => [], |
| 472 |
'contexts' => ['admin'], |
| 473 |
'type' => 'style', |
| 474 |
'handle' => 'embedpress-admin-notices', |
| 475 |
'priority' => 5, |
| 476 |
// 'page' => 'embedpress' |
| 477 |
], |
| 478 |
'feature-notices-css' => [ |
| 479 |
'file' => 'css/feature-notices.css', |
| 480 |
'deps' => [], |
| 481 |
'contexts' => ['admin'], |
| 482 |
'type' => 'style', |
| 483 |
'handle' => 'embedpress-feature-notices', |
| 484 |
'priority' => 5, |
| 485 |
], |
| 486 |
|
| 487 |
'el-icon-css' => [ |
| 488 |
'file' => 'css/el-icon.css', |
| 489 |
'deps' => [], |
| 490 |
'contexts' => ['elementor-editor'], |
| 491 |
'type' => 'style', |
| 492 |
'handle' => 'embedpress-el-icon', |
| 493 |
'priority' => 5, |
| 494 |
], |
| 495 |
'embedpress-elementor-css' => [ |
| 496 |
'file' => 'css/embedpress-elementor.css', |
| 497 |
'deps' => [], |
| 498 |
'contexts' => ['elementor'], |
| 499 |
'type' => 'style', |
| 500 |
'handle' => 'embedpress-elementor-css', |
| 501 |
'priority' => 5, |
| 502 |
], |
| 503 |
'embedpress-css' => [ |
| 504 |
'file' => 'css/embedpress.css', |
| 505 |
'deps' => [], |
| 506 |
'contexts' => ['editor', 'frontend', 'elementor'], |
| 507 |
'type' => 'style', |
| 508 |
'handle' => 'embedpress-css', |
| 509 |
'priority' => 5, |
| 510 |
], |
| 511 |
'pdf-gallery-css' => [ |
| 512 |
'file' => 'css/pdf-gallery.css', |
| 513 |
'deps' => ['embedpress-css'], |
| 514 |
'contexts' => ['frontend', 'elementor', 'editor', 'elementor-editor'], |
| 515 |
'type' => 'style', |
| 516 |
'handle' => 'embedpress-pdf-gallery-css', |
| 517 |
'priority' => 6, |
| 518 |
'providers' => ['pdf-gallery'], |
| 519 |
], |
| 520 |
'pdf-lightbox-css' => [ |
| 521 |
'file' => 'css/ep-pdf-lightbox.css', |
| 522 |
'deps' => ['embedpress-css'], |
| 523 |
'contexts' => ['frontend', 'elementor'], |
| 524 |
'type' => 'style', |
| 525 |
'handle' => 'embedpress-pdf-lightbox-css', |
| 526 |
'priority' => 6, |
| 527 |
'providers' => ['pdf'], |
| 528 |
], |
| 529 |
'modal-css' => [ |
| 530 |
'file' => 'css/modal.css', |
| 531 |
'deps' => [], |
| 532 |
'contexts' => ['editor', 'classic_editor'], |
| 533 |
'type' => 'style', |
| 534 |
'handle' => 'embedpress-classic-editor-modal', |
| 535 |
'priority' => 6, |
| 536 |
], |
| 537 |
'meetup-events-css' => [ |
| 538 |
'file' => 'css/meetup-events.css', |
| 539 |
'deps' => ['embedpress-css'], |
| 540 |
'contexts' => ['frontend', 'editor', 'elementor'], |
| 541 |
'type' => 'style', |
| 542 |
'handle' => 'embedpress-meetup-events', |
| 543 |
'providers' => ['meetup'], // Only for Meetup embeds |
| 544 |
'priority' => 6, |
| 545 |
], |
| 546 |
'settings-icons-css' => [ |
| 547 |
'file' => 'css/settings-icons.css', |
| 548 |
'deps' => [], |
| 549 |
'contexts' => ['admin'], |
| 550 |
'type' => 'style', |
| 551 |
'handle' => 'embedpress-settings-icons', |
| 552 |
'priority' => 5, |
| 553 |
'page' => 'embedpress' |
| 554 |
], |
| 555 |
'settings-css' => [ |
| 556 |
'file' => 'css/settings.css', |
| 557 |
'deps' => [], |
| 558 |
'contexts' => ['admin'], |
| 559 |
'type' => 'style', |
| 560 |
'handle' => 'embedpress-settings-css', |
| 561 |
'priority' => 5, |
| 562 |
'page' => 'embedpress' |
| 563 |
], |
| 564 |
'instagram-shortcode-generator-css' => [ |
| 565 |
'file' => 'css/instagram-shortcode-generator.css', |
| 566 |
'deps' => [], |
| 567 |
'contexts' => ['admin'], |
| 568 |
'type' => 'style', |
| 569 |
'handle' => 'embedpress-instagram-shortcode-generator', |
| 570 |
'priority' => 6, |
| 571 |
'page' => 'embedpress' |
| 572 |
], |
| 573 |
'admin-css' => [ |
| 574 |
'file' => 'css/admin.css', |
| 575 |
'deps' => [], |
| 576 |
'contexts' => ['admin'], |
| 577 |
'type' => 'style', |
| 578 |
'handle' => 'embedpress-admin-css', |
| 579 |
'priority' => 5, |
| 580 |
'page' => 'embedpress' |
| 581 |
], |
| 582 |
'admin-build-css' => [ |
| 583 |
'file' => 'css/admin.build.css', |
| 584 |
'deps' => [], |
| 585 |
'contexts' => ['admin'], |
| 586 |
'type' => 'style', |
| 587 |
'handle' => 'embedpress-admin-build-css', |
| 588 |
'priority' => 6, |
| 589 |
'page' => 'embedpress' |
| 590 |
], |
| 591 |
'onboarding-build-css' => [ |
| 592 |
'file' => 'css/onboarding.build.css', |
| 593 |
'deps' => [], |
| 594 |
'contexts' => ['admin'], |
| 595 |
'type' => 'style', |
| 596 |
'handle' => 'embedpress-onboarding-build-css', |
| 597 |
'priority' => 6, |
| 598 |
'page' => 'embedpress-onboarding' |
| 599 |
], |
| 600 |
'custom-player-build-css' => [ |
| 601 |
'file' => 'css/custom-player.build.css', |
| 602 |
'deps' => [], |
| 603 |
'contexts' => ['admin'], |
| 604 |
'type' => 'style', |
| 605 |
'handle' => 'embedpress-custom-player-css', |
| 606 |
'priority' => 6, |
| 607 |
'page' => 'embedpress-player-engagement' |
| 608 |
], |
| 609 |
]; |
| 610 |
|
| 611 |
/** |
| 612 |
* Initialize asset manager |
| 613 |
*/ |
| 614 |
public static function init() |
| 615 |
{ |
| 616 |
// Register all assets early so they're available as dependencies for Elementor and other plugins |
| 617 |
add_action('wp_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1); |
| 618 |
add_action('admin_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1); |
| 619 |
|
| 620 |
// Use proper priorities to ensure correct load order |
| 621 |
add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_frontend_assets'], 5); |
| 622 |
add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_admin_assets'], 5); |
| 623 |
add_action('admin_enqueue_scripts', [__CLASS__, 'enqueue_classic_editor_assets'], 5); |
| 624 |
add_action('enqueue_block_assets', [__CLASS__, 'enqueue_block_assets'], 5); |
| 625 |
|
| 626 |
|
| 627 |
add_action('enqueue_block_editor_assets', [__CLASS__, 'enqueue_editor_assets'], 5); |
| 628 |
|
| 629 |
// Elementor preview (frontend iframe) - enqueue after scripts are enqueued |
| 630 |
add_action('elementor/frontend/after_enqueue_scripts', [__CLASS__, 'enqueue_elementor_assets'], 5); |
| 631 |
|
| 632 |
// In Elementor editor, WP_Scripts registry is reset; re-register our assets before enqueuing |
| 633 |
add_action('elementor/editor/before_enqueue_scripts', [__CLASS__, 'register_all_assets'], 1); |
| 634 |
// Elementor editor panel (admin) - enqueue after editor scripts are enqueued |
| 635 |
add_action('elementor/editor/after_enqueue_scripts', [__CLASS__, 'enqueue_elementor_editor_assets'], 5); |
| 636 |
} |
| 637 |
|
| 638 |
/** |
| 639 |
* Register all assets early so they're available as dependencies |
| 640 |
* This is crucial for Elementor widgets that declare script/style dependencies |
| 641 |
*/ |
| 642 |
public static function register_all_assets() |
| 643 |
{ |
| 644 |
foreach (self::$assets as $key => $asset) { |
| 645 |
$file_url = EMBEDPRESS_PLUGIN_DIR_URL . 'assets/' . $asset['file']; |
| 646 |
$file_path = EMBEDPRESS_PLUGIN_DIR_PATH . '/assets/' . $asset['file']; |
| 647 |
|
| 648 |
if (!file_exists($file_path)) { |
| 649 |
continue; |
| 650 |
} |
| 651 |
|
| 652 |
$version = filemtime($file_path); |
| 653 |
|
| 654 |
// Register (not enqueue) all assets |
| 655 |
if ($asset['type'] === 'script') { |
| 656 |
wp_register_script( |
| 657 |
$asset['handle'], |
| 658 |
$file_url, |
| 659 |
$asset['deps'], |
| 660 |
$version, |
| 661 |
!empty($asset['footer']) |
| 662 |
); |
| 663 |
|
| 664 |
// Wire up JS translations via the `embedpress` textdomain so |
| 665 |
// `__('Foo','embedpress')` calls inside React build files |
| 666 |
// resolve against wp-content/languages/plugins/embedpress-{locale}-{handle}.json. |
| 667 |
// Applies to every script we register, since the textdomain is shared. |
| 668 |
if (function_exists('wp_set_script_translations')) { |
| 669 |
wp_set_script_translations( |
| 670 |
$asset['handle'], |
| 671 |
'embedpress', |
| 672 |
defined('EMBEDPRESS_PATH_BASE') ? EMBEDPRESS_PATH_BASE . 'languages' : false |
| 673 |
); |
| 674 |
} |
| 675 |
|
| 676 |
// Add module attribute for ES modules (only build files) |
| 677 |
if (strpos($asset['file'], '.build.js') !== false) { |
| 678 |
// Track this handle as a module |
| 679 |
self::$module_handles[] = $asset['handle']; |
| 680 |
|
| 681 |
// Add the global filter only once |
| 682 |
if (!self::$module_filter_added) { |
| 683 |
self::$module_filter_added = true; |
| 684 |
add_filter('script_loader_tag', [__CLASS__, 'add_module_attribute'], 10, 2); |
| 685 |
} |
| 686 |
} |
| 687 |
} elseif ($asset['type'] === 'style') { |
| 688 |
wp_register_style( |
| 689 |
$asset['handle'], |
| 690 |
$file_url, |
| 691 |
$asset['deps'], |
| 692 |
$version, |
| 693 |
$asset['media'] ?? 'all' |
| 694 |
); |
| 695 |
} |
| 696 |
} |
| 697 |
} |
| 698 |
|
| 699 |
/** |
| 700 |
* Enqueue frontend assets |
| 701 |
*/ |
| 702 |
public static function enqueue_frontend_assets() |
| 703 |
{ |
| 704 |
self::enqueue_assets_for_context('frontend'); |
| 705 |
|
| 706 |
// Setup frontend localization |
| 707 |
LocalizationManager::setup_frontend_localization(); |
| 708 |
} |
| 709 |
|
| 710 |
/** |
| 711 |
* Enqueue admin assets |
| 712 |
*/ |
| 713 |
public static function enqueue_admin_assets($hook = '') |
| 714 |
{ |
| 715 |
self::enqueue_assets_for_context('admin', $hook); |
| 716 |
|
| 717 |
// Load settings assets only on EmbedPress settings pages (not onboarding) |
| 718 |
$current_page = isset($_GET['page']) ? $_GET['page'] : ''; |
| 719 |
if (strpos($hook, 'embedpress') !== false && $current_page !== 'embedpress-onboarding' && $current_page !== 'embedpress-player-engagement') { |
| 720 |
self::enqueue_assets_for_context('settings', $hook); |
| 721 |
|
| 722 |
// Ensure wp-color-picker is loaded for settings page |
| 723 |
wp_enqueue_style('wp-color-picker'); |
| 724 |
|
| 725 |
// Ensure media scripts are loaded |
| 726 |
if (!did_action('wp_enqueue_media')) { |
| 727 |
wp_enqueue_media(); |
| 728 |
} |
| 729 |
} |
| 730 |
|
| 731 |
// Setup admin localization |
| 732 |
LocalizationManager::setup_admin_localization($hook); |
| 733 |
} |
| 734 |
|
| 735 |
/** |
| 736 |
* Enqueue block assets (both editor and frontend) |
| 737 |
*/ |
| 738 |
public static function enqueue_block_assets() |
| 739 |
{ |
| 740 |
// This runs on both frontend and editor for blocks |
| 741 |
// For frontend, we don't need the editor scripts |
| 742 |
if (is_admin()) { |
| 743 |
self::enqueue_assets_for_context('editor'); |
| 744 |
} |
| 745 |
} |
| 746 |
|
| 747 |
/** |
| 748 |
* Enqueue editor-only assets |
| 749 |
*/ |
| 750 |
public static function enqueue_editor_assets() |
| 751 |
{ |
| 752 |
// Ensure editor assets are loaded |
| 753 |
self::enqueue_assets_for_context('editor'); |
| 754 |
|
| 755 |
// Setup editor localization |
| 756 |
LocalizationManager::setup_editor_localization(); |
| 757 |
} |
| 758 |
|
| 759 |
public static function enqueue_classic_editor_assets() |
| 760 |
{ |
| 761 |
|
| 762 |
// Ensure editor assets are loaded |
| 763 |
self::enqueue_assets_for_context('classic_editor'); |
| 764 |
|
| 765 |
// Setup editor localization |
| 766 |
LocalizationManager::setup_editor_localization(); |
| 767 |
} |
| 768 |
|
| 769 |
/** |
| 770 |
* Enqueue Elementor frontend assets |
| 771 |
*/ |
| 772 |
public static function enqueue_elementor_assets() |
| 773 |
{ |
| 774 |
self::enqueue_assets_for_context('elementor'); |
| 775 |
|
| 776 |
// Setup Elementor localization |
| 777 |
LocalizationManager::setup_elementor_localization(); |
| 778 |
} |
| 779 |
|
| 780 |
/** |
| 781 |
* Enqueue Elementor editor assets |
| 782 |
*/ |
| 783 |
public static function enqueue_elementor_editor_assets() |
| 784 |
{ |
| 785 |
// In Elementor editor, load only elementor and elementor-editor contexts |
| 786 |
// Do NOT load 'editor' context - that's for Gutenberg only |
| 787 |
self::enqueue_assets_for_context('elementor'); |
| 788 |
self::enqueue_assets_for_context('elementor-editor'); |
| 789 |
|
| 790 |
// Setup Elementor editor localization |
| 791 |
LocalizationManager::setup_elementor_localization(); |
| 792 |
} |
| 793 |
|
| 794 |
/** |
| 795 |
* Enqueue assets for a specific context |
| 796 |
*/ |
| 797 |
private static function enqueue_assets_for_context($context, $hook = '') |
| 798 |
{ |
| 799 |
|
| 800 |
$assets_to_enqueue = []; |
| 801 |
|
| 802 |
// Collect assets for this context |
| 803 |
foreach (self::$assets as $key => $asset) { |
| 804 |
if (in_array($context, $asset['contexts'])) { |
| 805 |
// Check if asset has page restriction |
| 806 |
if (isset($asset['page']) && !empty($asset['page'])) { |
| 807 |
// Only enqueue if we're on the specified page |
| 808 |
if (strpos($hook, $asset['page']) !== false) { |
| 809 |
$assets_to_enqueue[] = array_merge($asset, ['key' => $key]); |
| 810 |
} |
| 811 |
// If page doesn't match, don't enqueue this asset |
| 812 |
} else { |
| 813 |
// No page restriction, enqueue normally |
| 814 |
$assets_to_enqueue[] = array_merge($asset, ['key' => $key]); |
| 815 |
} |
| 816 |
} |
| 817 |
} |
| 818 |
|
| 819 |
// Sort by priority |
| 820 |
usort($assets_to_enqueue, function ($a, $b) { |
| 821 |
return $a['priority'] - $b['priority']; |
| 822 |
}); |
| 823 |
|
| 824 |
// Enqueue assets |
| 825 |
foreach ($assets_to_enqueue as $asset) { |
| 826 |
self::enqueue_single_asset($asset); |
| 827 |
} |
| 828 |
} |
| 829 |
|
| 830 |
/** |
| 831 |
* Enqueue a single asset (assumes asset is already registered) |
| 832 |
*/ |
| 833 |
private static function enqueue_single_asset($asset) |
| 834 |
{ |
| 835 |
$file_path = EMBEDPRESS_PLUGIN_DIR_PATH . '/assets/' . $asset['file']; |
| 836 |
|
| 837 |
if (! file_exists($file_path)) { |
| 838 |
return; |
| 839 |
} |
| 840 |
|
| 841 |
// Check if we should load this asset based on current context |
| 842 |
if (!self::should_load_asset($asset)) { |
| 843 |
return; |
| 844 |
} |
| 845 |
|
| 846 |
// Enqueue the already-registered asset |
| 847 |
if ($asset['type'] === 'script') { |
| 848 |
wp_enqueue_script($asset['handle']); |
| 849 |
} elseif ($asset['type'] === 'style') { |
| 850 |
wp_enqueue_style($asset['handle']); |
| 851 |
} |
| 852 |
} |
| 853 |
|
| 854 |
/** |
| 855 |
* Determine if an asset should be loaded based on current context |
| 856 |
*/ |
| 857 |
private static function should_load_asset($asset) |
| 858 |
{ |
| 859 |
// Check conditional loading requirements first |
| 860 |
if (isset($asset['condition'])) { |
| 861 |
if (!self::check_asset_condition($asset['condition'])) { |
| 862 |
return false; |
| 863 |
} |
| 864 |
|
| 865 |
// When a condition like 'custom_player' already passed, skip the |
| 866 |
// provider check — the condition itself proves these scripts are |
| 867 |
// needed. Provider detection is fragile (missing URL attrs, |
| 868 |
// widget-name typos, etc.) and should not block explicitly-enabled |
| 869 |
// features. |
| 870 |
if ($asset['condition'] === 'custom_player') { |
| 871 |
// Provider check not needed; fall through to context check |
| 872 |
} elseif (isset($asset['providers']) && !empty($asset['providers'])) { |
| 873 |
if (!self::check_provider_match($asset['providers'])) { |
| 874 |
return false; |
| 875 |
} |
| 876 |
} |
| 877 |
} elseif (isset($asset['providers']) && !empty($asset['providers'])) { |
| 878 |
// No condition set — still check providers |
| 879 |
if (!self::check_provider_match($asset['providers'])) { |
| 880 |
return false; |
| 881 |
} |
| 882 |
} |
| 883 |
|
| 884 |
// Get current environment state |
| 885 |
$is_admin = is_admin(); |
| 886 |
$is_elementor_editor = false; |
| 887 |
$is_elementor_preview = false; |
| 888 |
$is_gutenberg_editor = false; |
| 889 |
|
| 890 |
// Check Elementor states |
| 891 |
if (class_exists('\Elementor\Plugin')) { |
| 892 |
$elementor = \Elementor\Plugin::$instance; |
| 893 |
|
| 894 |
if (isset($elementor->editor)) { |
| 895 |
$is_elementor_editor = $elementor->editor->is_edit_mode(); |
| 896 |
} |
| 897 |
|
| 898 |
if (isset($elementor->preview)) { |
| 899 |
$is_elementor_preview = $elementor->preview->is_preview_mode(); |
| 900 |
} |
| 901 |
} |
| 902 |
|
| 903 |
// Check if we're in Gutenberg editor |
| 904 |
if ($is_admin) { |
| 905 |
global $pagenow; |
| 906 |
$is_gutenberg_editor = ( |
| 907 |
$pagenow === 'post.php' || |
| 908 |
$pagenow === 'post-new.php' || |
| 909 |
$pagenow === 'site-editor.php' |
| 910 |
) && function_exists('use_block_editor_for_post_type'); |
| 911 |
|
| 912 |
// Check if we're in classic editor (not Gutenberg) |
| 913 |
$is_classic_editor = false; |
| 914 |
if ($pagenow === 'post.php' || $pagenow === 'post-new.php') { |
| 915 |
// Check if classic editor is being used |
| 916 |
if ( |
| 917 |
isset($_GET['classic-editor']) || |
| 918 |
(function_exists('use_block_editor_for_post_type') && |
| 919 |
isset($_GET['post']) && |
| 920 |
!use_block_editor_for_post_type(get_post_type($_GET['post']))) |
| 921 |
) { |
| 922 |
$is_classic_editor = true; |
| 923 |
} |
| 924 |
// Also check if Classic Editor plugin is active and set to classic mode |
| 925 |
if ( |
| 926 |
class_exists('Classic_Editor') && |
| 927 |
get_option('classic-editor-replace') === 'classic' |
| 928 |
) { |
| 929 |
$is_classic_editor = true; |
| 930 |
} |
| 931 |
} |
| 932 |
} |
| 933 |
|
| 934 |
// Asset loading logic based on contexts |
| 935 |
foreach ($asset['contexts'] as $context) { |
| 936 |
|
| 937 |
|
| 938 |
switch ($context) { |
| 939 |
case 'frontend': |
| 940 |
// Load on frontend (not in any editor or admin) |
| 941 |
if (!$is_admin && !$is_elementor_editor && !$is_elementor_preview) { |
| 942 |
return true; |
| 943 |
} |
| 944 |
break; |
| 945 |
|
| 946 |
case 'admin': |
| 947 |
// Load in WordPress admin (but not in Elementor editor) |
| 948 |
if ($is_admin && !$is_elementor_editor && !$is_elementor_preview) { |
| 949 |
// Check if asset has page restriction |
| 950 |
if (isset($asset['page'])) { |
| 951 |
return self::is_embedpress_admin_page($asset['page']); |
| 952 |
} |
| 953 |
return true; |
| 954 |
} |
| 955 |
break; |
| 956 |
|
| 957 |
case 'editor': |
| 958 |
// Load ONLY in Gutenberg editor (not in Elementor editor or other admin pages) |
| 959 |
if ($is_gutenberg_editor && !$is_elementor_editor) { |
| 960 |
return true; |
| 961 |
} |
| 962 |
break; |
| 963 |
case 'classic_editor': |
| 964 |
// Load only in classic editor (TinyMCE) |
| 965 |
if ($is_classic_editor) { |
| 966 |
return true; |
| 967 |
} |
| 968 |
break; |
| 969 |
case 'elementor': |
| 970 |
// Load in Elementor editor, preview, or frontend when Elementor is rendering |
| 971 |
if ($is_elementor_editor || $is_elementor_preview) { |
| 972 |
return true; |
| 973 |
} |
| 974 |
// Also load on frontend if Elementor content is present |
| 975 |
if (!$is_admin && self::has_elementor_content()) { |
| 976 |
return true; |
| 977 |
} |
| 978 |
break; |
| 979 |
|
| 980 |
case 'elementor-editor': |
| 981 |
|
| 982 |
// Load only in Elementor editor (not preview or frontend) |
| 983 |
if ($is_elementor_editor) { |
| 984 |
return true; |
| 985 |
} |
| 986 |
break; |
| 987 |
|
| 988 |
case 'settings': |
| 989 |
// Load only on EmbedPress settings pages |
| 990 |
if ($is_admin && !$is_elementor_editor && !$is_elementor_preview) { |
| 991 |
return true; |
| 992 |
} |
| 993 |
break; |
| 994 |
} |
| 995 |
} |
| 996 |
|
| 997 |
// Check if this is an individual block script and if it should be loaded |
| 998 |
if (strpos($asset['handle'], 'embedpress-block-') === 0) { |
| 999 |
return self::should_load_individual_block($asset['handle']); |
| 1000 |
} |
| 1001 |
|
| 1002 |
return false; |
| 1003 |
} |
| 1004 |
|
| 1005 |
/** |
| 1006 |
* Check if we're on an EmbedPress admin page |
| 1007 |
*/ |
| 1008 |
private static function is_embedpress_admin_page($page_type) |
| 1009 |
{ |
| 1010 |
global $pagenow; |
| 1011 |
|
| 1012 |
// Get current page |
| 1013 |
$current_page = isset($_GET['page']) ? $_GET['page'] : ''; |
| 1014 |
|
| 1015 |
switch ($page_type) { |
| 1016 |
case 'embedpress': |
| 1017 |
// Check if we're on any EmbedPress admin page (except onboarding which has its own assets) |
| 1018 |
return ( |
| 1019 |
$current_page === 'embedpress' || |
| 1020 |
(strpos($current_page, 'embedpress') !== false && $current_page !== 'embedpress-onboarding') |
| 1021 |
); |
| 1022 |
case 'embedpress-analytics': |
| 1023 |
return $current_page === 'embedpress-analytics'; |
| 1024 |
case 'embedpress-onboarding': |
| 1025 |
return $current_page === 'embedpress-onboarding'; |
| 1026 |
case 'embedpress-player-engagement': |
| 1027 |
return $current_page === 'embedpress-player-engagement'; |
| 1028 |
case 'embedpress-google-reviews': |
| 1029 |
return $current_page === 'embedpress-google-reviews'; |
| 1030 |
default: |
| 1031 |
return false; |
| 1032 |
} |
| 1033 |
} |
| 1034 |
|
| 1035 |
/** |
| 1036 |
* Check if individual block should be loaded based on active blocks |
| 1037 |
*/ |
| 1038 |
private static function should_load_individual_block($handle) |
| 1039 |
{ |
| 1040 |
// Get active blocks from settings |
| 1041 |
$elements = (array) get_option(EMBEDPRESS_PLG_NAME . ":elements", []); |
| 1042 |
$active_blocks = isset($elements['gutenberg']) ? (array) $elements['gutenberg'] : []; |
| 1043 |
|
| 1044 |
// Map handles to block names |
| 1045 |
$block_map = [ |
| 1046 |
'embedpress-block-embedpress' => 'embedpress', |
| 1047 |
'embedpress-block-document' => 'document', |
| 1048 |
'embedpress-block-pdf' => 'embedpress-pdf', |
| 1049 |
'embedpress-block-calendar' => 'embedpress-calendar', |
| 1050 |
'embedpress-block-google-docs' => 'google-docs', |
| 1051 |
'embedpress-block-google-drawings' => 'google-drawings', |
| 1052 |
'embedpress-block-google-forms' => 'google-forms', |
| 1053 |
'embedpress-block-google-maps' => 'google-maps', |
| 1054 |
'embedpress-block-google-sheets' => 'google-sheets', |
| 1055 |
'embedpress-block-google-slides' => 'google-slides', |
| 1056 |
'embedpress-block-twitch' => 'twitch', |
| 1057 |
'embedpress-block-wistia' => 'wistia', |
| 1058 |
'embedpress-block-youtube' => 'youtube' |
| 1059 |
]; |
| 1060 |
|
| 1061 |
$block_name = isset($block_map[$handle]) ? $block_map[$handle] : ''; |
| 1062 |
|
| 1063 |
// If no block name found or no active blocks set, load all blocks (default behavior) |
| 1064 |
if (empty($block_name) || empty($active_blocks)) { |
| 1065 |
return true; |
| 1066 |
} |
| 1067 |
|
| 1068 |
// Check if this specific block is active |
| 1069 |
return in_array($block_name, $active_blocks); |
| 1070 |
} |
| 1071 |
|
| 1072 |
/** |
| 1073 |
* Check if current page has Elementor content |
| 1074 |
*/ |
| 1075 |
private static function has_elementor_content() |
| 1076 |
{ |
| 1077 |
if (! class_exists('\Elementor\Plugin')) { |
| 1078 |
return false; |
| 1079 |
} |
| 1080 |
|
| 1081 |
if (is_singular()) { |
| 1082 |
$post_id = get_the_ID(); |
| 1083 |
if (empty($post_id) || ! is_numeric($post_id)) { |
| 1084 |
return false; |
| 1085 |
} |
| 1086 |
|
| 1087 |
$document = \Elementor\Plugin::$instance->documents->get($post_id); |
| 1088 |
|
| 1089 |
if ($document && method_exists($document, 'is_built_with_elementor')) { |
| 1090 |
return (bool) $document->is_built_with_elementor(); |
| 1091 |
} |
| 1092 |
} |
| 1093 |
|
| 1094 |
return false; |
| 1095 |
} |
| 1096 |
|
| 1097 |
|
| 1098 |
/** |
| 1099 |
* Get asset URL |
| 1100 |
*/ |
| 1101 |
public static function get_asset_url($file) |
| 1102 |
{ |
| 1103 |
return EMBEDPRESS_PLUGIN_DIR_URL . 'assets/' . $file; |
| 1104 |
} |
| 1105 |
|
| 1106 |
|
| 1107 |
|
| 1108 |
/** |
| 1109 |
* Add module attribute to script tags for ES modules |
| 1110 |
*/ |
| 1111 |
public static function add_module_attribute($tag, $handle) |
| 1112 |
{ |
| 1113 |
if (in_array($handle, self::$module_handles)) { |
| 1114 |
// Only add type="module" if it doesn't already exist |
| 1115 |
if (strpos($tag, 'type="module"') === false) { |
| 1116 |
return str_replace('<script ', '<script type="module" ', $tag); |
| 1117 |
} |
| 1118 |
} |
| 1119 |
return $tag; |
| 1120 |
} |
| 1121 |
|
| 1122 |
/** |
| 1123 |
* Check if asset exists |
| 1124 |
*/ |
| 1125 |
public static function asset_exists($file) |
| 1126 |
{ |
| 1127 |
$plugin_path = dirname(dirname(dirname(__DIR__))); |
| 1128 |
return file_exists($plugin_path . '/assets/' . $file); |
| 1129 |
} |
| 1130 |
|
| 1131 |
/** |
| 1132 |
* Check if an asset condition is met |
| 1133 |
* |
| 1134 |
* @param string $condition The condition to check |
| 1135 |
* @return bool |
| 1136 |
*/ |
| 1137 |
private static function check_asset_condition($condition) |
| 1138 |
{ |
| 1139 |
switch ($condition) { |
| 1140 |
case 'custom_player': |
| 1141 |
return self::is_custom_player_enabled(); |
| 1142 |
|
| 1143 |
case 'has_content': |
| 1144 |
// In Elementor editor, always load core scripts with has_content condition |
| 1145 |
// because we can't detect unsaved content |
| 1146 |
if (class_exists('\Elementor\Plugin')) { |
| 1147 |
$elementor = \Elementor\Plugin::$instance; |
| 1148 |
if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) { |
| 1149 |
return true; |
| 1150 |
} |
| 1151 |
} |
| 1152 |
return self::has_embedpress_content(); |
| 1153 |
|
| 1154 |
case 'lazy_load': |
| 1155 |
// In Elementor editor, always load lazy load scripts |
| 1156 |
// because we can't detect unsaved content |
| 1157 |
if (class_exists('\Elementor\Plugin')) { |
| 1158 |
$elementor = \Elementor\Plugin::$instance; |
| 1159 |
if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) { |
| 1160 |
return true; |
| 1161 |
} |
| 1162 |
} |
| 1163 |
return self::has_lazy_load_enabled(); |
| 1164 |
|
| 1165 |
case 'analytics_enabled': |
| 1166 |
// Gate on both the tracking toggle and the presence of EmbedPress content, |
| 1167 |
// so disabling analytics short-circuits the script (and the JS cookie writer) |
| 1168 |
// entirely instead of relying on a runtime flag. |
| 1169 |
if (!get_option('embedpress_analytics_tracking_enabled', true)) { |
| 1170 |
return false; |
| 1171 |
} |
| 1172 |
return self::has_embedpress_content(); |
| 1173 |
|
| 1174 |
case 'always': |
| 1175 |
default: |
| 1176 |
return true; |
| 1177 |
} |
| 1178 |
} |
| 1179 |
|
| 1180 |
/** |
| 1181 |
* Check if custom player is enabled on the current page |
| 1182 |
* |
| 1183 |
* @return bool |
| 1184 |
*/ |
| 1185 |
private static function is_custom_player_enabled() |
| 1186 |
{ |
| 1187 |
// Cache the result to avoid multiple checks |
| 1188 |
if (self::$custom_player_enabled !== null) { |
| 1189 |
return self::$custom_player_enabled; |
| 1190 |
} |
| 1191 |
|
| 1192 |
// In the Gutenberg block editor and Elementor editor, always load |
| 1193 |
// custom-player scripts so live preview keeps working as the user |
| 1194 |
// toggles options — the saved post content doesn't yet reflect |
| 1195 |
// unsaved inspector / widget changes, so the per-post detection |
| 1196 |
// below would return false and Plyr would never get enqueued. |
| 1197 |
if (is_admin() && function_exists('get_current_screen')) { |
| 1198 |
$screen = get_current_screen(); |
| 1199 |
if ($screen && method_exists($screen, 'is_block_editor') && $screen->is_block_editor()) { |
| 1200 |
self::$custom_player_enabled = true; |
| 1201 |
return true; |
| 1202 |
} |
| 1203 |
} |
| 1204 |
if (class_exists('\Elementor\Plugin')) { |
| 1205 |
$elementor = \Elementor\Plugin::$instance; |
| 1206 |
if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) { |
| 1207 |
self::$custom_player_enabled = true; |
| 1208 |
return true; |
| 1209 |
} |
| 1210 |
} |
| 1211 |
|
| 1212 |
global $post; |
| 1213 |
|
| 1214 |
if (!$post) { |
| 1215 |
self::$custom_player_enabled = false; |
| 1216 |
return false; |
| 1217 |
} |
| 1218 |
|
| 1219 |
$content = $post->post_content; |
| 1220 |
|
| 1221 |
// Check for custom player in Gutenberg blocks |
| 1222 |
if (function_exists('has_blocks') && has_blocks($content)) { |
| 1223 |
$blocks = parse_blocks($content); |
| 1224 |
if (self::has_custom_player_in_blocks($blocks)) { |
| 1225 |
self::$custom_player_enabled = true; |
| 1226 |
return true; |
| 1227 |
} |
| 1228 |
} |
| 1229 |
|
| 1230 |
// Check for custom player in Elementor |
| 1231 |
if (class_exists('\Elementor\Plugin')) { |
| 1232 |
$document = \Elementor\Plugin::$instance->documents->get($post->ID); |
| 1233 |
if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) { |
| 1234 |
// Check Elementor meta for custom player settings |
| 1235 |
$elementor_data = get_post_meta($post->ID, '_elementor_data', true); |
| 1236 |
if ($elementor_data && is_string($elementor_data) && (strpos($elementor_data, 'emberpress_custom_player') !== false || strpos($elementor_data, '"customPlayer":true') !== false)) { |
| 1237 |
self::$custom_player_enabled = true; |
| 1238 |
return true; |
| 1239 |
} |
| 1240 |
} |
| 1241 |
} |
| 1242 |
|
| 1243 |
// Check for custom player in shortcodes (look for customPlayer attribute) |
| 1244 |
if (has_shortcode($content, 'embedpress')) { |
| 1245 |
if (is_string($content) && (strpos($content, 'customPlayer') !== false || strpos($content, 'custom_player') !== false)) { |
| 1246 |
self::$custom_player_enabled = true; |
| 1247 |
return true; |
| 1248 |
} |
| 1249 |
} |
| 1250 |
|
| 1251 |
self::$custom_player_enabled = false; |
| 1252 |
return false; |
| 1253 |
} |
| 1254 |
|
| 1255 |
/** |
| 1256 |
* Check if blocks contain custom player settings |
| 1257 |
* |
| 1258 |
* @param array $blocks |
| 1259 |
* @return bool |
| 1260 |
*/ |
| 1261 |
private static function has_custom_player_in_blocks($blocks) |
| 1262 |
{ |
| 1263 |
foreach ($blocks as $block) { |
| 1264 |
// Check if this is an EmbedPress block with custom player enabled |
| 1265 |
$block_name = $block['blockName'] ?? ''; |
| 1266 |
if ($block_name && strpos($block_name, 'embedpress/') === 0) { |
| 1267 |
if (isset($block['attrs']['customPlayer']) && $block['attrs']['customPlayer']) { |
| 1268 |
return true; |
| 1269 |
} |
| 1270 |
} |
| 1271 |
|
| 1272 |
// Recursively check inner blocks |
| 1273 |
if (!empty($block['innerBlocks'])) { |
| 1274 |
if (self::has_custom_player_in_blocks($block['innerBlocks'])) { |
| 1275 |
return true; |
| 1276 |
} |
| 1277 |
} |
| 1278 |
} |
| 1279 |
|
| 1280 |
return false; |
| 1281 |
} |
| 1282 |
|
| 1283 |
/** |
| 1284 |
* Check if lazy loading is enabled in any embed on the page |
| 1285 |
* |
| 1286 |
* @return bool |
| 1287 |
*/ |
| 1288 |
private static function has_lazy_load_enabled() |
| 1289 |
{ |
| 1290 |
// Check global lazy load setting first - if enabled globally, |
| 1291 |
// load lazy-load assets whenever EmbedPress content is present |
| 1292 |
$g_settings = get_option(EMBEDPRESS_PLG_NAME, []); |
| 1293 |
if (isset($g_settings['g_lazyload']) && $g_settings['g_lazyload'] == 1) { |
| 1294 |
return self::has_embedpress_content(); |
| 1295 |
} |
| 1296 |
|
| 1297 |
global $post; |
| 1298 |
|
| 1299 |
if (!$post) { |
| 1300 |
return false; |
| 1301 |
} |
| 1302 |
|
| 1303 |
$content = $post->post_content; |
| 1304 |
|
| 1305 |
// Check if post content contains lazy load attributes in blocks |
| 1306 |
if (function_exists('has_blocks') && has_blocks($content)) { |
| 1307 |
$blocks = parse_blocks($content); |
| 1308 |
if (self::has_lazy_load_in_blocks($blocks)) { |
| 1309 |
return true; |
| 1310 |
} |
| 1311 |
} |
| 1312 |
|
| 1313 |
// Check for Elementor meta (if Elementor is active) |
| 1314 |
if (class_exists('\Elementor\Plugin')) { |
| 1315 |
$document = \Elementor\Plugin::$instance->documents->get($post->ID); |
| 1316 |
if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) { |
| 1317 |
$elementor_data = get_post_meta($post->ID, '_elementor_data', true); |
| 1318 |
if ($elementor_data && is_string($elementor_data) && strpos($elementor_data, '"enable_lazy_load":"yes"') !== false) { |
| 1319 |
return true; |
| 1320 |
} |
| 1321 |
} |
| 1322 |
} |
| 1323 |
|
| 1324 |
return false; |
| 1325 |
} |
| 1326 |
|
| 1327 |
/** |
| 1328 |
* Check if blocks contain lazy load settings |
| 1329 |
* |
| 1330 |
* @param array $blocks |
| 1331 |
* @return bool |
| 1332 |
*/ |
| 1333 |
private static function has_lazy_load_in_blocks($blocks) |
| 1334 |
{ |
| 1335 |
foreach ($blocks as $block) { |
| 1336 |
// Check if this is an EmbedPress block with lazy load enabled |
| 1337 |
$block_name = $block['blockName'] ?? ''; |
| 1338 |
if ($block_name && strpos($block_name, 'embedpress/') === 0) { |
| 1339 |
if (isset($block['attrs']['enableLazyLoad']) && $block['attrs']['enableLazyLoad']) { |
| 1340 |
return true; |
| 1341 |
} |
| 1342 |
} |
| 1343 |
|
| 1344 |
// Recursively check inner blocks |
| 1345 |
if (!empty($block['innerBlocks'])) { |
| 1346 |
if (self::has_lazy_load_in_blocks($block['innerBlocks'])) { |
| 1347 |
return true; |
| 1348 |
} |
| 1349 |
} |
| 1350 |
} |
| 1351 |
|
| 1352 |
return false; |
| 1353 |
} |
| 1354 |
|
| 1355 |
|
| 1356 |
/** |
| 1357 |
* Check if current page has EmbedPress content |
| 1358 |
* |
| 1359 |
* @return bool |
| 1360 |
*/ |
| 1361 |
private static function has_embedpress_content() |
| 1362 |
{ |
| 1363 |
// Cache the result to avoid multiple checks |
| 1364 |
if (self::$has_embedpress_content !== null) { |
| 1365 |
return self::$has_embedpress_content; |
| 1366 |
} |
| 1367 |
|
| 1368 |
global $post; |
| 1369 |
|
| 1370 |
if (!$post) { |
| 1371 |
self::$has_embedpress_content = false; |
| 1372 |
return false; |
| 1373 |
} |
| 1374 |
|
| 1375 |
$content = $post->post_content; |
| 1376 |
|
| 1377 |
// Check for EmbedPress shortcodes |
| 1378 |
if (has_shortcode($content, 'embedpress') || has_shortcode($content, 'embedpress_pdf_gallery')) { |
| 1379 |
self::$has_embedpress_content = true; |
| 1380 |
return true; |
| 1381 |
} |
| 1382 |
|
| 1383 |
// Check for EmbedPress Gutenberg blocks |
| 1384 |
$embedpress_blocks = [ |
| 1385 |
'embedpress/embedpress', |
| 1386 |
'embedpress/google-docs-block', |
| 1387 |
'embedpress/google-sheets-block', |
| 1388 |
'embedpress/google-slides-block', |
| 1389 |
'embedpress/google-forms-block', |
| 1390 |
'embedpress/google-drawings-block', |
| 1391 |
'embedpress/google-maps-block', |
| 1392 |
'embedpress/youtube-block', |
| 1393 |
'embedpress/vimeo-block', |
| 1394 |
'embedpress/wistia-block', |
| 1395 |
'embedpress/twitch-block', |
| 1396 |
'embedpress/embedpress-pdf', |
| 1397 |
'embedpress/pdf-gallery', |
| 1398 |
'embedpress/document', |
| 1399 |
'embedpress/embedpress-calendar', |
| 1400 |
'embedpress/google-reviews' |
| 1401 |
]; |
| 1402 |
|
| 1403 |
foreach ($embedpress_blocks as $block_name) { |
| 1404 |
if (has_block($block_name, $post)) { |
| 1405 |
self::$has_embedpress_content = true; |
| 1406 |
return true; |
| 1407 |
} |
| 1408 |
} |
| 1409 |
|
| 1410 |
// Check for Elementor EmbedPress widgets |
| 1411 |
if (class_exists('\Elementor\Plugin')) { |
| 1412 |
$document = \Elementor\Plugin::$instance->documents->get($post->ID); |
| 1413 |
if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) { |
| 1414 |
$elementor_data = get_post_meta($post->ID, '_elementor_data', true); |
| 1415 |
if ($elementor_data && is_string($elementor_data) && (strpos($elementor_data, 'embedpress') !== false || strpos($elementor_data, 'Embedpress') !== false)) { |
| 1416 |
self::$has_embedpress_content = true; |
| 1417 |
return true; |
| 1418 |
} |
| 1419 |
} |
| 1420 |
} |
| 1421 |
|
| 1422 |
self::$has_embedpress_content = false; |
| 1423 |
return false; |
| 1424 |
} |
| 1425 |
|
| 1426 |
/** |
| 1427 |
* Check if any of the required providers match the detected embed types |
| 1428 |
* |
| 1429 |
* @param array $required_providers List of providers this asset needs |
| 1430 |
* @return bool |
| 1431 |
*/ |
| 1432 |
private static function check_provider_match($required_providers) |
| 1433 |
{ |
| 1434 |
// In Elementor editor or preview, always load provider scripts to allow live preview |
| 1435 |
// because we can't detect unsaved widgets from _elementor_data |
| 1436 |
if (class_exists('\Elementor\Plugin')) { |
| 1437 |
$elementor = \Elementor\Plugin::$instance; |
| 1438 |
if (isset($elementor->editor) && $elementor->editor->is_edit_mode()) { |
| 1439 |
return true; |
| 1440 |
} |
| 1441 |
if (isset($elementor->preview) && $elementor->preview->is_preview_mode()) { |
| 1442 |
return true; |
| 1443 |
} |
| 1444 |
} |
| 1445 |
|
| 1446 |
// In Gutenberg editor, always load provider assets to allow live preview |
| 1447 |
// because we can't detect unsaved blocks from post_content |
| 1448 |
if (function_exists('get_current_screen')) { |
| 1449 |
$screen = get_current_screen(); |
| 1450 |
if ($screen && $screen->is_block_editor()) { |
| 1451 |
return true; |
| 1452 |
} |
| 1453 |
} |
| 1454 |
|
| 1455 |
$detected_types = self::detect_embed_types(); |
| 1456 |
|
| 1457 |
// If no embeds detected, don't load |
| 1458 |
if (empty($detected_types)) { |
| 1459 |
return false; |
| 1460 |
} |
| 1461 |
|
| 1462 |
// Check if any required provider matches detected types |
| 1463 |
foreach ($required_providers as $provider) { |
| 1464 |
if (in_array($provider, $detected_types)) { |
| 1465 |
return true; |
| 1466 |
} |
| 1467 |
} |
| 1468 |
|
| 1469 |
return false; |
| 1470 |
} |
| 1471 |
|
| 1472 |
/** |
| 1473 |
* Detect all embed types on the current page |
| 1474 |
* |
| 1475 |
* @return array List of detected embed types |
| 1476 |
*/ |
| 1477 |
private static function detect_embed_types() |
| 1478 |
{ |
| 1479 |
// Cache the result to avoid multiple checks |
| 1480 |
if (self::$detected_embed_types !== null) { |
| 1481 |
return self::$detected_embed_types; |
| 1482 |
} |
| 1483 |
|
| 1484 |
self::$detected_embed_types = []; |
| 1485 |
|
| 1486 |
global $post; |
| 1487 |
|
| 1488 |
if (!$post) { |
| 1489 |
return self::$detected_embed_types; |
| 1490 |
} |
| 1491 |
|
| 1492 |
$content = $post->post_content; |
| 1493 |
|
| 1494 |
// Detect from Gutenberg blocks |
| 1495 |
if (function_exists('has_blocks') && has_blocks($content)) { |
| 1496 |
$blocks = parse_blocks($content); |
| 1497 |
self::$detected_embed_types = array_merge( |
| 1498 |
self::$detected_embed_types, |
| 1499 |
self::detect_types_from_blocks($blocks) |
| 1500 |
); |
| 1501 |
} |
| 1502 |
|
| 1503 |
// Detect from shortcodes |
| 1504 |
self::$detected_embed_types = array_merge( |
| 1505 |
self::$detected_embed_types, |
| 1506 |
self::detect_types_from_shortcodes($content) |
| 1507 |
); |
| 1508 |
|
| 1509 |
// Detect from Elementor |
| 1510 |
if (class_exists('\Elementor\Plugin')) { |
| 1511 |
$document = \Elementor\Plugin::$instance->documents->get($post->ID); |
| 1512 |
if ($document && method_exists($document, 'is_built_with_elementor') && $document->is_built_with_elementor()) { |
| 1513 |
self::$detected_embed_types = array_merge( |
| 1514 |
self::$detected_embed_types, |
| 1515 |
self::detect_types_from_elementor($post->ID) |
| 1516 |
); |
| 1517 |
} |
| 1518 |
} |
| 1519 |
|
| 1520 |
// Remove duplicates |
| 1521 |
self::$detected_embed_types = array_unique(self::$detected_embed_types); |
| 1522 |
|
| 1523 |
return self::$detected_embed_types; |
| 1524 |
} |
| 1525 |
|
| 1526 |
/** |
| 1527 |
* Detect embed types from Gutenberg blocks |
| 1528 |
* |
| 1529 |
* @param array $blocks |
| 1530 |
* @return array |
| 1531 |
*/ |
| 1532 |
private static function detect_types_from_blocks($blocks) |
| 1533 |
{ |
| 1534 |
$types = []; |
| 1535 |
|
| 1536 |
foreach ($blocks as $block) { |
| 1537 |
// Map block names to embed types |
| 1538 |
$block_name = $block['blockName'] ?? ''; |
| 1539 |
|
| 1540 |
if ($block_name && strpos($block_name, 'embedpress/') === 0) { |
| 1541 |
// Extract type from block name |
| 1542 |
if ($block_name === 'embedpress/embedpress') { |
| 1543 |
// Generic block - detect from URL |
| 1544 |
$url = $block['attrs']['url'] ?? ''; |
| 1545 |
$types = array_merge($types, self::detect_type_from_url($url)); |
| 1546 |
} elseif ($block_name === 'embedpress/embedpress-pdf') { |
| 1547 |
$types[] = 'pdf'; |
| 1548 |
} elseif ($block_name === 'embedpress/pdf-gallery') { |
| 1549 |
$types[] = 'pdf-gallery'; |
| 1550 |
$types[] = 'pdf'; |
| 1551 |
} elseif ($block_name === 'embedpress/document') { |
| 1552 |
$types[] = 'document'; |
| 1553 |
} elseif ($block_name === 'embedpress/youtube-block') { |
| 1554 |
$types[] = 'youtube'; |
| 1555 |
} elseif ($block_name === 'embedpress/vimeo-block') { |
| 1556 |
$types[] = 'vimeo'; |
| 1557 |
} elseif ($block_name === 'embedpress/google-docs-block') { |
| 1558 |
$types[] = 'google-docs'; |
| 1559 |
} elseif ($block_name === 'embedpress/google-sheets-block') { |
| 1560 |
$types[] = 'google-sheets'; |
| 1561 |
} elseif ($block_name === 'embedpress/google-slides-block') { |
| 1562 |
$types[] = 'google-slides'; |
| 1563 |
} elseif ($block_name === 'embedpress/wistia-block') { |
| 1564 |
$types[] = 'wistia'; |
| 1565 |
} elseif ($block_name === 'embedpress/twitch-block') { |
| 1566 |
$types[] = 'twitch'; |
| 1567 |
} |
| 1568 |
} |
| 1569 |
|
| 1570 |
// Recursively check inner blocks |
| 1571 |
if (!empty($block['innerBlocks'])) { |
| 1572 |
$types = array_merge($types, self::detect_types_from_blocks($block['innerBlocks'])); |
| 1573 |
} |
| 1574 |
} |
| 1575 |
|
| 1576 |
return $types; |
| 1577 |
} |
| 1578 |
|
| 1579 |
/** |
| 1580 |
* Detect embed types from shortcodes |
| 1581 |
* |
| 1582 |
* @param string $content |
| 1583 |
* @return array |
| 1584 |
*/ |
| 1585 |
private static function detect_types_from_shortcodes($content) |
| 1586 |
{ |
| 1587 |
$types = []; |
| 1588 |
|
| 1589 |
if (!is_string($content)) { |
| 1590 |
return $types; |
| 1591 |
} |
| 1592 |
|
| 1593 |
// Find all embedpress shortcodes with URL attribute (with or without quotes) |
| 1594 |
// Matches: [embedpress url="..."], [embedpress url='...'], [embedpress url=...] |
| 1595 |
if (preg_match_all('/\[embedpress[^\]]*url=["\']?([^"\'\s\]]+)["\']?[^\]]*\]/i', $content, $matches)) { |
| 1596 |
foreach ($matches[1] as $url) { |
| 1597 |
$types = array_merge($types, self::detect_type_from_url($url)); |
| 1598 |
} |
| 1599 |
} |
| 1600 |
|
| 1601 |
// Detect PDF gallery shortcode |
| 1602 |
if (has_shortcode($content, 'embedpress_pdf_gallery')) { |
| 1603 |
$types[] = 'pdf-gallery'; |
| 1604 |
$types[] = 'pdf'; |
| 1605 |
} |
| 1606 |
|
| 1607 |
// Find embedpress shortcodes with URL between tags |
| 1608 |
// Matches: [embedpress]URL[/embedpress] |
| 1609 |
if (preg_match_all('/\[embedpress[^\]]*\]([^\[]+)\[\/embedpress\]/i', $content, $matches)) { |
| 1610 |
foreach ($matches[1] as $url) { |
| 1611 |
$url = trim($url); |
| 1612 |
if (!empty($url)) { |
| 1613 |
$types = array_merge($types, self::detect_type_from_url($url)); |
| 1614 |
} |
| 1615 |
} |
| 1616 |
} |
| 1617 |
|
| 1618 |
return $types; |
| 1619 |
} |
| 1620 |
|
| 1621 |
/** |
| 1622 |
* Detect embed types from Elementor |
| 1623 |
* |
| 1624 |
* @param int $post_id |
| 1625 |
* @return array |
| 1626 |
*/ |
| 1627 |
private static function detect_types_from_elementor($post_id) |
| 1628 |
{ |
| 1629 |
$types = []; |
| 1630 |
$elementor_data = get_post_meta($post_id, '_elementor_data', true); |
| 1631 |
|
| 1632 |
if (!$elementor_data || !is_string($elementor_data)) { |
| 1633 |
return $types; |
| 1634 |
} |
| 1635 |
|
| 1636 |
// Decode JSON data |
| 1637 |
$data = json_decode($elementor_data, true); |
| 1638 |
if (!$data || !is_array($data)) { |
| 1639 |
return $types; |
| 1640 |
} |
| 1641 |
|
| 1642 |
// Recursively search for EmbedPress widgets |
| 1643 |
$types = self::detect_types_from_elementor_data($data); |
| 1644 |
|
| 1645 |
return $types; |
| 1646 |
} |
| 1647 |
|
| 1648 |
/** |
| 1649 |
* Recursively detect types from Elementor data |
| 1650 |
* |
| 1651 |
* @param array $data |
| 1652 |
* @return array |
| 1653 |
*/ |
| 1654 |
private static function detect_types_from_elementor_data($data) |
| 1655 |
{ |
| 1656 |
$types = []; |
| 1657 |
|
| 1658 |
if (!is_array($data)) { |
| 1659 |
return $types; |
| 1660 |
} |
| 1661 |
|
| 1662 |
foreach ($data as $element) { |
| 1663 |
if (!is_array($element)) { |
| 1664 |
continue; |
| 1665 |
} |
| 1666 |
|
| 1667 |
// Check if this is an EmbedPress widget |
| 1668 |
// Note: widget name is 'embedpres_elementor' (legacy typo without double 's') |
| 1669 |
$widget_type = $element['widgetType'] ?? ''; |
| 1670 |
if ($widget_type && (strpos($widget_type, 'embedpres') !== false || strpos($widget_type, 'Embedpress') !== false)) { |
| 1671 |
// Get the embed source |
| 1672 |
$settings = $element['settings'] ?? []; |
| 1673 |
$source = $settings['embedpress_pro_embeded_source'] ?? ''; |
| 1674 |
$url = $settings['embedpress_embeded_link'] ?? ''; |
| 1675 |
|
| 1676 |
if ($source) { |
| 1677 |
$types[] = $source; |
| 1678 |
} elseif ($url) { |
| 1679 |
$types = array_merge($types, self::detect_type_from_url($url)); |
| 1680 |
} |
| 1681 |
|
| 1682 |
// Detect PDF widget specifically (uses different setting names) |
| 1683 |
if ($widget_type === 'embedpress_pdf') { |
| 1684 |
$pdf_url = $settings['embedpress_pdf_Uploader']['url'] ?? ''; |
| 1685 |
$pdf_link = $settings['embedpress_pdf_file_link']['url'] ?? ''; |
| 1686 |
if ($pdf_url || $pdf_link) { |
| 1687 |
$types[] = 'pdf'; |
| 1688 |
} |
| 1689 |
} |
| 1690 |
} |
| 1691 |
|
| 1692 |
// Recursively check elements |
| 1693 |
if (isset($element['elements'])) { |
| 1694 |
$types = array_merge($types, self::detect_types_from_elementor_data($element['elements'])); |
| 1695 |
} |
| 1696 |
} |
| 1697 |
|
| 1698 |
return $types; |
| 1699 |
} |
| 1700 |
|
| 1701 |
/** |
| 1702 |
* Detect embed type from URL using Embera's provider detection |
| 1703 |
* |
| 1704 |
* @param string $url |
| 1705 |
* @return array |
| 1706 |
*/ |
| 1707 |
private static function detect_type_from_url($url) |
| 1708 |
{ |
| 1709 |
$types = []; |
| 1710 |
|
| 1711 |
if (empty($url) || !is_string($url)) { |
| 1712 |
return $types; |
| 1713 |
} |
| 1714 |
|
| 1715 |
// Use Helper class which leverages Embera's built-in provider detection |
| 1716 |
if (class_exists('\EmbedPress\Includes\Classes\Helper')) { |
| 1717 |
$provider_name = Helper::get_provider_name($url); |
| 1718 |
|
| 1719 |
if (!empty($provider_name)) { |
| 1720 |
// Normalize provider name to lowercase for consistency |
| 1721 |
$provider_name = strtolower($provider_name); |
| 1722 |
|
| 1723 |
// Map provider names to asset provider keys |
| 1724 |
$provider_map = [ |
| 1725 |
'youtube' => 'youtube', |
| 1726 |
'youtubechannel' => 'youtube-channel', |
| 1727 |
'vimeo' => 'vimeo', |
| 1728 |
'instagram' => 'instagram', |
| 1729 |
'instagramfeed' => 'instagram', |
| 1730 |
'opensea' => 'opensea', |
| 1731 |
'wistia' => 'wistia', |
| 1732 |
'twitch' => 'twitch', |
| 1733 |
'meetup' => 'meetup', |
| 1734 |
'googledocs' => 'google-docs', |
| 1735 |
'googlesheets' => 'google-sheets', |
| 1736 |
'googleslides' => 'google-slides', |
| 1737 |
'googlephotos' => 'google-photos', |
| 1738 |
]; |
| 1739 |
|
| 1740 |
// Check if provider name matches our map |
| 1741 |
if (isset($provider_map[$provider_name])) { |
| 1742 |
$types[] = $provider_map[$provider_name]; |
| 1743 |
return $types; |
| 1744 |
} |
| 1745 |
|
| 1746 |
// Check for document types from Helper's response |
| 1747 |
if (strpos($provider_name, 'document_') === 0) { |
| 1748 |
$types[] = 'document'; |
| 1749 |
return $types; |
| 1750 |
} |
| 1751 |
} |
| 1752 |
} |
| 1753 |
|
| 1754 |
// Fallback to manual detection for special cases not handled by Embera |
| 1755 |
$url_lower = strtolower($url); |
| 1756 |
|
| 1757 |
// YouTube special cases (channel, live, shorts) |
| 1758 |
if (strpos($url_lower, 'youtube.com') !== false || strpos($url_lower, 'youtu.be') !== false) { |
| 1759 |
if (preg_match('#/(channel|c|user)/[\w-]+/live$|/@[\w-]+/live$#i', $url_lower)) { |
| 1760 |
$types[] = 'youtube-live'; |
| 1761 |
} elseif (strpos($url_lower, '/channel/') !== false || strpos($url_lower, '/c/') !== false || strpos($url_lower, '/@') !== false) { |
| 1762 |
$types[] = 'youtube-channel'; |
| 1763 |
} elseif (strpos($url_lower, '/live') !== false) { |
| 1764 |
$types[] = 'youtube-live'; |
| 1765 |
} elseif (strpos($url_lower, '/shorts/') !== false) { |
| 1766 |
$types[] = 'youtube-shorts'; |
| 1767 |
} else { |
| 1768 |
$types[] = 'youtube'; |
| 1769 |
} |
| 1770 |
} |
| 1771 |
// Google Photos (shared album links) |
| 1772 |
elseif (strpos($url_lower, 'photos.app.goo.gl') !== false || strpos($url_lower, 'photos.google.com') !== false) { |
| 1773 |
$types[] = 'google-photos'; |
| 1774 |
} |
| 1775 |
// PDF detection |
| 1776 |
elseif (preg_match('/\.pdf$/i', $url)) { |
| 1777 |
$types[] = 'pdf'; |
| 1778 |
} |
| 1779 |
// Document detection |
| 1780 |
elseif (preg_match('/\.(doc|docx|ppt|pptx|xls|xlsx)$/i', $url)) { |
| 1781 |
$types[] = 'document'; |
| 1782 |
} |
| 1783 |
// Self-hosted video |
| 1784 |
elseif (preg_match('/\.(mp4|mov|avi|wmv|flv|mkv|webm|mpeg|mpg)$/i', $url)) { |
| 1785 |
$types[] = 'video'; |
| 1786 |
} |
| 1787 |
// Self-hosted audio |
| 1788 |
elseif (preg_match('/\.(mp3|wav|ogg|aac)$/i', $url)) { |
| 1789 |
$types[] = 'audio'; |
| 1790 |
} |
| 1791 |
|
| 1792 |
return $types; |
| 1793 |
} |
| 1794 |
} |
| 1795 |
|