| | |
| | |
| |
|
| | const currentScriptTag = document.currentScript; |
| |
|
| | (async function() { |
| | |
| | let scriptTag = currentScriptTag; |
| | if (!scriptTag) { |
| | const scripts = document.getElementsByTagName('script'); |
| | for (let i = 0; i < scripts.length; i++) { |
| | if (scripts[i].src.includes('interface.js') && scripts[i].hasAttribute('data-config')) { |
| | scriptTag = scripts[i]; |
| | break; |
| | } |
| | } |
| | if (!scriptTag && scripts.length > 0) { |
| | scriptTag = scripts[scripts.length - 1]; |
| | } |
| | } |
| |
|
| | const configUrl = scriptTag.getAttribute('data-config'); |
| | let config = {}; |
| | if (configUrl) { |
| | try { |
| | const response = await fetch(configUrl); |
| | config = await response.json(); |
| | } catch (error) { |
| | return; |
| | } |
| | } else { |
| | return; |
| | } |
| |
|
| | if (config.css_url) { |
| | const linkEl = document.createElement('link'); |
| | linkEl.rel = "stylesheet"; |
| | linkEl.href = config.css_url; |
| | document.head.appendChild(linkEl); |
| | } |
| |
|
| | const instanceId = Math.random().toString(36).substr(2, 8); |
| |
|
| | let aspectPercent = "100%"; |
| | if (config.aspect) { |
| | if (config.aspect.includes(":")) { |
| | const parts = config.aspect.split(":"); |
| | const w = parseFloat(parts[0]); |
| | const h = parseFloat(parts[1]); |
| | if (!isNaN(w) && !isNaN(h) && w > 0) { |
| | aspectPercent = (h / w * 100) + "%"; |
| | } |
| | } else { |
| | const aspectValue = parseFloat(config.aspect); |
| | if (!isNaN(aspectValue) && aspectValue > 0) { |
| | aspectPercent = (100 / aspectValue) + "%"; |
| | } |
| | } |
| | } else { |
| | const parentContainer = scriptTag.parentNode; |
| | const containerWidth = parentContainer.offsetWidth; |
| | const containerHeight = parentContainer.offsetHeight; |
| | if (containerWidth > 0 && containerHeight > 0) { |
| | aspectPercent = (containerHeight / containerWidth * 100) + "%"; |
| | } |
| | } |
| |
|
| | const widgetContainer = document.createElement('div'); |
| | widgetContainer.id = 'ply-widget-container-' + instanceId; |
| | widgetContainer.classList.add('ply-widget-container'); |
| | widgetContainer.style.height = "0"; |
| | widgetContainer.style.paddingBottom = aspectPercent; |
| | widgetContainer.setAttribute('data-original-aspect', aspectPercent); |
| |
|
| | const tooltipsButtonHTML = config.tooltips_url |
| | ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">⦿</button>` |
| | : ''; |
| |
|
| | widgetContainer.innerHTML = ` |
| | <div id="viewer-container-${instanceId}" class="viewer-container"> |
| | <div id="progress-dialog-${instanceId}" class="progress-dialog"> |
| | <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress> |
| | </div> |
| | <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button> |
| | <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button> |
| | <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn"> |
| | <span class="reset-icon">⟲</span> |
| | </button> |
| | ${tooltipsButtonHTML} |
| | <div id="menu-content-${instanceId}" class="menu-content"> |
| | <span id="help-close-${instanceId}" class="help-close">×</span> |
| | <div class="help-text"></div> |
| | </div> |
| | </div> |
| | <div id="tooltip-panel" class="tooltip-panel" style="display: none;"> |
| | <div class="tooltip-content"> |
| | <span id="tooltip-close" class="tooltip-close">×</span> |
| | <div id="tooltip-text" class="tooltip-text"></div> |
| | <img id="tooltip-image" class="tooltip-image" src="" alt="" style="display: none;" /> |
| | </div> |
| | </div> |
| | `; |
| |
|
| | scriptTag.parentNode.appendChild(widgetContainer); |
| |
|
| | const viewerContainerElem = document.getElementById('viewer-container-' + instanceId); |
| | const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId); |
| | const helpToggle = document.getElementById('help-toggle-' + instanceId); |
| | const helpCloseBtn = document.getElementById('help-close-' + instanceId); |
| | const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId); |
| | const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId); |
| | const menuContent = document.getElementById('menu-content-' + instanceId); |
| | const helpTextDiv = menuContent.querySelector('.help-text'); |
| | const tooltipPanel = document.getElementById('tooltip-panel'); |
| | const tooltipTextDiv = document.getElementById('tooltip-text'); |
| | const tooltipImage = document.getElementById('tooltip-image'); |
| | const tooltipCloseBtn = document.getElementById('tooltip-close'); |
| |
|
| | const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; |
| | const isMobile = isIOS || /Android/i.test(navigator.userAgent); |
| |
|
| | const tooltipInstruction = config.tooltips_url |
| | ? '- Cliquez sur ⦿ pour afficher/masquer les tooltips.<br>' |
| | : ''; |
| |
|
| | if (isMobile) { |
| | helpTextDiv.innerHTML = |
| | '- Déplacez vous en glissant deux doigts sur l\'écran.<br>' + |
| | '- Orbitez en glissant un doigt.<br>' + |
| | '- Zoomez en pinçant avec deux doigts.<br>' + |
| | tooltipInstruction + |
| | '- Cliquez sur ⟲ pour réinitialiser la caméra.<br>' + |
| | '- Cliquez sur ⇱ pour passer en plein écran.<br>'; |
| | } else { |
| | helpTextDiv.innerHTML = |
| | '- Orbitez avec le clic droit<br>' + |
| | '- Zoomez avec la molette<br>' + |
| | '- Déplacez vous avec le clic gauche<br>' + |
| | tooltipInstruction + |
| | '- Cliquez sur ⟲ pour réinitialiser la caméra.<br>' + |
| | '- Cliquez sur ⇱ pour passer en plein écran.<br>'; |
| | } |
| |
|
| | |
| | function setMenuContentMaxSize() { |
| | if (!isMobile) { |
| | menuContent.style.maxWidth = ""; |
| | menuContent.style.maxHeight = ""; |
| | menuContent.style.width = ""; |
| | menuContent.style.height = ""; |
| | menuContent.style.overflowY = ""; |
| | menuContent.style.overflowX = ""; |
| | return; |
| | } |
| | let parent = viewerContainerElem; |
| | if (parent) { |
| | const vw = parent.offsetWidth; |
| | const vh = parent.offsetHeight; |
| | if (vw && vh) { |
| | menuContent.style.maxWidth = Math.round(vw * 0.8) + "px"; |
| | menuContent.style.maxHeight = Math.round(vh * 0.8) + "px"; |
| | menuContent.style.width = ""; |
| | menuContent.style.height = ""; |
| | menuContent.style.overflowY = "auto"; |
| | menuContent.style.overflowX = "auto"; |
| | } else { |
| | menuContent.style.maxWidth = "80vw"; |
| | menuContent.style.maxHeight = "80vh"; |
| | menuContent.style.overflowY = "auto"; |
| | menuContent.style.overflowX = "auto"; |
| | } |
| | } |
| | } |
| | setMenuContentMaxSize(); |
| | window.addEventListener('resize', setMenuContentMaxSize); |
| | document.addEventListener('fullscreenchange', setMenuContentMaxSize); |
| | window.addEventListener('orientationchange', setMenuContentMaxSize); |
| |
|
| | |
| | menuContent.style.display = 'block'; |
| |
|
| | viewerContainerElem.style.display = 'block'; |
| |
|
| | let dragHide = null; |
| |
|
| | function hideTooltipPanel() { |
| | if (dragHide) { |
| | viewerContainerElem.removeEventListener('pointermove', dragHide); |
| | dragHide = null; |
| | } |
| | tooltipPanel.style.display = 'none'; |
| | } |
| | function hideHelpPanel() { |
| | menuContent.style.display = 'none'; |
| | } |
| |
|
| | |
| | let viewerModule; |
| | try { |
| | viewerModule = await import('https://mikafil-viewer-gs.static.hf.space/viewer.js'); |
| | await viewerModule.initializeViewer(config, instanceId); |
| | } catch (err) { |
| | return; |
| | } |
| |
|
| | const canvasId = 'canvas-' + instanceId; |
| | const canvasEl = document.getElementById(canvasId); |
| |
|
| | if (tooltipsToggleBtn) { |
| | if (!config.tooltips_url) { |
| | tooltipsToggleBtn.style.display = 'none'; |
| | } else { |
| | fetch(config.tooltips_url) |
| | .then(resp => { if (!resp.ok) tooltipsToggleBtn.style.display = 'none'; }) |
| | .catch(() => { tooltipsToggleBtn.style.display = 'none'; }); |
| | } |
| | } |
| |
|
| | let isFullscreen = false; |
| | let savedState = null; |
| |
|
| | function saveCurrentState() { |
| | if (isFullscreen) return; |
| | const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent; |
| | savedState = { |
| | widget: { |
| | position: widgetContainer.style.position, |
| | top: widgetContainer.style.top, |
| | left: widgetContainer.style.left, |
| | width: widgetContainer.style.width, |
| | height: widgetContainer.style.height, |
| | maxWidth: widgetContainer.style.maxWidth, |
| | maxHeight:widgetContainer.style.maxHeight, |
| | paddingBottom: widgetContainer.style.paddingBottom || originalAspect, |
| | margin: widgetContainer.style.margin, |
| | }, |
| | viewer: { |
| | borderRadius: viewerContainerElem.style.borderRadius, |
| | border: viewerContainerElem.style.border, |
| | } |
| | }; |
| | } |
| |
|
| | function restoreOriginalStyles() { |
| | if (!savedState) return; |
| | const aspectToUse = savedState.widget.paddingBottom; |
| | widgetContainer.style.position = savedState.widget.position || ""; |
| | widgetContainer.style.top = savedState.widget.top || ""; |
| | widgetContainer.style.left = savedState.widget.left || ""; |
| | widgetContainer.style.width = "100%"; |
| | widgetContainer.style.height = "0"; |
| | widgetContainer.style.maxWidth = savedState.widget.maxWidth || ""; |
| | widgetContainer.style.maxHeight = savedState.widget.maxHeight || ""; |
| | widgetContainer.style.paddingBottom= aspectToUse; |
| | widgetContainer.style.margin = savedState.widget.margin || ""; |
| | widgetContainer.classList.remove('fake-fullscreen'); |
| |
|
| | viewerContainerElem.style.position = "absolute"; |
| | viewerContainerElem.style.top = "0"; |
| | viewerContainerElem.style.left = "0"; |
| | viewerContainerElem.style.right = "0"; |
| | viewerContainerElem.style.bottom = "0"; |
| | viewerContainerElem.style.width = "100%"; |
| | viewerContainerElem.style.height = "100%"; |
| | viewerContainerElem.style.borderRadius = savedState.viewer.borderRadius || ""; |
| | viewerContainerElem.style.border = savedState.viewer.border || ""; |
| |
|
| | if (viewerModule.app) { |
| | viewerModule.app.resizeCanvas( |
| | viewerContainerElem.clientWidth, |
| | viewerContainerElem.clientHeight |
| | ); |
| | } |
| |
|
| | if (fullscreenToggle) fullscreenToggle.textContent = '⇱'; |
| |
|
| | savedState = null; |
| | setMenuContentMaxSize(); |
| | } |
| |
|
| | function applyFullscreenStyles() { |
| | widgetContainer.style.position = 'fixed'; |
| | widgetContainer.style.top = '0'; |
| | widgetContainer.style.left = '0'; |
| | widgetContainer.style.width = '100vw'; |
| | widgetContainer.style.height = '100vh'; |
| | widgetContainer.style.maxWidth = '100vw'; |
| | widgetContainer.style.maxHeight = '100vh'; |
| | widgetContainer.style.paddingBottom = '0'; |
| | widgetContainer.style.margin = '0'; |
| | widgetContainer.style.border = 'none'; |
| | widgetContainer.style.borderRadius = '0'; |
| |
|
| | viewerContainerElem.style.width = '100%'; |
| | viewerContainerElem.style.height = '100%'; |
| | viewerContainerElem.style.borderRadius= '0'; |
| | viewerContainerElem.style.border = 'none'; |
| |
|
| | if (viewerModule.app) { |
| | viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight); |
| | } |
| |
|
| | if (fullscreenToggle) fullscreenToggle.textContent = '⇲'; |
| | isFullscreen = true; |
| | setMenuContentMaxSize(); |
| | } |
| |
|
| | function enterFullscreen() { |
| | if (!savedState) saveCurrentState(); |
| | if (isIOS) { |
| | applyFullscreenStyles(); |
| | widgetContainer.classList.add('fake-fullscreen'); |
| | } else if (widgetContainer.requestFullscreen) { |
| | widgetContainer.requestFullscreen() |
| | .then(applyFullscreenStyles) |
| | .catch(() => { |
| | applyFullscreenStyles(); |
| | widgetContainer.classList.add('fake-fullscreen'); |
| | }); |
| | } else { |
| | applyFullscreenStyles(); |
| | widgetContainer.classList.add('fake-fullscreen'); |
| | } |
| | } |
| |
|
| | function exitFullscreen() { |
| | if (document.fullscreenElement === widgetContainer && document.exitFullscreen) { |
| | document.exitFullscreen().catch(() => {}); |
| | } |
| | widgetContainer.classList.remove('fake-fullscreen'); |
| | restoreOriginalStyles(); |
| | isFullscreen = false; |
| | if (fullscreenToggle) fullscreenToggle.textContent = '⇱'; |
| | setMenuContentMaxSize(); |
| | } |
| |
|
| | fullscreenToggle.addEventListener('click', () => { |
| | hideTooltipPanel(); |
| | isFullscreen ? exitFullscreen() : enterFullscreen(); |
| | }); |
| |
|
| | document.addEventListener('fullscreenchange', () => { |
| | if (!document.fullscreenElement && isFullscreen) { |
| | isFullscreen = false; |
| | restoreOriginalStyles(); |
| | if (fullscreenToggle) fullscreenToggle.textContent = '⇱'; |
| | } else if (document.fullscreenElement === widgetContainer) { |
| | if (fullscreenToggle) fullscreenToggle.textContent = '⇲'; |
| | } |
| | setMenuContentMaxSize(); |
| | }); |
| |
|
| | helpToggle.addEventListener('click', (e) => { |
| | hideTooltipPanel(); |
| | e.stopPropagation(); |
| | |
| | if (menuContent.style.display === 'block') { |
| | menuContent.style.display = 'none'; |
| | } else { |
| | menuContent.style.display = 'block'; |
| | setMenuContentMaxSize(); |
| | } |
| | }); |
| | helpCloseBtn.addEventListener('click', hideHelpPanel); |
| |
|
| | resetCameraBtn.addEventListener('click', () => { |
| | hideTooltipPanel(); |
| | if (viewerModule.resetViewerCamera) { |
| | viewerModule.resetViewerCamera(); |
| | } |
| | }); |
| |
|
| | if (tooltipsToggleBtn) { |
| | let tooltipsVisible = !!config.showTooltipsDefault; |
| | tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5'; |
| | tooltipsToggleBtn.addEventListener('click', () => { |
| | hideTooltipPanel(); |
| | tooltipsVisible = !tooltipsVisible; |
| | tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5'; |
| | document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } })); |
| | }); |
| | } |
| |
|
| | tooltipCloseBtn.addEventListener('click', hideTooltipPanel); |
| |
|
| | document.addEventListener('tooltip-selected', (evt) => { |
| | |
| | if (dragHide) { |
| | viewerContainerElem.removeEventListener('pointermove', dragHide); |
| | dragHide = null; |
| | } |
| | const { title, description, imgUrl } = evt.detail; |
| | tooltipTextDiv.innerHTML = `<strong>${title}</strong><br>${description}`; |
| | |
| | tooltipImage.style.display = 'none'; |
| | tooltipImage.src = ''; |
| | if (imgUrl) { |
| | tooltipImage.onload = () => { |
| | tooltipImage.style.display = 'block'; |
| | }; |
| | tooltipImage.src = imgUrl; |
| | } else { |
| | tooltipImage.style.display = 'none'; |
| | } |
| | tooltipPanel.style.display = 'flex'; |
| | |
| | setTimeout(() => { |
| | dragHide = (e) => { |
| | if ((e.pointerType === 'mouse' && e.buttons !== 0) || e.pointerType === 'touch') { |
| | hideTooltipPanel(); |
| | } |
| | }; |
| | viewerContainerElem.addEventListener('pointermove', dragHide); |
| | }, 100); |
| | }); |
| |
|
| | if (canvasEl) { |
| | canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true }); |
| | } |
| | document.addEventListener('keydown', (e) => { |
| | if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen(); |
| | }); |
| | window.addEventListener('resize', () => { |
| | if (viewerModule.app) { |
| | if (isFullscreen) { |
| | viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight); |
| | } else { |
| | viewerModule.app.resizeCanvas( |
| | viewerContainerElem.clientWidth, |
| | viewerContainerElem.clientHeight |
| | ); |
| | } |
| | } |
| | setMenuContentMaxSize(); |
| | }); |
| |
|
| | setTimeout(() => { |
| | saveCurrentState(); |
| | document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } })); |
| | setMenuContentMaxSize(); |
| | }, 200); |
| |
|
| | })(); |
| |
|