1/** 2 * URANIA Iframe Gallery â postMessage listener 3 * 4 * URANIA (the hotelview / booking engine) is embedded as an iframe inside 5 * QUADRA pages via the [smg_travelbox] shortcode. When a user taps a hotel 6 * image gallery inside the iframe, URANIA sends a postMessage to the parent 7 * window with the event name "uraniaOpenGallery" and an array of image URLs. 8 * 9 * This script runs on the parent (WordPress / QUADRA) side: 10 * 1. Listens for the "uraniaOpenGallery" postMessage from the iframe. 11 * 2. Opens a full-screen lightbox gallery on the parent page. 12 * 3. Supports prev / next navigation, keyboard (Esc, arrows) and touch swipe. 13 * 14 * The corresponding sender lives in URANIA's Angular component: 15 * HotelviewTabContentMediaComponent.openLightbox() 16 * â window.parent.postMessage(JSON.stringify({ 17 * event: 'uraniaOpenGallery', 18 * payload: [imageUrl, ...], 19 * activeIndex: <number> 20 * }), '*'); 21 */ 22(function () { 23 'use strict'; 24 25 var CLS = 'smg-urania-gallery'; 26 27 var gallery = { 28 images: [], 29 currentIndex: 0, 30 overlay: null, 31 imageEl: null, 32 counterEl: null, 33 prevBtn: null, 34 nextBtn: null, 35 spinnerEl: null, 36 touchStartX: 0, 37 touchEndX: 0, 38 preloadCache: {} 39 }; 40 41 function preloadImage(url) { 42 if (gallery.preloadCache[url]) return; 43 var img = new Image(); 44 img.src = url; 45 gallery.preloadCache[url] = img; 46 } 47 48 function preloadAdjacent(index) { 49 var len = gallery.images.length; 50 if (len <= 1) return; 51 preloadImage(gallery.images[(index + 1) % len]); 52 preloadImage(gallery.images[(index - 1 + len) % len]); 53 } 54 55 function createGallery() { 56 if (gallery.overlay) return; 57 58 var overlay = document.createElement('div'); 59 overlay.className = CLS; 60 overlay.setAttribute('role', 'dialog'); 61 overlay.setAttribute('aria-modal', 'true'); 62 overlay.setAttribute('aria-label', 'Bildergalerie'); 63 overlay.innerHTML = 64 '<div class="' + CLS + '__backdrop"></div>' + 65 '<button class="' + CLS + '__close" type="button" aria-label="SchlieÃen">×</button>' + 66 '<button class="' + CLS + '__prev" type="button" aria-label="Vorheriges Bild">❮</button>' + 67 '<button class="' + CLS + '__next" type="button" aria-label="Nächstes Bild">❯</button>' + 68 '<div class="' + CLS + '__content">' + 69 '<div class="' + CLS + '__spinner"></div>' + 70 '<img class="' + CLS + '__image" src="" alt="Galeriebild" />' + 71 '</div>' + 72 '<div class="' + CLS + '__counter"></div>'; 73 74 document.body.appendChild(overlay); 75 76 gallery.overlay = overlay; 77 gallery.imageEl = overlay.querySelector('.' + CLS + '__image'); 78 gallery.counterEl = overlay.querySelector('.' + CLS + '__counter'); 79 gallery.prevBtn = overlay.querySelector('.' + CLS + '__prev'); 80 gallery.nextBtn = overlay.querySelector('.' + CLS + '__next'); 81 gallery.spinnerEl = overlay.querySelector('.' + CLS + '__spinner'); 82 83 gallery.imageEl.addEventListener('load', function () { 84 gallery.imageEl.classList.add('is-loaded'); 85 gallery.spinnerEl.style.display = 'none'; 86 }); 87 88 overlay.querySelector('.' + CLS + '__backdrop').addEventListener('click', close); 89 overlay.querySelector('.' + CLS + '__close').addEventListener('click', close); 90 gallery.prevBtn.addEventListener('click', prev); 91 gallery.nextBtn.addEventListener('click', next); 92 93 var content = overlay.querySelector('.' + CLS + '__content'); 94 content.addEventListener('touchstart', function (e) { 95 gallery.touchStartX = e.changedTouches[0].screenX; 96 }, { passive: true }); 97 content.addEventListener('touchend', function (e) { 98 gallery.touchEndX = e.changedTouches[0].screenX; 99 var diff = gallery.touchStartX - gallery.touchEndX; 100 if (Math.abs(diff) > 50) { 101 if (diff > 0) { next(); } else { prev(); } 102 } 103 }, { passive: true }); 104 } 105 106 function showImage(index) { 107 if (index < 0 || index >= gallery.images.length) return; 108 gallery.currentIndex = index; 109 110 gallery.imageEl.classList.remove('is-loaded'); 111 gallery.spinnerEl.style.display = ''; 112 113 gallery.imageEl.src = gallery.images[index]; 114 gallery.imageEl.alt = 'Bild ' + (index + 1) + ' von ' + gallery.images.length; 115 gallery.counterEl.textContent = (index + 1) + ' / ' + gallery.images.length; 116 gallery.prevBtn.style.display = gallery.images.length > 1 ? '' : 'none'; 117 gallery.nextBtn.style.display = gallery.images.length > 1 ? '' : 'none'; 118 119 if (gallery.preloadCache[gallery.images[index]] && gallery.preloadCache[gallery.images[index]].complete) { 120 gallery.imageEl.classList.add('is-loaded'); 121 gallery.spinnerEl.style.display = 'none'; 122 } 123 124 preloadAdjacent(index); 125 } 126 127 function open(images, activeIndex) { 128 createGallery(); 129 gallery.images = images; 130 showImage(activeIndex || 0); 131 gallery.overlay.classList.add('is-open'); 132 document.body.style.overflow = 'hidden'; 133 gallery.overlay.querySelector('.' + CLS + '__close').focus(); 134 } 135 136 function close() { 137 if (!gallery.overlay) return; 138 gallery.overlay.classList.remove('is-open'); 139 document.body.style.overflow = ''; 140 } 141 142 function prev() { 143 var idx = gallery.currentIndex > 0 ? gallery.currentIndex - 1 : gallery.images.length - 1; 144 showImage(idx); 145 } 146 147 function next() { 148 var idx = gallery.currentIndex < gallery.images.length - 1 ? gallery.currentIndex + 1 : 0; 149 showImage(idx); 150 } 151 152 document.addEventListener('keydown', function (e) { 153 if (!gallery.overlay || !gallery.overlay.classList.contains('is-open')) return; 154 if (e.key === 'Escape') { close(); } 155 else if (e.key === 'ArrowLeft') { prev(); } 156 else if (e.key === 'ArrowRight') { next(); } 157 }); 158 159 /** 160 * Listen for postMessage from the URANIA iframe. 161 * 162 * Expected message format (JSON-stringified): 163 * { event: "uraniaOpenGallery", payload: string[], activeIndex: number } 164 * 165 * `payload` contains image URLs from URANIA's HotelviewTabContentMediaComponent 166 * (the hotel image gallery). Each URL points to the 800px variant of the image. 167 */ 168 window.addEventListener('message', function (e) { 169 var data; 170 try { 171 data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data; 172 } catch (_) { 173 return; 174 } 175 176 if (data && data.event === 'uraniaOpenGallery' && Array.isArray(data.payload) && data.payload.length > 0) { 177 open(data.payload, data.activeIndex || 0); 178 } 179 }); 180})();
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.