Skip to content

iframe Viewer Page: Complete Example

This is the page that runs inside the iframe. Host it on your server or CDN.

Back to Embedding guide

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Wedding Band Viewer</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    html, body, #root { width: 100%; height: 100%; overflow: hidden; }
  </style>
</head>
<body>
  <div id="root"></div>

  <script src="https://releases.ijewel3d.com/libs/mini-viewer/latest/bundle.iife.js"></script>

  <script>
    const root = document.getElementById('root');
    const params = new URLSearchParams(window.location.search);

    const project = {
      name: 'Wedding Band Builder',
      version: 'v5',
      basePath: params.get('basePath') || 'https://your-cdn.com/wbb-assets/',
      plugins: {
        WeddingBandBuilder: {
          manifestUrl: params.get('manifest') || 'wedding-band-project.json',
          showUI: params.get('ui') !== 'false',
        },
      },
    };

    new ijewelViewer.Viewer(root, project, {
      showCard: false,
      showSwitchNode: false,
      showUiButtons: params.get('ui') !== 'false',
      showConfigurator: false,
      showZoomButtons: params.get('ui') !== 'false',
      enableZoom: true,
    });

    // Bridge: forward API to parent via postMessage
    window.addEventListener('ijewel-viewer-ready', async (e) => {
      const viewer = e.detail.viewer;

      // Wait for the WBB plugin to initialize
      const wbb = await new Promise((resolve, reject) => {
        let attempts = 0;
        const check = setInterval(() => {
          const p = viewer.getPluginByType('WeddingBandBuilder');
          if (p?.controller) { clearInterval(check); resolve(p); }
          else if (++attempts > 100) { clearInterval(check); reject(new Error('Plugin timeout')); }
        }, 200);
      });

      const api = wbb.controller;

      // Controller is accessible via:
      // iframe.contentWindow.ijewelViewer.getPluginByType('WeddingBandBuilder').controller

      // Notify parent that we're ready
      window.parent.postMessage({ event: 'ready' }, '*');

      // Forward all API events to parent
      const forwardEvents = [
        'profile:changed', 'dimensions:changed', 'material:changed',
        'partition:changed', 'diamonds:changed', 'edge:changed',
        'engraving:changed', 'finish:changed', 'build:started',
        'build:complete', 'price:updated', 'band:switched',
        'pose:changed', 'theme:changed', 'error', 'log',
      ];
      forwardEvents.forEach(evt => {
        api.events.on(evt, (data) => {
          window.parent.postMessage({ event: evt, data }, '*');
        });
      });

      // Handle commands from parent
      window.addEventListener('message', async (msg) => {
        const { id, method, args } = msg.data || {};
        if (!id || !method) return;

        try {
          const result = await api[method]?.(...(args || []));
          window.parent.postMessage({ id, result: result ?? null }, '*');
        } catch (err) {
          window.parent.postMessage({
            id,
            error: { message: err.message, source: 'api' },
          }, '*');
        }
      });
    });
  </script>
</body>
</html>