iframe Viewer Page: Complete Example
This is the page that runs inside the iframe. Host it on your server or CDN.
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>