Jake Vanderwerf
9 days ago 47e77f9fac1155c536b2b87fec552c7fcce66fa6
assets/js/concise/Gallery.js
@@ -26,7 +26,6 @@
      this.initModal();
      this.initListeners();
      this.initSubscribers();
   }
   /*********************************************************************
    ELEMENTS
@@ -42,7 +41,8 @@
            image: '.image',
            leftImage: '.image-left',
            rightImage: '.image-right',
            counter: '.counter'
            counter: '.counter',
            caption: 'details .item-info'
         },
      }
      this.ui = window.uiFromSelectors(this.elements);
@@ -66,6 +66,16 @@
      let selector = filtered ? `[data-gallery="${filtered}"]` : this.elements.imageSelector;
      this.items = Array.from(document.querySelectorAll(selector))
         .map((img, index) => {
            let caption = '';
            if (Object.hasOwn(img.dataset, 'caption')) {
               caption = img.parentElement.querySelector(img.dataset.caption).cloneNode(true);
            } else {
               caption = img.nextElementSibling && img.nextElementSibling.tagName === 'FIGCAPTION' ?
                  img.nextElementSibling.textContent :
                  '';
            }
            return {
               id: img.dataset.id||index,
               srcset: img.srcset || img.src, // Clone the srcset from page
@@ -73,7 +83,8 @@
               src: img.currentSrc || img.src, // Fallback
               full: img.dataset.full || img.src,
               alt: img.alt || '',
               element: img
               element: img,
               caption: caption
            };
         });
   }
@@ -101,10 +112,8 @@
         this.toggleGallery(true);
      } else if (this.modal.isOpen) {
         if (window.targetCheck(e, this.elements.gallery.nextButton)) {
            console.log('Next');
            this.nextElement();
         } else if (window.targetCheck(e, this.elements.gallery.prevButton)) {
            console.log('Previous');
            this.prevElement();
         }
      }
@@ -193,17 +202,12 @@
      // PINCH (two pointers)
      if (this.activePointers.size === 2) {
         const pts = [...this.activePointers.values()];
         const dist = Math.hypot(
            pts[0].x - pts[1].x,
            pts[0].y - pts[1].y
         );
         const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
         const increment = this.pinchStartScale * (dist / this.pinchStartDist) - this.zoom.scale;
         const newScale = this.pinchStartScale * (dist / this.pinchStartDist);
         const increment = newScale - this.zoom.scale;
         // Zoom centered (NO midpoint)
         this.handleZoom(increment);
         const midX = (pts[0].x + pts[1].x) / 2;  // ← anchor to finger midpoint
         const midY = (pts[0].y + pts[1].y) / 2;
         this.handleZoom(increment, midX, midY);
         return;
      }
@@ -216,35 +220,26 @@
   }
   onPointerUp(e) {
      const last = this.activePointers.get(e.pointerId); // grab before delete
      this.activePointers.delete(e.pointerId);
      if (this.activePointers.size < 2) {
         this.pinchStartDist = 0;
      }
      // Only check for swipe if we weren't panning and no more active pointers
      if (!this.zoom.panning && this.activePointers.size === 0) {
         // End of tap or swipe - detect swipe
         this.swipe.endX = e.clientX;
         this.swipe.endY = e.clientY;
         const dx = this.swipe.endX - this.swipe.startX;
         const dy = this.swipe.endY - this.swipe.startY;
      if (this.zoom.scale <= 1 && !this.zoom.panning && this.activePointers.size === 0) {
         const endX = last?.x ?? e.clientX; // use tracked position, fall back to event
         const endY = last?.y ?? e.clientY;
         const dx = endX - this.swipe.startX;
         const dy = endY - this.swipe.startY;
         if (Math.abs(dx) > this.zoom.threshold) {
            if (dx > 0) {
               console.log('Swipe right');
               this.prevElement();
            } else {
               console.log('Swipe left');
               this.nextElement();
            }
         if (Math.abs(dx) > this.zoom.threshold && Math.abs(dx) > Math.abs(dy)) {
            dx > 0 ? this.prevElement() : this.nextElement();
         }
      }
      // Reset panning state when all pointers are released
      if (this.activePointers.size === 0) {
         this.zoom.panning = false;
         // Reset cursor based on zoom state
         this.ui.gallery.image.style.cursor = this.zoom.scale > 1 ? 'grab' : 'default';
      }
   }
@@ -290,12 +285,19 @@
   }
   applyTransform() {
      // this.clampPan();
      this.clampPan();
      const img = this.ui.gallery.image;
      img.style.transform = `translate(${this.zoom.x}px, ${this.zoom.y}px) scale(${this.zoom.scale})`;
      // Update cursor based on zoom level
      img.style.cursor = this.zoom.scale > 1 ? 'grab' : 'default';
   }
   clampPan() {
      const img = this.ui.gallery.image;
      const excessX = Math.max(0, (img.offsetWidth  * this.zoom.scale - window.innerWidth)  / 2);
      const excessY = Math.max(0, (img.offsetHeight * this.zoom.scale - window.innerHeight) / 2);
      this.zoom.x = Math.max(-excessX, Math.min(excessX, this.zoom.x));
      this.zoom.y = Math.max(-excessY, Math.min(excessY, this.zoom.y));
   }
   resetZoom() {
      this.zoom.scale = 1;
      this.zoom.x = 0;
@@ -394,7 +396,17 @@
      galleryImg.src = item.src; // Fallback
      galleryImg.alt = item.alt;
      // ALWAYS load full resolution for zoom quality
      window.removeChildren(this.ui.gallery.caption);
      if (typeof item.caption === 'object') {
         this.ui.gallery.caption.append(item.caption);
      } else {
         this.ui.gallery.caption.textContent = item.caption;
      }
      if (item.full && item.full !== item.src) {
         const fullImg = new Image();
         fullImg.onload = () => {