Spaces:
Running
Running
| // ============================== | |
| // interface.js | |
| // ============================== | |
| // Store a reference to the <script> tag that loaded this file | |
| const currentScriptTag = document.currentScript; | |
| (async function() { | |
| // βββ 1. Locate the <script> and read data-config βββββββββββββββββββββββββββββββ | |
| 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) { | |
| console.error("Error loading config file:", error); | |
| return; | |
| } | |
| } else { | |
| console.error("No config file provided. Please set a data-config attribute on the <script> tag."); | |
| return; | |
| } | |
| // βββ 2. If config.css_url is provided, inject a <link> to that CSS βββββββββββββ | |
| if (config.css_url) { | |
| const linkEl = document.createElement('link'); | |
| linkEl.rel = "stylesheet"; | |
| linkEl.href = config.css_url; | |
| document.head.appendChild(linkEl); | |
| } | |
| // βββ 3. Generate a unique instanceId for this widget βββββββββββββββββββββββββββ | |
| const instanceId = Math.random().toString(36).substr(2, 8); | |
| // βββ 4. Compute the aspect ratio (padding-bottom %) ββββββββββββββββββββββββββββ | |
| 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) + "%"; | |
| } | |
| } | |
| // βββ 5. Create the widget container ββββββββββββββββββββββββββββββββββββββββββββ | |
| 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); | |
| // Conditionally add the tooltips toggle button only if defined | |
| const tooltipsButtonHTML = config.tooltips_url | |
| ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">β¦Ώ</button>` | |
| : ''; | |
| // Add all inner HTML, including help panel with French lines | |
| 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">β²</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> | |
| `; | |
| // Append to DOM | |
| scriptTag.parentNode.appendChild(widgetContainer); | |
| // βββ 6. Grab references βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| 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'); | |
| // Tooltip panel refs | |
| const tooltipPanel = document.getElementById('tooltip-panel'); | |
| const tooltipTextDiv = document.getElementById('tooltip-text'); | |
| const tooltipImage = document.getElementById('tooltip-image'); | |
| const tooltipCloseBtn = document.getElementById('tooltip-close'); | |
| // βββ 6a. Mobile detection βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; | |
| const isMobile = isIOS || /Android/i.test(navigator.userAgent); | |
| // Conditionally include the French tooltip instruction | |
| const tooltipInstruction = config.tooltips_url | |
| ? '- Cliquez sur β¦Ώ pour afficher/masquer les tooltips.<br>' | |
| : ''; | |
| // βββ 6b. Populate help-text with French instructions ββββββββββββββββββββββββββ | |
| if (isMobile) { | |
| helpTextDiv.innerHTML = ` | |
| - Pour vous dΓ©placer, glissez deux doigts sur l'Γ©cran.<br> | |
| - Pour orbiter, utilisez un doigt.<br> | |
| - Pour zoomer, pincez avec deux doigts.<br> | |
| β² RΓ©initialise la camΓ©ra.<br> | |
| β± Passe en plein Γ©cran.<br> | |
| ${tooltipInstruction} | |
| `; | |
| } else { | |
| helpTextDiv.innerHTML = ` | |
| - orbitez avec le clic droit<br> | |
| - zoomez avec la molette<br> | |
| - dΓ©placez vous avec le clic gauche<br> | |
| β² RΓ©initialise la camΓ©ra.<br> | |
| β± Passe en plein Γ©cran.<br> | |
| ${tooltipInstruction} | |
| `; | |
| } | |
| // Show help panel by default | |
| menuContent.style.display = 'block'; | |
| viewerContainerElem.style.display = 'block'; | |
| // βββ 7. Dynamically load viewer.js βββββββββββββββββββββββββββββββββββββββββββ | |
| try { | |
| console.log('[Viewer-Debug] About to import viewer.js'); | |
| const viewerModule = await import('./viewer.js'); | |
| console.log('[Viewer-Debug] viewer.js imported, calling initializeViewer()'); | |
| await viewerModule.initializeViewer(config, instanceId); | |
| console.log('[Viewer-Debug] initializeViewer() completed'); | |
| } catch (err) { | |
| console.error("Failed to load viewer.js or initialize the 3D viewer:", err); | |
| return; | |
| } | |
| // βββ 8. Rest of the event listeners (fullscreen, help toggle, tooltips, etc.) β | |
| // (This section remains exactly as before, wiring up all button behaviour, | |
| // drag-to-hide, wheel-to-hide, resize listeners, etc., without alteration.) | |
| })(); | |