# Asset Loading (Pro Overlay)

How Pro hooks into Lite's `Asset_Builder` for conditional per-page CSS/JS enqueuing, plus the few asset-related responsibilities Pro owns directly.

## Context

Lite's `Asset_Builder` (`Essential_Addons_Elementor\Classes\Asset_Builder`) is the single mechanism for enqueuing widget assets on the front end. It reads the `registered_elements` and `registered_extensions` arrays Lite built (which include Pro's contributions via the `eael/registered_elements` and `eael/registered_extensions` filters) and enqueues only the CSS/JS needed for widgets actually present on the current page.

**Pro does not own its own `Asset_Builder`.** The full asset-loading lifecycle — popup detection, template/shortcode awareness, CSS print modes, post-meta caching — lives in Lite and is documented in `../../../essential-addons-for-elementor-lite/docs/architecture/asset-loading.md`. This Pro doc captures only the **delta** that's specific to Pro overlay behaviour.

## Verified facts

### Pro's contribution is the merged `$GLOBALS['eael_pro_config']`

Pro contributes assets to `Asset_Builder` exclusively through the merged config arrays. Pro's `config.php` ships `'elements'` and `'extensions'` keys with the same shape as Lite's config — slug → `[ 'class' => ..., 'dependency' => [ 'css' => [...], 'js' => [...] ] ]`. Pro's `Bootstrap::inject_new_elements` / `inject_new_extensions` callbacks merge these into Lite's arrays via `array_merge_recursive` (see [`pro-lite-bridge.md`](pro-lite-bridge.md)).

By the time Lite instantiates `new Asset_Builder( $this->registered_elements, $this->registered_extensions )` (Lite `Bootstrap.php:128`), Pro's entries are already present. `Asset_Builder` cannot distinguish a Pro entry from a Lite entry — and **does not need to** — both follow the same registry contract.

### Dependency shape in Pro config

Each Pro element/extension declares its assets in the same `'dependency'` structure Lite uses:

```php
'custom-cursor' => [
    'class'      => '\Essential_Addons_Elementor\Pro\Extensions\Custom_Cursor',
    'dependency' => [
        'css' => [
            [ 'file' => EAEL_PRO_PLUGIN_PATH . 'assets/front-end/css/lib-view/cursor/ghost-following.min.css',
              'type' => 'lib', 'context' => 'view' ],
            // ...more
        ],
        'js' => [
            [ 'file' => EAEL_PLUGIN_PATH . '/assets/front-end/js/lib-view/dom-purify/purify.min.js',
              'type' => 'lib', 'context' => 'view' ],  // ← shared with Lite (uses Lite's path)
            [ 'file' => EAEL_PRO_PLUGIN_PATH . 'assets/front-end/js/lib-view/gsap/gsap.min.js',
              'type' => 'lib', 'context' => 'view' ],
            // ...more
        ],
    ],
],
```

Three field-level conventions hold:

| Field      | Values                          | Meaning                                                                                                |
| ---------- | ------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `file`     | Absolute filesystem path        | Use `EAEL_PRO_PLUGIN_PATH` for Pro-owned assets, `EAEL_PLUGIN_PATH` (Lite constant) for shared Lite vendor libs |
| `type`     | `'self'`, `'lib'`               | `'self'` = Pro's compiled source (gets cache-bust); `'lib'` = third-party (vendor path)                |
| `context`  | `'view'`, `'edit'`              | `'view'` = front-end render; `'edit'` = Elementor editor                                               |

### Mixed-path entries

A single Pro extension can depend on assets from **both** Pro's own plugin dir AND Lite's plugin dir:

```php
'js' => [
    [ 'file' => EAEL_PLUGIN_PATH . '/assets/front-end/js/lib-view/dom-purify/purify.min.js',     // ← Lite
      'type' => 'lib', 'context' => 'view' ],
    [ 'file' => EAEL_PRO_PLUGIN_PATH . 'assets/front-end/js/lib-view/gsap/gsap.min.js',           // ← Pro
      'type' => 'lib', 'context' => 'view' ],
]
```

This is intentional — Pro reuses Lite's vendor copies of libraries Lite already ships (DOMPurify, Isotope, imagesLoaded). Pro ships its own copies only for libraries Lite doesn't have (GSAP, jarallax, tippy, popper, particles, custom cursors). This avoids double-bundling.

**Constraint:** when a Pro entry references `EAEL_PLUGIN_PATH`, Pro depends on Lite being active (which it always is via the bridge gate). The constant is defined by Lite's entry file.

### Pro's own enqueue work — `Traits/Enqueue.php`

`Asset_Builder` handles the bulk of enqueuing. Pro additionally registers extra scripts via its own `Enqueue` trait, invoked by Lite's enqueue pipeline through a filter/action:

`includes/Traits/Enqueue.php` — sample of `before_enqueue_scripts( $widgets )`:

```php
public function before_enqueue_scripts($widgets)
{
    $version = defined('WP_DEBUG') ? time() : EAEL_PRO_PLUGIN_VERSION;

    if (in_array('adv-google-map', $widgets) && '' != get_option('eael_save_google_map_api')) {
        wp_enqueue_script(
            'eael-google-map-api',
            'https://maps.googleapis.com/maps/api/js?key=' . get_option('eael_save_google_map_api') . '&callback=Function.prototype',
            ['jquery'],
            $version,
            false
        );
    }

    if ( in_array( 'login-register', $widgets ) ) {
        wp_enqueue_script( 'eael-google-api', 'https://accounts.google.com/gsi/client', ...);
        wp_register_script( 'eael-gsap', EAEL_PRO_PLUGIN_URL . 'assets/front-end/js/lib-view/gsap/gsap.min.js', ...);
        wp_enqueue_script( 'eael-gsap' );
    }
}
```

What this does:

1. **Adv Google Map** widget — enqueues Google Maps JS from `maps.googleapis.com` with the user-stored API key. Conditional on the widget being present on the page.
2. **Login/Register** widget — enqueues Google Identity Services SDK + GSAP for animated UI. Login/Register is a Lite widget but the Pro-extension features (social login, GSAP animations) need extra scripts.

### Cache-bust version strategy

```php
$version = defined('WP_DEBUG') ? time() : EAEL_PRO_PLUGIN_VERSION;
```

- **Production** (`WP_DEBUG` not defined / false) — pinned to `EAEL_PRO_PLUGIN_VERSION` (currently `6.8.1`). Asset URL stays stable across requests, browsers cache normally, version bumps invalidate cache on plugin update.
- **Dev** (`WP_DEBUG` defined / true) — `time()` per request, always new URL, no browser cache. Useful for local dev.

This is **only** for the manually enqueued scripts in `before_enqueue_scripts`. `Asset_Builder` has its own versioning (file-mtime-based for `'self'` types, plugin-version for `'lib'` types).

### Script localizer — runtime data injection

`Enqueue::script_localizer( $object )` extends Lite's localized JS object with Pro-specific runtime data:

| Key                       | Value                                                  | Used by                            |
| ------------------------- | ------------------------------------------------------ | ---------------------------------- |
| `ajaxurl`                 | `admin_url('admin-ajax.php')`                          | All Pro AJAX-driven widgets        |
| `ParticleThemesData`      | Five JSON preset themes (default, nasa, bubble, snow, nyan_cat) for `section-particles` extension | Particles.js init in section-particles |
| `eael_login_nonce`        | `wp_create_nonce('eael-login-action')`                 | Login/Register social login submit |
| `eael_register_nonce`     | `wp_create_nonce('eael-register-action')`              | Login/Register registration submit |
| `eael_lostpassword_nonce` | `wp_create_nonce('eael-lostpassword-action')`          | Lost-password flow                 |
| `eael_resetpassword_nonce`| `wp_create_nonce('eael-resetpassword-action')`         | Password reset flow                |

The `ParticleThemesData` payload is sizeable (~6KB of JSON in five preset strings). It's included in every page that loads the localized object, regardless of whether the particles extension is on the page. See "What's missing" for the size-optimization gap.

The five preset JSON strings are **also stored** in `includes/Extensions/particle-themes.php` (a PHP file that `return`s an array). Today the JS-side data in `script_localizer` is **a hardcoded copy** of those strings, not a `require` of the file. The two should be unified — if `particle-themes.php` is edited, `Enqueue.php` won't pick up the change.

### Pro lib-view assets

| Library                        | Path                                                                                                | Used by                                              |
| ------------------------------ | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| GSAP + plugins (ScrollTrigger, SplitText) | `assets/front-end/js/lib-view/gsap/`                                                     | Smooth_Animation extension, Custom_Cursor, Login/Register Pro UI |
| jarallax + jquery-parallax     | `assets/front-end/js/lib-view/jarallax/`, `.../jquery-parallax/`                                    | EAEL_Parallax_Section extension                      |
| particles.js                   | `assets/front-end/js/lib-view/particles/`                                                           | EAEL_Particle_Section extension                      |
| popper + tippy                 | `assets/front-end/js/lib-view/popper/`, `.../tippy/`; CSS at `assets/front-end/css/lib-view/tippy/` | EAEL_Tooltip_Section extension                       |
| Cursor effects                 | `assets/front-end/js/lib-view/cursor/` (90s, ghost-following, glowing-boxes, ink-line, phantom-smoke, pointer-particle, color-balls) | Custom_Cursor extension                              |

These are checked-in compiled libraries, not built from source. When upgrading a vendor library, replace the file in place — webpack does not touch `lib-view/`.

### Admin asset

`assets/admin/css/admin.css` (240 lines) — single admin stylesheet shared across Pro's contributed admin UI (license markup added to Lite's `eael-settings` page, settings tabs Pro injects). Hand-written, no SCSS source. No minified variant.

## What's missing

1. **`ParticleThemesData` is duplicated.** Same JSON in `Traits/Enqueue.php:34-39` and `Extensions/particle-themes.php`. Single source of truth needed — load from the PHP file at runtime, drop the inline JSON in Enqueue.
2. **`ParticleThemesData` is sent on every page-localize.** The 5-preset JSON payload (~6KB) ships on every page that uses Pro, even when Particle Section is not on the page. Move to a per-page conditional inside `before_enqueue_scripts`, only when `'section-particles'` is in `$widgets`.
3. **No automated check that Pro's `config.php` paths exist on disk.** A typo in a `file` entry produces a silent miss — the asset just doesn't load. Add a `npm run check:assets` script that verifies every `'self'` and `'lib'` path resolves.
4. **`time()`-based cache-bust may regress on shared hosting with `WP_DEBUG` accidentally `true`.** Some hosts ship a `wp-config.php` with `define('WP_DEBUG', true)` in production. Pro then ships uncached assets to every visitor. The check should be tighter — `WP_DEBUG === true` plus an explicit `EAEL_DEV` opt-in.
5. **No admin CSS minification.** `admin.css` is served raw (240 lines, ~6KB unminified). Minify on build, ship `.min.css` for production.
6. **No SCSS source for admin.css.** The file is hand-edited. If a design-system migration adds tokens (see `../design-system/README.md`), admin styles will need a SCSS source pipeline.
7. **Some Pro entries reference `EAEL_PLUGIN_PATH` (Lite's constant) without a fallback.** If a future refactor moves the referenced library out of Lite, every Pro entry pointing at it silently breaks. Add a `class_exists`-style check at config load time.
8. **Heavy GSAP enqueue for Login/Register.** GSAP is ~70KB minified and is enqueued whenever Login/Register is on the page, regardless of whether the animation feature is enabled. Gate on a Login/Register setting.

## Acceptance

This doc accurately reflects:

- Pro contributes assets via `array_merge_recursive` into Lite's element/extension registries ✓
- Pro entries mix `EAEL_PRO_PLUGIN_PATH` and `EAEL_PLUGIN_PATH` references intentionally ✓
- `Enqueue::before_enqueue_scripts` enqueues Google Maps JS + Google Identity Services + GSAP per-widget ✓
- `ParticleThemesData` is duplicated between `Enqueue.php` and `particle-themes.php` ✓
- Admin assets are a single hand-written `admin.css` (240 lines) ✓
- Cache-bust version = `WP_DEBUG ? time() : EAEL_PRO_PLUGIN_VERSION` ✓

If `config.php` structure or `Enqueue` behaviour changes, update this doc in the same PR.

## Pairs with

- `pro-lite-bridge.md` — explains how Pro's config arrays reach Lite's `Asset_Builder`
- Lite's `docs/architecture/asset-loading.md` — full `Asset_Builder` lifecycle that Pro overlays on
- `external-api-integrations.md` — Google Maps JS, Google Identity SDK are external scripts enqueued here

## Related

- `extensions.md` — the 8 Pro extensions whose assets are listed here
- `../../webpack.config.js` — build pipeline producing `assets/front-end/*.min.{js,css}`

## Out of scope

- `Asset_Builder`'s own internals (popup detection, post-meta caching, CSS print modes) — that's Lite's doc
- Webpack entry-point auto-discovery — that's `.claude/rules/asset-pipeline.md`
- Editor-mode asset loading (`'context' => 'edit'`) — same mechanism as Lite, no Pro-specific delta
