PageSourceSearch

https://www.arpia.ai/assets/js/vmodal.js?v=4

js arpia.ai collected 2026-10-05 11:48:31 UTC 5,522 bytes, 141 lines download raw bytes

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">&times;</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, '&quot;') + '">';
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.