1/* ARPIA v2 media lightbox â videos and screenshots open full-screen. 2 El contenido se dimensiona solo: crece hasta topar con el viewport, y el marco 3 redondeado va POR FUERA, para no recortar las esquinas de la grabacion. */ 4(function () { 5 var m = document.createElement('div'); 6 m.className = 'vmodal'; 7 m.setAttribute('role', 'dialog'); 8 m.setAttribute('aria-modal', 'true'); 9 m.innerHTML = '<button class="vmodal-close" aria-label="Close">×</button><div class="vmodal-inner"></div>'; 10 document.body.appendChild(m); 11 var inner = m.querySelector('.vmodal-inner'); 12 var lastFocus = null; 13 var nodeHome = null, nodeEl = null; // panel prestado a este modal 14 15 function returnNode() { 16 if (nodeEl && nodeHome) { nodeEl.hidden = true; nodeHome.appendChild(nodeEl); } 17 nodeEl = null; nodeHome = null; 18 } 19 20 function close() { 21 m.classList.remove('open'); 22 returnNode(); 23 inner.innerHTML = ''; 24 inner.className = 'vmodal-inner'; 25 document.body.style.overflow = ''; 26 if (lastFocus && lastFocus.focus) lastFocus.focus(); 27 lastFocus = null; 28 } 29 30 function openVideo(src, poster, label) { 31 if (src) { 32 inner.className = 'vmodal-inner is-frame'; 33 inner.innerHTML = '<video controls autoplay playsinline' + (poster ? ' poster="' + poster + '"' : '') + 34 '><source src="' + src + '" type="video/mp4"></video>'; 35 } else { 36 inner.className = 'vmodal-inner is-soon'; 37 inner.innerHTML = '<div class="vmodal-soon"><ion-icon name="play-circle-outline"></ion-icon>' + 38 '<p>' + (label || 'Demo') + '</p><span>Coming soon</span></div>'; 39 } 40 show(); 41 } 42 43 function openImage(src, alt) { 44 inner.className = 'vmodal-inner is-frame'; 45 inner.innerHTML = '<img class="vmodal-img" src="' + src + '" alt="' + (alt || '').replace(/"/g, '"') + '">'; 46 show(); 47 } 48 49 function openNode(sel) { 50 var el = document.querySelector(sel); 51 if (!el) return false; 52 if (el === nodeEl) return true; // ya esta abierto 53 returnNode(); // otro panel abierto: devuelvelo antes de vaciar 54 // se mueve al modal y se devuelve a su sitio al cerrar, para no duplicar ids 55 nodeEl = el; nodeHome = el.parentNode; 56 inner.className = 'vmodal-inner is-panel'; 57 inner.innerHTML = ''; 58 el.hidden = false; 59 inner.appendChild(el); 60 inner.scrollTop = 0; // reabrir empieza por el principio 61 show(); 62 var first = el.querySelector('input, select, textarea'); 63 if (first) setTimeout(function () { first.focus(); }, 60); 64 return true; 65 } 66 67 function show() { 68 lastFocus = document.activeElement; 69 m.classList.add('open'); 70 document.body.style.overflow = 'hidden'; 71 m.querySelector('.vmodal-close').focus(); 72 } 73 74 m.querySelector('.vmodal-close').addEventListener('click', close); 75 m.addEventListener('click', function (e) { 76 // fuera del contenido cierra; sobre el video no, para no pelear con sus controles. 77 // con un panel abierto no se cierra al descuido: se perderia lo escrito. 78 if (inner.classList.contains('is-panel')) return; 79 if (e.target === m || e.target === inner) close(); 80 }); 81 document.addEventListener('keydown', function (e) { 82 if (e.key === 'Escape' && m.classList.contains('open')) close(); 83 }); 84 85 function trigger(t) { 86 if (t.matches('.media-shot, img[data-zoom]')) { 87 openImage(t.getAttribute('src'), t.getAttribute('alt')); 88 return true; 89 } 90 var lbl = t.querySelector && t.querySelector('.media-ph-label'); 91 openVideo(t.getAttribute('data-video'), t.getAttribute('data-poster'), lbl ? lbl.textContent : null); 92 return true; 93 } 94 95 document.addEventListener('click', function (e) { 96 var panel = e.target.closest('[data-modal]'); 97 if (panel) { 98 // si el modal no abre, el href del enlace sigue funcionando 99 if (openNode(panel.getAttribute('data-modal'))) e.preventDefault(); 100 return; 101 } 102 var t = e.target.closest('[data-video], .media-ph-video, .media-shot, img[data-zoom]'); 103 if (!t) return; 104 e.preventDefault(); 105 trigger(t); 106 }); 107 108 // teclado: los bloques son operables sin raton 109 document.addEventListener('keydown', function (e) { 110 if (e.key !== 'Enter' && e.key !== ' ') return;
111 var t = e.target.closest && e.target.closest('[data-video], .media-ph-video, .media-shot, img[data-zoom]'); 112 if (!t) return; 113 e.preventDefault(); 114 trigger(t); 115 }); 116 117 // las capturas se marcan operables aqui, para no repetir atributos en cada pagina 118 function markShots() { 119 var shots = document.querySelectorAll('.media-shot:not([data-zoomable])'); 120 for (var i = 0; i < shots.length; i++) { 121 shots[i].setAttribute('data-zoomable', '1'); 122 shots[i].setAttribute('role', 'button'); 123 shots[i].setAttribute('tabindex', '0'); 124 if (!shots[i].getAttribute('title')) shots[i].setAttribute('title', 'Open full size'); 125 } 126 } 127 if (document.readyState === 'loading') { 128 document.addEventListener('DOMContentLoaded', markShots); 129 } else { 130 markShots(); 131 } 132 // las story pages tambien se inyectan como overlay desde el dial: sus capturas 133 // aparecen despues de la carga, asi que se marcan cuando entran al DOM 134 if (window.MutationObserver) { 135 new MutationObserver(function (muts) { 136 for (var i = 0; i < muts.length; i++) { 137 if (muts[i].addedNodes && muts[i].addedNodes.length) { markShots(); return; } 138 } 139 }).observe(document.body, { childList: true, subtree: true }); 140 } 141})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.