PageSourceSearch

https://www.samedpavilionresort.com/assets/js/site-v19.js

js samedpavilionresort.com collected 2026-10-08 06:57:34 UTC 7,332 bytes, 143 lines download raw bytes

1(()=>{
2  'use strict';
3  const galleries=[...document.querySelectorAll('[data-room-gallery]')];
4  if(!galleries.length)return;
5  const reduced=window.matchMedia('(prefers-reduced-motion: reduce)').matches;
6  const lang=document.documentElement.lang==='en'?'en':'th';
7  let activeGallery=null,activeIndex=0,lastFocus=null,closeTimer=null,touchStartX=null;
8
9  const modal=document.createElement('div');
10  modal.className='room-lightbox';
11  modal.hidden=true;
12  modal.setAttribute('role','dialog');
13  modal.setAttribute('aria-modal','true');
14  modal.setAttribute('aria-label',lang==='en'?'Room photo gallery':'แกลเลอรีภาพห้องพัก');
15  modal.innerHTML=`
16    <div class="room-lightbox-head">
17      <p class="room-lightbox-title"></p>
18      <button class="room-lightbox-close" type="button" aria-label="${lang==='en'?'Close gallery':'ปิดแกลเลอรี'}">×</button>
19    </div>
20    <div class="room-lightbox-stage">
21      <button class="room-lightbox-nav prev" type="button" aria-label="${lang==='en'?'Previous photo':'ภาพก่อนหน้า'}">‹</button>
22      <img class="room-lightbox-image" alt="" width="1440" height="810">
23      <button class="room-lightbox-nav next" type="button" aria-label="${lang==='en'?'Next photo':'ภาพถัดไป'}">›</button>
24    </div>
25    <div class="room-lightbox-foot" aria-live="polite"></div>`;
26  document.body.append(modal);
27  const lbImage=modal.querySelector('.room-lightbox-image');
28  const lbTitle=modal.querySelector('.room-lightbox-title');
29  const lbCount=modal.querySelector('.room-lightbox-foot');
30  const lbClose=modal.querySelector('.room-lightbox-close');
31
32  const items=gallery=>[...gallery.querySelectorAll('.room-thumbs img')].map((img,i)=>({
33    src:img.dataset.full||img.src,alt:img.alt||`${gallery.dataset.roomGallery} ${i+1}`,thumb:img
34  }));
35
36  const preload=(list,index)=>{
37    [-1,1].forEach(step=>{const p=new Image();p.src=list[(index+step+list.length)%list.length].src;});
38  };
39
40  function setGalleryImage(gallery,index,animate=true){
41    const list=items(gallery); if(!list.length)return;
42    index=(index+list.length)%list.length;
43    gallery.dataset.currentIndex=String(index);
44    const main=gallery.querySelector('.room-main');
45    const stage=gallery.querySelector('.room-stage');
46    const count=gallery.querySelector('.room-photo-count');
47    const apply=()=>{
48      main.src=list[index].src; main.alt=list[index].alt;
49      list.forEach((x,i)=>{x.thumb.classList.toggle('active',i===index);if(i===index)x.thumb.setAttribute('aria-current','true');else x.thumb.removeAttribute('aria-current');});
50      if(count)count.textContent=`${index+1} / ${list.length}`;
51      list[index].thumb.scrollIntoView({behavior:reduced?'auto':'smooth',block:'nearest',inline:'nearest'});
52      preload(list,index);
53    };
54    if(!animate||reduced){apply();return;}
55    stage.classList.add('is-changing');
56    const temp=new Image();
57    temp.onload=()=>{apply();requestAnimationFrame(()=>stage.classList.remove('is-changing'));};
58    temp.onerror=()=>{apply();stage.classList.remove('is-changing');};
59    temp.src=list[index].src;
60  }
61
62  function showLightbox(index,animate=true){
63    if(!activeGallery)return;
64    const list=items(activeGallery); if(!list.length)return;
65    activeIndex=(index+list.length)%list.length;
66    const item=list[activeIndex];
67    lbTitle.textContent=activeGallery.dataset.roomGallery||'';
68    lbCount.textContent=`${activeIndex+1} / ${list.length}`;
69    lbImage.alt=item.alt;
70    if(animate&&!reduced)lbImage.classList.add('is-switching');
71    const temp=new Image();
72    temp.onload=()=>{
73      lbImage.src=item.src;
74      requestAnimationFrame(()=>{lbImage.classList.add('is-ready');lbImage.classList.remove('is-switching');});
75      preload(list,activeIndex);
76    };
77    temp.onerror=()=>{lbImage.src=item.src;lbImage.classList.add('is-ready');lbImage.classList.remove('is-switching');};
78    temp.src=item.src;
79  }
80
81  function openLightbox(gallery,index){
82    clearTimeout(closeTimer);
83    activeGallery=gallery;activeIndex=index;lastFocus=document.activeElement;
84    modal.hidden=false;document.body.classList.add('no-scroll');
85    lbImage.classList.remove('is-ready');
86    requestAnimationFrame(()=>{modal.classList.add('is-open');showLightbox(index,false);lbClose.focus({preventScroll:true});});
87  }
88
89  function closeLightbox(){
90    modal.classList.remove('is-open');lbImage.classList.remove('is-ready');document.body.classList.remove('no-scroll');
91    closeTimer=setTimeout(()=>{modal.hidden=true;activeGallery=null;if(lastFocus?.focus)lastFocus.focus({preventScroll:true});},reduced?0:280);
92  }
93
94  galleries.forEach(gallery=>{
95    gallery.dataset.currentIndex='0';
96    const thumbWrap=gallery.querySelector('.room-thumbs');
97    const openBtn=gallery.querySelector('[data-room-open]');
98    const prev=gallery.querySelector('[data-room-prev]');
99    const next=gallery.querySelector('[data-room-next]');
100    const current=()=>Number(gallery.dataset.currentIndex||0);
101
102    // Capture phase suppresses the earlier basic thumbnail handler from site.js.
103    thumbWrap?.addEventListener('click',e=>{
104      const img=e.target.closest('img');if(!img)return;
105      e.preventDefault();e.stopImmediatePropagation();
106      setGalleryImage(gallery,[...thumbWrap.querySelectorAll('img')].indexOf(img));
107    },true);
108    thumbWrap?.addEventListener('keydown',e=>{
109      const img=e.target.closest('img');if(!img)return;
110      const thumbs=[...thumbWrap.querySelectorAll('img')];let index=thumbs.indexOf(img);
111      if(e.key==='Enter'||e.key===' '){e.preventDefault();setGalleryImage(gallery,index);}
112      if(e.key==='ArrowRight'||e.key==='ArrowLeft'){
113        e.preventDefault();index=(index+(e.key==='ArrowRight'?1:-1)+thumbs.length)%thumbs.length;thumbs[index].focus();setGalleryImage(gallery,index);
114      }
115    });
116    prev?.addEventListener('click',()=>setGalleryImage(gallery,current()-1));
117    next?.addEventListener('click',()=>setGalleryImage(gallery,current()+1));
118    openBtn?.addEventListener('click',()=>openLightbox(gallery,current()));
119  });
120
121  modal.querySelector('.room-lightbox-nav.prev').addEventListener('click',()=>showLightbox(activeIndex-1));
122  modal.querySelector('.room-lightbox-nav.next').addEventListener('click',()=>showLightbox(activeIndex+1));
123  lbClose.addEventListener('click',closeLightbox);
124  modal.addEventListener('click',e=>{if(e.target===modal)closeLightbox();});
125  document.addEventListener('keydown',e=>{
126    if(modal.hidden)return;
127    if(e.key==='Escape')closeLightbox();
128    if(e.key==='ArrowLeft')showLightbox(activeIndex-1);
129    if(e.key==='ArrowRight')showLightbox(activeIndex+1);
130    if(e.key==='Tab'){
131      const focusable=[...modal.querySelectorAll('button:not([disabled])')];
132      if(!focusable.length)return;
133      const first=focusable[0],last=focusable[focusable.length-1];
134      if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus();}
135      else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus();}
136    }
137  });
138  modal.addEventListener('pointerdown',e=>{touchStartX=e.clientX;});
139  modal.addEventListener('pointerup',e=>{
140    if(touchStartX===null)return;const dx=e.clientX-touchStartX;touchStartX=null;
141    if(Math.abs(dx)>55)showLightbox(activeIndex+(dx<0?1:-1));
142  });
143})();

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.