# Sphere Photo Viewer Widget

> 360° interactive panorama viewer powered by Three.js + photo-sphere-viewer. Supports hotspot markers with tooltips and detailed content panels.

**Class file:** `includes/Elements/Sphere_Photo_Viewer.php` (1,090 lines)
**Slug:** `sphere-photo-viewer` (widget id `eael-sphere-photo-viewer`)
**Public docs:** <https://essential-addons.com/elementor/docs/sphere-photo-viewer/>
**Pro-shared:** Pro-only. **Vendor libs: Three.js (from Lite) + photo-sphere-viewer (Pro-bundled)**.

## Overview

Renders an interactive equirectangular panorama (360° photo). User pans/zooms with mouse / touch. Optional markers placed on the sphere via lat/lon — each marker shows a tooltip on hover and a content panel on click. Built on photo-sphere-viewer which uses Three.js as 3D rendering engine. Three.js loaded from **Lite's** vendored copy via `EAEL_PLUGIN_PATH`.

## Pro vs Lite

Pro-only widget. Pro depends on Lite for the Three.js vendor lib (one of the more pronounced Pro→Lite asset reuse cases).

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Sphere_Photo_Viewer.php` | Widget class (1,090 lines) |
| `src/css/view/sphere-photo-viewer.scss` → `assets/front-end/css/view/sphere-photo-viewer.min.css` | Marker tooltip + panel styling |
| `src/js/view/sphere-photo-viewer.js` → `assets/front-end/js/view/sphere-photo-viewer.min.js` | PhotoSphereViewer init + marker bind |
| `assets/front-end/css/lib-view/photo-sphere-viewer/core.min.css` | Vendor — core CSS |
| `assets/front-end/css/lib-view/photo-sphere-viewer/markers.min.css` | Vendor — markers plugin CSS |
| `EAEL_PLUGIN_PATH/assets/front-end/js/lib-view/three/three.min.js` | **Lite** — Three.js |
| `assets/front-end/js/lib-view/photo-sphere-viewer/core.min.js` | Vendor — PhotoSphereViewer core |
| `assets/front-end/js/lib-view/photo-sphere-viewer/markers.min.js` | Vendor — markers plugin |
| `config.php` entry `'sphere-photo-viewer'` | Multiple lib CSS/JS + Pro CSS + Pro JS |

## Architecture

- **Three.js from Lite, photo-sphere-viewer from Pro** — Lite owns Three.js (used by other Lite widgets too); Pro ships its own photo-sphere-viewer. Mixed `EAEL_PLUGIN_PATH` + `EAEL_PRO_PLUGIN_PATH` config — see [`docs/architecture/asset-loading.md`](../architecture/asset-loading.md).
- **Markers as Repeater** — `ea_spv_markers_list` (line 458). Each marker has tooltip / content / image / position (lat-lon coordinates on the sphere).
- **`title_field` for Repeater label** — `{{ ea_spv_markers_tooltip }}` (line 469) — Elementor's Repeater editor shows the tooltip text as the row label.
- **Two-tier marker info** — tooltip (short, on hover) + content panel (longer, on click). User configures both per marker.
- **Optional marker image** — `ea_spv_markers_img` (line 431). Custom icon instead of default marker dot.
- **Markers toggle (`ea_spv_markers_switch`)** — gates the entire markers Repeater + style controls.
- **`'context' => 'view'` on `sphere-photo-viewer.min.css`** with `'type' => 'lib'` (atypical — Pro's own self-CSS marked as `'lib'` in config). Verify if intentional.

## Render Output

```html
<div class="eael-sphere-photo-viewer-wrap">
  <div class="eael-sphere-photo-viewer"
       id="eael-spv-{element-id}"
       data-panorama="{panorama-image-url}"
       data-markers='[{"id":"...","tooltip":"...","position":{"yaw":0,"pitch":0}},...]'>
  </div>
  <!-- Marker content panels (hidden, shown on marker click) -->
  [?] <div class="eael-spv-marker-content" data-marker-id="...">
    <h3>{tooltip-title}</h3>
    [?] <img src="{marker-image}" />
    <div>{detailed-content}</div>
  </div>
</div>
```

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| Panorama image | Content → Photo | MEDIA | Equirectangular 360° image |
| Default yaw / pitch | Content → Photo | NUMBER | Initial view direction |
| Zoom controls | Content → Photo | various | Min/max zoom |
| `ea_spv_markers_switch` | Content → Markers | SWITCHER | Enable markers |
| `ea_spv_markers_list` | Content → Markers | REPEATER | Marker list (when enabled) |
| (Per-marker) tooltip | Inside Repeater | TEXT | Hover tooltip |
| (Per-marker) content | Inside Repeater | WYSIWYG | Click-panel content |
| (Per-marker) image | Inside Repeater | MEDIA | Custom marker icon |
| (Per-marker) yaw / pitch | Inside Repeater | NUMBER | Marker position on sphere |
| Marker tooltip / panel styling | Style → Markers | various | Per-region styling |

## Conditional Dependencies

```text
ea_spv_markers_switch = 'yes' → marker Repeater + style controls visible
Per-marker image set → custom marker icon; else default dot
```

## JavaScript Lifecycle

`src/js/view/sphere-photo-viewer.js`:

```js
var SphereViewer = function( $scope, $ ) {
    var $wrap = $scope.find( '.eael-sphere-photo-viewer' );
    if ( ! $wrap.length ) return;
    if ( typeof PhotoSphereViewer === 'undefined' ) return;

    var viewer = new PhotoSphereViewer.Viewer( {
        container: $wrap[0],
        panorama: $wrap.data( 'panorama' ),
        // ... default yaw/pitch, zoom from data attrs
        plugins: [ [ PhotoSphereViewer.MarkersPlugin, {
            markers: $wrap.data( 'markers' )
        } ] ],
    } );

    viewer.getPlugin( PhotoSphereViewer.MarkersPlugin ).addEventListener( 'select-marker', function( e ) {
        // show marker content panel
    } );
};
```

## Hooks & Filters

Standard widget render. No Pro-emitted hooks.

## Common Issues

| Symptom | Likely cause | Diagnose | Fix |
| --- | --- | --- | --- |
| Black sphere / no panorama | Image not equirectangular (regular photo) | Inspect image aspect ratio | Use 2:1 equirectangular panorama |
| `PhotoSphereViewer is not defined` | Three.js missing (Lite deactivated?) | DevTools console | Activate Lite — Three.js loads from Lite path |
| Markers appear at wrong positions | yaw/pitch units mismatch (radians vs degrees) | Inspect PhotoSphereViewer docs version | Verify coord unit in JSON-encoded markers data attribute |
| Performance jank on mobile | Large panorama image (>5MB) | Inspect image file size | Compress; recommend <2MB for mobile |
| Marker tooltips truncate | No max-width on tooltip | Inspect computed CSS | Use Pro tooltip style controls or override CSS |

## Known Limitations

- **Three.js from Lite** — Pro non-functional if Lite removed
- **Heavy vendor stack** (Three.js + PhotoSphereViewer core + markers plugin) — ~300KB+ JS
- **No `prefers-reduced-motion`** — auto-rotate / inertia animations always on
- **Large panorama images** stall page render — no lazy-load
- **`'type' => 'lib'` on Pro's own CSS** in config.php — atypical; verify if intentional
- **No fallback** when panorama is invalid — black sphere, no error message to user
- **WebGL required** — older browsers without WebGL support show black sphere

## Cross-References

- Architecture: [`docs/architecture/asset-loading.md`](../architecture/asset-loading.md) — mixed `EAEL_PLUGIN_PATH` + `EAEL_PRO_PLUGIN_PATH`
- Sibling: [`flip-carousel.md`](flip-carousel.md), [`image-comparison.md`](image-comparison.md) (verify exists) — other heavy-asset media widgets
- Shared patterns: [`_patterns.md`](_patterns.md)
