Spaces:
Running
Running
| // fullscreen.js | |
| (function () { | |
| // βββ 1. Localiser la balise <script> ββββββββββββββββββββββββββββββββββββββββ | |
| const scriptTag = document.currentScript || (function () { | |
| const all = document.getElementsByTagName('script'); | |
| for (let i = all.length - 1; i >= 0; i--) { | |
| if (all[i].src && all[i].src.includes('fullscreen.js')) return all[i]; | |
| } | |
| return all[all.length - 1]; | |
| })(); | |
| const playcanvasUrl = scriptTag.getAttribute('data-src'); | |
| if (!playcanvasUrl) return; | |
| const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; | |
| const id = Math.random().toString(36).substr(2, 8); | |
| // βββ 4. Calcul des ratios (Desktop & Mobile Portrait) ββββββββββββββββββββββββ | |
| function computeAspectPadding(aspectStr) { | |
| if (!aspectStr) return null; | |
| if (aspectStr.includes(':')) { | |
| const [w, h] = aspectStr.split(':').map(Number); | |
| return (w > 0 && h > 0) ? (h / w) * 100 + '%' : null; | |
| } | |
| const v = parseFloat(aspectStr); | |
| return (v > 0) ? (100 / v) + '%' : null; | |
| } | |
| const desktopPadding = computeAspectPadding(scriptTag.getAttribute('data-aspect')) || '56.25%'; | |
| const mobilePadding = computeAspectPadding(scriptTag.getAttribute('data-aspect-mobile')) || desktopPadding; | |
| // βββ 5. Injection du CSS avec Media Queries ββββββββββββββββββββββββββββββββββ | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| .pc-embed-wrapper-${id} { | |
| position: relative; | |
| width: 100%; | |
| height: 0; | |
| overflow: hidden; | |
| background: #000; | |
| box-sizing: border-box; | |
| /* Ratio par dΓ©faut (Paysage / Desktop) */ | |
| padding-bottom: ${desktopPadding}; | |
| } | |
| /* Ratio Mobile Portrait : appliquΓ© uniquement si l'Γ©cran est plus haut que large */ | |
| @media (orientation: portrait) and (max-width: 768px) { | |
| .pc-embed-wrapper-${id} { | |
| padding-bottom: ${mobilePadding}; | |
| } | |
| } | |
| .pc-embed-wrapper-${id}.fake-fullscreen { | |
| position: fixed !important; | |
| top: 0 !important; | |
| left: 0 !important; | |
| width: 100vw !important; | |
| height: 100vh !important; | |
| height: 100dvh !important; | |
| max-width: 100vw !important; | |
| max-height: 100dvh !important; | |
| padding-bottom: 0 !important; | |
| margin: 0 !important; | |
| z-index: 99999; | |
| } | |
| .pc-embed-inner-${id} { | |
| position: absolute; | |
| top: 0; left: 0; | |
| width: 100%; height: 100%; | |
| } | |
| .pc-embed-inner-${id} iframe { | |
| width: 100%; height: 100%; | |
| border: none; | |
| display: block; | |
| } | |
| .pc-fs-btn-${id} { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| z-index: 10; | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 50%; | |
| border: none; | |
| background: #2E2E2EB3; | |
| color: #fff; | |
| font-size: 18px; | |
| line-height: 36px; | |
| text-align: center; | |
| cursor: pointer; | |
| user-select: none; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // βββ 6. Construction du DOM ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const wrapper = document.createElement('div'); | |
| wrapper.className = `pc-embed-wrapper-${id}`; | |
| const inner = document.createElement('div'); | |
| inner.className = `pc-embed-inner-${id}`; | |
| const iframe = document.createElement('iframe'); | |
| const urlObj = new URL(playcanvasUrl); | |
| urlObj.searchParams.set('overlay', 'false'); | |
| iframe.src = urlObj.href; | |
| iframe.setAttribute('allowfullscreen', ''); | |
| iframe.setAttribute('allow', 'autoplay; fullscreen'); | |
| iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-pointer-lock allow-popups allow-forms'); | |
| const fsBtn = document.createElement('button'); | |
| fsBtn.className = `pc-fs-btn-${id}`; | |
| fsBtn.textContent = 'β±'; | |
| inner.appendChild(iframe); | |
| wrapper.appendChild(inner); | |
| wrapper.appendChild(fsBtn); | |
| scriptTag.parentNode.insertBefore(wrapper, scriptTag.nextSibling); | |
| // βββ 7. Γtat & Helper ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| let isFullscreen = false; | |
| let savedParent = null; | |
| let savedNextSibling = null; | |
| function getHeightUnit() { | |
| return (CSS && CSS.supports && CSS.supports('height', '100dvh')) ? '100dvh' : '100vh'; | |
| } | |
| // βββ 8. Styles Plein Γcran βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function applyFullscreenStyles() { | |
| const h = isIOS ? getHeightUnit() : '100vh'; | |
| wrapper.style.position = 'fixed'; | |
| wrapper.style.top = '0'; | |
| wrapper.style.left = '0'; | |
| wrapper.style.width = '100vw'; | |
| wrapper.style.height = h; | |
| wrapper.style.maxHeight = h; | |
| wrapper.style.paddingBottom = '0'; | |
| wrapper.style.margin = '0'; | |
| wrapper.style.zIndex = '99999'; | |
| wrapper.classList.add('fake-fullscreen'); | |
| fsBtn.textContent = 'β²'; | |
| isFullscreen = true; | |
| } | |
| function applyFakeFullscreenStyles() { | |
| savedParent = wrapper.parentNode; | |
| savedNextSibling = wrapper.nextSibling; | |
| document.body.appendChild(wrapper); | |
| applyFullscreenStyles(); | |
| } | |
| function restoreStyles() { | |
| wrapper.style.cssText = ''; | |
| // Le CSS via les Media Queries reprendra le dessus automatiquement ici | |
| wrapper.classList.remove('fake-fullscreen'); | |
| fsBtn.textContent = 'β±'; | |
| isFullscreen = false; | |
| if (savedParent) { | |
| savedParent.insertBefore(wrapper, savedNextSibling); | |
| savedParent = null; | |
| savedNextSibling = null; | |
| } | |
| } | |
| // βββ 9. Gestionnaires d'Γ©vΓ©nements βββββββββββββββββββββββββββββββββββββββββββ | |
| function enterFullscreen() { | |
| if (isIOS) { | |
| applyFakeFullscreenStyles(); | |
| document.body.style.overflow = 'hidden'; | |
| } else { | |
| const el = wrapper; | |
| const req = el.requestFullscreen || el.webkitRequestFullscreen || el.mozRequestFullScreen || el.msRequestFullscreen; | |
| if (req) { | |
| req.call(el).catch(() => { | |
| applyFakeFullscreenStyles(); | |
| document.body.style.overflow = 'hidden'; | |
| }); | |
| } else { | |
| applyFakeFullscreenStyles(); | |
| document.body.style.overflow = 'hidden'; | |
| } | |
| } | |
| } | |
| function exitFullscreen() { | |
| if (document.fullscreenElement || document.webkitFullscreenElement) { | |
| (document.exitFullscreen || document.webkitExitFullscreen || function(){}).call(document); | |
| } | |
| restoreStyles(); | |
| document.body.style.overflow = ''; | |
| } | |
| fsBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| isFullscreen ? exitFullscreen() : enterFullscreen(); | |
| }); | |
| document.addEventListener('fullscreenchange', () => { | |
| const fsEl = document.fullscreenElement || document.webkitFullscreenElement; | |
| if (!fsEl && isFullscreen) exitFullscreen(); | |
| else if (fsEl === wrapper && !isFullscreen) applyFullscreenStyles(); | |
| }); | |
| window.addEventListener('resize', () => { | |
| if (isFullscreen) { | |
| const h = isIOS ? getHeightUnit() : '100vh'; | |
| wrapper.style.height = h; | |
| wrapper.style.maxHeight = h; | |
| } | |
| }); | |
| })(); |