import { ModelViewer } from './model-viewer/ModelViewer'; import { decodeBase64 } from './utils/base64'; /** * Initializes 3D models on the page. * @param selector - CSS selector to find the elements to initialize. Default is '.press3d'. * * import { init3DViewers } from './init-3d-viewers'; * document.addEventListener('DOMContentLoaded', () => { * init3DViewers(); * }); */ export function init3DViewers(selector = '.press3d'): void { /* * Gutenberg editor detekce * Ve WordPress editoru (admin) tento skript nemá běžet, * protože editor už sám vytváří instanci ModelVieweru v edit.tsx. */ if ( document.body.classList.contains('block-editor-page') || document.body.classList.contains('wp-admin') ) { //console.info('[Press3D] Skipping init-3d-viewers in Gutenberg editor.'); return; } const blocks = document.querySelectorAll(selector); blocks.forEach((block) => { const base64Data = block.dataset.b64; if (!base64Data || base64Data.length === 0) { /* * Pokud je hodnota atributu prázdná, přeskočíme tento element * V editoru může být tento stav dočasný, takže není potřeba zobrazovat chybu. * Děje se to jen při reloadu stránky při editaci v Guttenbergu */ // alert('Test: attribut b64 je prázdný'); return; } const result = decodeBase64(base64Data); if (!result.success || !result.data) return; const data = result.data; let container: HTMLElement = block; // If link is present in data, wrap viewer in anchor element if (data.linkUrl) { const link = document.createElement('a'); link.style.display = 'block'; link.style.width = '100%'; link.style.height = '100%'; // Sanitize URL to prevent XSS (javascript: protocol) // Allow http, https, mailto, tel, and relative URLs const isSafeUrl = /^(https?:\/\/|mailto:|tel:|\/|#)/i.test(data.linkUrl); if (isSafeUrl) { link.href = data.linkUrl; } else { link.href = '#'; console.warn('[Press3D] Blocked potentially unsafe URL:', data.linkUrl); } if (data.linkOpenInNewTab) { link.target = '_blank'; link.rel = 'noopener noreferrer'; } // Add accessibility label to the link link.setAttribute('aria-label', data.altText || '3D Model'); block.appendChild(link); container = link; } else { // Ensure the container has accessibility attributes (important for shortcodes) container.setAttribute('role', 'img'); container.setAttribute('aria-label', data.altText || '3D Model'); } const viewer = new ModelViewer(container, data); viewer .loadAsync() .catch((err) => console.error('❌ Error loading model:', err)); viewer.on('destroy', () => { }); //console.log('Viewer destroyed')); }); }