(function () { 'use strict'; if (!document.querySelector('.customization-3d-wrapper')) return; const get3DViewer = () => window.Customization3DViewer; const load3DModel = (modelUrl, productId) => { const viewer = get3DViewer(); return viewer ? viewer.loadModel(modelUrl, productId) : Promise.resolve(false); }; const clear3DModel = () => { const viewer = get3DViewer(); return viewer ? viewer.clearModel() : Promise.resolve(); }; const show3DEmpty = () => { get3DViewer()?.showEmpty(); }; const get3DModelUrl = (productId, variantId, wrapper) => { const viewer = get3DViewer(); return viewer ? viewer.getModelUrl(productId, variantId, wrapper) : Promise.resolve(null); }; const get3DHotspots = () => { const hotspots = window.CUSTOMIZATION_3D_HOTSPOTS; if (!Array.isArray(hotspots)) return []; return hotspots .filter((item) => item && typeof item === 'object') .map((item) => { const meshName = String(item.meshName || '').trim(); if (!meshName) return null; let offset = [0, 0, 0]; if (Array.isArray(item.offset) && item.offset.length >= 3) { const parsed = item.offset.slice(0, 3).map((v) => Number(v)); if (parsed.every(Number.isFinite)) { const maxAbs = Math.max(...parsed.map((v) => Math.abs(v))); offset = parsed; } } const next = { id: String(item.id || meshName), name: String(item.name || item.id || meshName), meshName, offset, }; const color = String(item.color || '').trim(); if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) next.color = color; const r = Number(item.radius); if (Number.isFinite(r) && r > 0) { next.radius = Math.min(Math.max(r, 0.5), 30); } else { const defaultRadius = Number(window.CUSTOMIZATION_3D_HOTSPOT_RADIUS_DEFAULT); next.radius = Number.isFinite(defaultRadius) && defaultRadius > 0 ? Math.min(30, defaultRadius) : 18; } const icon = String(item.icon || '').trim(); if (icon && (/^https?:\/\//i.test(icon) || icon.startsWith('//'))) { next.icon = icon; } if (item.payload && typeof item.payload === 'object' && !Array.isArray(item.payload)) { next.payload = item.payload; } return next; }) .filter(Boolean); }; const getHotspotActionConfig = (detail = {}) => { const all = window.CUSTOMIZATION_3D_HOTSPOT_ACTIONS; if (!all || typeof all !== 'object') return null; return all[detail.id] || all[detail.name] || null; }; const handle3DHotspotClick = (event) => { const detail = event?.detail || {}; const viewer = get3DViewer(); if (!viewer) return; if (typeof window.CUSTOMIZATION_3D_ON_HOTSPOT_CLICK === 'function') { try { window.CUSTOMIZATION_3D_ON_HOTSPOT_CLICK(detail, viewer); } catch (err) { console.warn('[Customization] CUSTOMIZATION_3D_ON_HOTSPOT_CLICK failed:', err); } } const action = getHotspotActionConfig(detail); if (action?.door) { viewer.door?.toggle(action.door); } if (action?.clipping) { const clip = action.clipping; if (typeof clip.height === 'number') { viewer.clipping?.setY( clip.height, clip.keepBelow !== false, Array.isArray(clip.meshNames) ? clip.meshNames : [] ); } } }; const setup3DEventBridge = () => { if (document.documentElement.dataset.customization3dEventsBoundCopy === '1') return; document.documentElement.dataset.customization3dEventsBoundCopy = '1'; let hotspotRenderTimer = null; const renderConfiguredHotspots = () => { if (hotspotRenderTimer) clearTimeout(hotspotRenderTimer); hotspotRenderTimer = setTimeout(() => { hotspotRenderTimer = null; const hotspots = get3DHotspots(); const viewer = get3DViewer(); viewer?.hotspot?.clear?.(); if (!hotspots.length) return; viewer?.hotspot?.render(hotspots); }, 200); }; document.addEventListener('3d:scene:ready', renderConfiguredHotspots); document.addEventListener('3d:hotspots:update', renderConfiguredHotspots); document.addEventListener('3d:hotspot:click', handle3DHotspotClick); }; const setupWheelScrollLockOn3DContainer = () => { const container = document.querySelector('[data-3d-container]'); if (!container || container.dataset.customization3dWheelLockCopy === '1') return; container.dataset.customization3dWheelLockCopy = '1'; container.addEventListener( 'wheel', (e) => { if (!container.contains(e.target)) return; e.preventDefault(); }, { capture: true, passive: false } ); }; const refreshHotspots = () => { document.dispatchEvent(new CustomEvent('3d:hotspots:update', { bubbles: true })); }; window.Customization3DInteractions = { load3DModel, clear3DModel, show3DEmpty, get3DModelUrl, get3DHotspots, refreshHotspots, }; const init3DInteractions = () => { setup3DEventBridge(); setupWheelScrollLockOn3DContainer(); }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init3DInteractions); } else { init3DInteractions(); } })();