1let thumbSlider, originSlider; 2let lastProductId = null; 3let lastGalleryType = null; 4let lastReviewProductId = null; 5let lastReviewId = null; 6 7/** íì¤í¬ë¦° ì¬ë¼ì´ë: ì¤ì URL ì 1Ã1 í¬ëª ì리íì (ëë ì´ë¯¸ì§ ëì ë¡ëë¡ ëª¨ë°ì¼ í í¬ëì ë°©ì§) */ 8const FULL_SLIDER_LAZY_PLACEHOLDER = 9 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg"/>'); 10 11/** 12 * OTA ì´ë¯¸ì§ URL ì ì©ëë³ ì¬ì´ì¦ë¡ ë³í (ìºììë ì¬ì´ì¦ 미ì§ì base URL ì ì¥ë¨ â hotelProduct.php ì°¸ê³ ) 13 * - Expedia(travelapi.com): _t(70px)/_b(350px)/_z(1000px) suffix 14 * - Agoda(agoda.net): ?s=<width>x ê°ë³ 리ì¬ì´ì¦, 미ì§ì ì ìë³¸ì´ ë´ë ¤ì¤ë¯ë¡ ë°ëì ì§ì 15 * - ê·¸ ì¸(ìì²´ ì ë¡ë ë±)ë ì본 ì ì§ 16 */ 17window.resizeGalleryImageUrl = window.resizeGalleryImageUrl || function (url, size) { 18 if (!url || typeof url !== 'string') return url; 19 if (url.indexOf('travelapi.com') !== -1) { 20 const suffix = size === 'large' ? '_z.jpg' : '_b.jpg'; 21 return url.replace(/_[tbz]\.jpg(\?.*)?$/, suffix + '$1'); 22 } 23 if (url.indexOf('agoda.net') !== -1) { 24 const width = size === 'small' ? 312 : size === 'medium' ? 512 : 1000; 25 if (/[?&]s=\d+x/.test(url)) { 26 return url.replace(/([?&])s=\d+x/, '$1s=' + width + 'x'); 27 } 28 return url + (url.indexOf('?') !== -1 ? '&' : '?') + 's=' + width + 'x'; 29 } 30 return url; 31}; 32 33let fullSliderThumbLazyIo = null; 34 35function disconnectFullSliderThumbLazyObserver() { 36 if (fullSliderThumbLazyIo) { 37 fullSliderThumbLazyIo.disconnect(); 38 fullSliderThumbLazyIo = null; 39 } 40} 41 42/** íì¬ ì¬ë¼ì´ëìì ì´ë§í¼ ë²ì´ë ì본 ì´ë¯¸ì§ë placeholder ë¡ ëëë ¤ ë©ëª¨ë¦¬ ì주를 ì í (iOS ë©ëª¨ë¦¬ íì â í° íë©´ ìë°©) */ 43const ORIGIN_KEEP_RANGE = 6; 44const FULL_SLIDER_MAX_RETRY = 3; 45 46function loadFullSliderImage(img) { 47 const url = img?.dataset?.src; 48 if (!url) return; 49 img.decoding = 'async'; 50 img.onload = () => { 51 img.onload = null; 52 img.onerror = null; 53 img.removeAttribute('data-retry'); 54 }; 55 img.onerror = () => { 56 // ë¡ë ì¤ë¨(íë©´ ì´íë¡ ìì² ì·¨ì ë±)/ì¤í¨ ì placeholder ë¡ ëëë ¤ ë¤ì ì§ì ë ì¬ìë 57 img.onload = null; 58 img.onerror = null; 59 const retry = (parseInt(img.dataset.retry, 10) || 0) + 1; 60 if (retry > FULL_SLIDER_MAX_RETRY) return; 61 img.dataset.retry = String(retry); 62 img.src = FULL_SLIDER_LAZY_PLACEHOLDER; 63 img.dataset.src = url; 64 if (fullSliderThumbLazyIo && img.isConnected && img.closest('.thumb-slider')) { 65 fullSliderThumbLazyIo.observe(img); 66 } 67 }; 68 img.src = url; 69 img.removeAttribute('data-src'); 70} 71 72/** ë¡ëë ì본 ì´ë¯¸ì§ë¥¼ placeholder ë¡ ëëë ¤ ëì½ë© ë©ëª¨ë¦¬ë¥¼ í´ì */ 73function unloadFullSliderImage(img) { 74 if (!img || img.dataset.src) return; 75 const url = img.currentSrc || img.src; 76 if (!url || url.indexOf('data:') === 0) return; 77 img.onload = null; 78 img.onerror = null; 79 img.dataset.src = url; 80 img.src = FULL_SLIDER_LAZY_PLACEHOLDER; 81} 82 83/** origin-slider: íì¬ ì¬ë¼ì´ë ± range ë§ ì¤ì ë¡ë, ± ORIGIN_KEEP_RANGE ë°ì ì¸ë¡ë */ 84function loadOriginNeighborImages(swiper, range = 2) { 85 if (!swiper || !swiper.slides || swiper.destroyed) return; 86 const idx = swiper.activeIndex; 87 const from = Math.max(0, idx - range); 88 const to = Math.min(swiper.slides.length - 1, idx + range); 89 for (let i = 0; i < swiper.slides.length; i++) { 90 const img = swiper.slides[i]?.querySelector('img'); 91 if (!img) continue; 92 if (i >= from && i <= to) { 93 if (img.dataset.src) loadFullSliderImage(img); 94 } else if (i < idx - ORIGIN_KEEP_RANGE || i > idx + ORIGIN_KEEP_RANGE) { 95 unloadFullSliderImage(img); 96 } 97 } 98} 99 100/** 101 * íì¬ ì¬ë¼ì´ë 주ë³ì ë¡ëë ì본 ì´ë¯¸ì§ë¥¼ ìºììì ë¤ì ëì½ë 102 * (iOS ê° ë°±ê·¸ë¼ì´ë ì í ì¤ ëì½ë© ë©ëª¨ë¦¬ë¥¼ íìíë©´ naturalWidth ë ì ìì¸ë° í° íë©´ì¼ë¡ ë³´ì´ë ê²½ì° ë³µêµ¬) 103 */ 104function refreshFullSliderActiveImages() { 105 const sliderEl = document.getElementById('fullscreenSlider'); 106 if (!sliderEl || sliderEl.style.visibility !== 'visible') return; 107 if (!originSlider || originSlider.destroyed || !originSlider.slides) return; 108 const idx = originSlider.activeIndex;
109 const from = Math.max(0, idx - 1); 110 const to = Math.min(originSlider.slides.length - 1, idx + 1); 111 for (let i = from; i <= to; i++) { 112 const img = originSlider.slides[i]?.querySelector('img'); 113 if (!img || img.dataset.src) continue; 114 const url = img.currentSrc || img.src; 115 if (!url || url.indexOf('data:') === 0) continue; 116 img.removeAttribute('src'); 117 img.dataset.src = url; 118 img.removeAttribute('data-retry'); 119 loadFullSliderImage(img); 120 } 121} 122 123/** ì¬ë¼ì´ë를 ë«ê³ 그리ë 모ë¬ë¡ ëìê° ë, ì¬ë¼ì´ë ì¬ì© ì¤ íìëì ì ìë 그리ë ì´ë¯¸ì§ 복구 */ 124function refreshUnderlyingGalleryImages() { 125 if (typeof refreshModalGalleryVisibleImages === 'function') { 126 refreshModalGalleryVisibleImages(); 127 } 128} 129 130/** ë©ëª¨ë¦¬ íì/ë¡ë ì¤ë¨ì¼ë¡ 깨ì§(í° íë©´) ì´ë¯¸ì§ë¥¼ ì°¾ì ì¬ë¡ë */ 131function recoverFullSliderImages() { 132 const sliderEl = document.getElementById('fullscreenSlider'); 133 if (!sliderEl) return; 134 sliderEl.querySelectorAll('img').forEach((img) => { 135 if (img.dataset.src) return; 136 const url = img.currentSrc || img.src; 137 if (!url || url.indexOf('data:') === 0) return; 138 if (img.complete && img.naturalWidth === 0) { 139 img.dataset.src = url; 140 img.removeAttribute('data-retry'); 141 loadFullSliderImage(img); 142 } 143 }); 144} 145 146/** thumb-slider: ê°ë¡ ì¤í¬ë¡¤ ììì ë¤ì´ì¨ ì¸ë¤ì¼ë§ ì¤ì ë¡ë */ 147function initLazyFullSliderThumbImages() { 148 disconnectFullSliderThumbLazyObserver(); 149 150 const thumbRoot = document.querySelector('.modal-fullscreen-slider .thumb-slider'); 151 const imgs = document.querySelectorAll('.modal-fullscreen-slider .thumb-slider img[data-src]'); 152 if (!thumbRoot || imgs.length === 0) return; 153 154 if (!('IntersectionObserver' in window)) { 155 imgs.forEach(loadFullSliderImage); 156 return; 157 } 158 159 fullSliderThumbLazyIo = new IntersectionObserver( 160 (entries, obs) => { 161 entries.forEach((entry) => { 162 if (!entry.isIntersecting) return; 163 loadFullSliderImage(entry.target); 164 obs.unobserve(entry.target); 165 }); 166 }, 167 { 168 root: thumbRoot, 169 rootMargin: '0px 480px', 170 threshold: 0.01, 171 } 172 ); 173 174 imgs.forEach((img) => fullSliderThumbLazyIo.observe(img)); 175} 176 177// fullscreen slider ë°ì´í° ë¡ë 178function screenGallery(product_id, gallery_type = "", imgIndex = 0, callback) { 179 fetchGallery(product_id, gallery_type, (data) => { 180 renderFullScreenGallery(data.imageList, imgIndex); 181 if (typeof callback === 'function') callback(); 182 }); 183} 184 185// ë¦¬ë·°ì© fullscreen slider ë°ì´í° ë¡ë 186function reviewScreenGallery (product_id, review_id = null, imgIndex = 0, callback) { 187 fetchReviewGallery(product_id, review_id, (data) => { 188 renderFullScreenGallery(data.imageList, imgIndex); 189 if (typeof callback === 'function') callback(); 190 }); 191} 192 193// fullscreen ì´ê¸° 194function openFullScreen(fullscreenSlider) { 195 // 먼ì ì¬ë¼ì´ë를 íì 196 fullscreenSlider.style.opacity = 1; 197 fullscreenSlider.style.visibility = "visible"; 198 199 // ì ì²´íë©´ ìì²ì ìì íê² ì²ë¦¬ 200 const requestFullscreenSafely = () => { 201 try { 202 if (fullscreenSlider.requestFullscreen) { 203 fullscreenSlider.requestFullscreen().catch(err => { 204 console.warn("Fullscreen ìì² ì¤í¨:", err.message || err); 205 // ì ì²´íë©´ ì¤í¨ (ëª¨ë¬ íí) 206 }); 207 } else if (fullscreenSlider.webkitRequestFullscreen) { 208 fullscreenSlider.webkitRequestFullscreen(); 209 } else if (fullscreenSlider.msRequestFullscreen) { 210 fullscreenSlider.msRequestFullscreen(); 211 } else { 212 console.warn("ëª¨ë¬ ííë¡ íì"); 213 } 214 } catch (err) { 215 console.warn("Fullscreen ìì² ì¤ ì¤ë¥:", err.message || err); 216 // ì¤ë¥ ë°ì ììë ì¬ë¼ì´ëë íìë¨ (ëª¨ë¬ íí) 217 } 218 }; 219 220 // ì ì²´íë©´ì ìë (ì¤í¨íë©´ 모ë¬) 221 requestFullscreenSafely(); 222} 223 224// fullscreen ë«ê¸° 225function closeFullScreen(fullscreenSlider) { 226 if (document.fullscreenElement && document.exitFullscreen) { 227 document.exitFullscreen(); 228 } else { 229 fullscreenSlider.style.opacity = 0; 230 fullscreenSlider.style.visibility = "hidden"; 231 refreshUnderlyingGalleryImages(); 232 } 233} 234 235// fullscreen slider ë§ë¤ê¸° 236function renderFullScreenGallery(imageList, imgIndex = 0) { 237 238 disconnectFullSliderThumbLazyObserver(); 239 240 const thumbWrapper = document.querySelector(".thumb-slider .swiper-wrapper"); 241 const originWrapper = document.querySelector(".origin-slider .swiper-wrapper"); 242 243 // ì´ì ë ë ì´ë¯¸ì§ì src 를 먼ì ë¹ì ëì½ë© ë©ëª¨ë¦¬ë¥¼ ì¦ì í´ì (ë°ë³µ open ì ëì ë°©ì§) 244 [thumbWrapper, originWrapper].forEach((wrapper) => { 245 wrapper.querySelectorAll('img').forEach((img) => { 246 img.onload = null; 247 img.onerror = null; 248 img.removeAttribute('data-src'); 249 img.src = FULL_SLIDER_LAZY_PLACEHOLDER; 250 }); 251 }); 252 253 thumbWrapper.innerHTML = ""; 254 originWrapper.innerHTML = ""; 255 256 imageList.forEach(item => { 257 const alt = item.alt ?? "ì´ë¯¸ì§"; 258 const imageUrl = item.webPath || item.link || ""; 259 260 const thumbSlide = document.createElement("div"); 261 thumbSlide.classList.add("swiper-slide"); 262 263 const thumbImg = document.createElement("img"); 264 if (imageUrl) { 265 thumbImg.src = FULL_SLIDER_LAZY_PLACEHOLDER; 266 thumbImg.dataset.src = window.resizeGalleryImageUrl(imageUrl, 'small'); 267 } 268 thumbImg.alt = alt; 269 thumbSlide.appendChild(thumbImg); 270 thumbWrapper.appendChild(thumbSlide); 271 272 const originSlide = document.createElement("div"); 273 originSlide.classList.add("swiper-slide"); 274 275 const originImg = document.createElement("img"); 276 if (imageUrl) { 277 originImg.src = FULL_SLIDER_LAZY_PLACEHOLDER; 278 originImg.dataset.src = window.resizeGalleryImageUrl(imageUrl, 'large'); 279 } 280 originImg.alt = alt; 281 originSlide.appendChild(originImg); 282 originWrapper.appendChild(originSlide); 283 }); 284 285 if (thumbSlider) thumbSlider.destroy(true, true); 286 if (originSlider) originSlider.destroy(true, true); 287 288 thumbSlider = new Swiper(".thumb-slider", { 289 spaceBetween: 8, 290 slidesPerView: "auto", 291 freeMode: true, 292 centerInsufficientSlides: true, 293 watchSlidesProgress: true, 294 watchSlidesVisibility: true, 295 slideToClickedSlide: true, 296 }); 297 298 originSlider = new Swiper(".origin-slider", { 299 speed: 300, 300 effect: 'fade', 301 fadeEffect: { 302 crossFade: true 303 }, 304 spaceBetween: 10, 305 navigation: { 306 nextEl: ".origin-slider .swiper-button-next", 307 prevEl: ".origin-slider .swiper-button-prev", 308 }, 309 thumbs: { 310 swiper: thumbSlider, 311 }, 312 on: { 313 afterInit() { 314 loadOriginNeighborImages(this); 315 }, 316 slideChange() { 317 loadOriginNeighborImages(this); 318 }, 319 }, 320 }); 321 322 initLazyFullSliderThumbImages(); 323 324 setTimeout(() => {
325 originSlider.update(); 326 originSlider.slideTo(imgIndex, 0); 327 loadOriginNeighborImages(originSlider); 328 }, 0); 329} 330 331document.addEventListener("DOMContentLoaded", () => { 332 const fullscreenSlider = document.getElementById("fullscreenSlider"); 333 334 document.body.addEventListener("click", function (e) { 335 const btnFullSlider = e.target.closest('.js-show-full-slider'); 336 const originSliderItem = e.target.closest(".origin-slider .swiper-slide"); 337 const closeBtn = e.target.closest('.btn-fullscreen-close'); 338 const btnReviewSlider = e.target.closest('.js-show-review-slider'); 339 340 // ì¼ë° ìíì© ì¬ë¼ì´ë 341 if (btnFullSlider) { 342 const imgIndex = parseInt(btnFullSlider.dataset.index, 10) || 0; 343 const product_id = btnFullSlider.getAttribute("data-product_id") || btnFullSlider.getAttribute("product_id"); 344 const gallery_type = btnFullSlider.getAttribute("data-gallery_type") || btnFullSlider.getAttribute("gallery_type") || ""; 345 346 if (!product_id) return; 347 348 if (lastProductId === product_id && lastGalleryType === gallery_type) { 349 openFullScreen(fullscreenSlider); 350 setTimeout(() => { 351 originSlider.slideTo(imgIndex, 0); 352 loadOriginNeighborImages(originSlider); 353 recoverFullSliderImages(); 354 }, 0); 355 return; 356 } 357 //console.log('ì¼ë°ì©',product_id,gallery_type,imgIndex); 358 359 screenGallery(product_id, gallery_type, imgIndex, () => { 360 lastProductId = product_id; 361 lastGalleryType = gallery_type; 362 // ì¬ì©ì ìí¸ìì© ì»¨í ì¤í¸ë¥¼ ì ì§í기 ìí´ ì¦ì ì¤í 363 openFullScreen(fullscreenSlider); 364 }); 365 } 366 367 // ë¦¬ë·°ì© ì¬ë¼ì´ë 368 if (btnReviewSlider) { 369 const imgIndex = parseInt(btnReviewSlider.dataset.index, 10) || 0; 370 const product_id = btnReviewSlider.getAttribute("data-product-id") || btnReviewSlider.getAttribute("product_id"); 371 const review_id = btnReviewSlider.getAttribute("data-review-id") || btnReviewSlider.getAttribute("review_id") || null; 372 373 if (!product_id) return; 374 375 if (lastReviewProductId === product_id && lastReviewId === review_id) { 376 openFullScreen(fullscreenSlider); 377 setTimeout(() => { 378 originSlider.slideTo(imgIndex, 0); 379 loadOriginNeighborImages(originSlider); 380 recoverFullSliderImages(); 381 }, 0); 382 return; 383 } 384 //console.log('리뷰ì©',product_id,review_id,imgIndex); 385 386 reviewScreenGallery(product_id, review_id, imgIndex, () => { 387 lastReviewProductId = product_id; 388 lastReviewId = review_id; 389 openFullScreen(fullscreenSlider); 390 setTimeout(() => { 391 originSlider.slideTo(imgIndex, 0); 392 }, 0); 393 }); 394 } 395 396 // ë«ê¸° ë²í¼ ëë ì¬ë¼ì´ë ì´ë¯¸ì§ í´ë¦ ì ë«ê¸° 397 if (closeBtn || (originSliderItem && document.fullscreenElement)) { 398 closeFullScreen(fullscreenSlider); 399 } 400 }); 401 402 document.addEventListener("fullscreenchange", () => { 403 if (!document.fullscreenElement) { 404 fullscreenSlider.style.opacity = 0; 405 fullscreenSlider.style.visibility = "hidden"; 406 refreshUnderlyingGalleryImages(); 407 } 408 }); 409 410 // ë¤ë¥¸ ì±/í ë¤ë ì¨ ë¤: iOS ê° ë°±ê·¸ë¼ì´ëìì ì´ë¯¸ì§ ë©ëª¨ë¦¬ë¥¼ íìí ê²½ì° ë³µêµ¬ 411 document.addEventListener("visibilitychange", () => { 412 if (document.visibilityState !== "visible") return; 413 refreshFullSliderActiveImages(); 414 }); 415 416 // bfcache ë³µì(ë¤ë¡ê°ê¸° ë±): íì/ì¤ë¨ì¼ë¡ ê¹¨ì§ ì´ë¯¸ì§ ì¬ë¡ë 417 window.addEventListener("pageshow", (event) => { 418 if (!event.persisted) return; 419 recoverFullSliderImages(); 420 refreshFullSliderActiveImages(); 421 }); 422});
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.