1import PhotoSwipeDynamicCaption from "/js/photoswipe/photoswipe-dynamic-caption-plugin.esm.js"; 2import PhotoSwipeLightbox from "/js/photoswipe/photoswipe-lightbox.esm.min.js"; 3import PhotoSwipe from "/js/photoswipe/photoswipe.esm.min.js"; 4 5function isVideoContent(content) { 6 return content && content.data && content.data.type === "video"; 7} 8 9class VideoContentSetup { 10 constructor(lightbox, options) { 11 this.options = options; 12 13 this.initLightboxEvents(lightbox); 14 lightbox.on("init", () => { 15 this.initPswpEvents(lightbox.pswp); 16 }); 17 } 18 19 initLightboxEvents(lightbox) { 20 lightbox.on("contentLoad", this.onContentLoad.bind(this)); 21 lightbox.on("contentDestroy", this.onContentDestroy.bind(this)); 22 lightbox.on("contentActivate", this.onContentActivate.bind(this)); 23 lightbox.on("contentDeactivate", this.onContentDeactivate.bind(this)); 24 lightbox.on("contentAppend", this.onContentAppend.bind(this)); 25 lightbox.on("contentResize", this.onContentResize.bind(this)); 26 27 lightbox.addFilter( 28 "isKeepingPlaceholder", 29 this.isKeepingPlaceholder.bind(this) 30 ); 31 lightbox.addFilter( 32 "isContentZoomable", 33 this.isContentZoomable.bind(this) 34 ); 35 lightbox.addFilter( 36 "useContentPlaceholder", 37 this.useContentPlaceholder.bind(this) 38 ); 39 40 lightbox.addFilter("domItemData", (itemData, element, linkEl) => { 41 if (itemData.type === "video" && linkEl) { 42 itemData.isVideoElement = true; 43 if (linkEl.dataset.pswpVideoSources) { 44 itemData.videoSources = JSON.parse(pswpVideoSources); 45 } else if (linkEl.dataset.pswpVideoSrc) { 46 itemData.videoSrc = linkEl.dataset.pswpVideoSrc; 47 } else { 48 itemData.videoSrc = linkEl.href; 49 } 50 } 51 if (linkEl.dataset.pswpExternalHtml) { 52 itemData.externalHtml = linkEl.dataset.pswpExternalHtml; 53 itemData.isVideoElement = false; 54 } 55 56 return itemData; 57 }); 58 } 59 60 initPswpEvents(pswp) { 61 // Prevent draggin when pointer is in bottom part of the video 62 // todo: add option for this 63 pswp.on("pointerDown", (e) => { 64 const slide = pswp.currSlide; 65 if (isVideoContent(slide) && this.options.preventDragOffset) { 66 const origEvent = e.originalEvent; 67 if (origEvent.type === "pointerdown") { 68 const videoHeight = Math.ceil( 69 slide.height * slide.currZoomLevel 70 ); 71 const verticalEnding = videoHeight + slide.bounds.center.y; 72 const pointerYPos = origEvent.pageY - pswp.offset.y; 73 if ( 74 pointerYPos > 75 verticalEnding - this.options.preventDragOffset && 76 pointerYPos < verticalEnding 77 ) { 78 e.preventDefault(); 79 } 80 } 81 } 82 }); 83 84 // do not append video on nearby slides 85 pswp.on("appendHeavy", (e) => { 86 if (isVideoContent(e.slide) && !e.slide.isActive) { 87 e.preventDefault(); 88 } 89 }); 90 91 pswp.on("close", () => { 92 if (isVideoContent(pswp.currSlide.content)) { 93 // Switch from zoom to fade closing transition, 94 // as zoom transition is choppy for videos 95 if ( 96 !pswp.options.showHideAnimationType || 97 pswp.options.showHideAnimationType === "zoom" 98 ) { 99 pswp.options.showHideAnimationType = "fade"; 100 } 101 102 // pause video when closing 103 this.pauseVideo(pswp.currSlide.content); 104 } 105 }); 106 } 107 108 onContentDestroy({ content }) { 109 if (isVideoContent(content)) { 110 if (content._videoPosterImg) { 111 content._videoPosterImg.onload = 112 content._videoPosterImg.onerror = null; 113 content._videoPosterImg = null; 114 } 115 } 116 } 117 118 onContentResize(e) { 119 if (isVideoContent(e.content)) { 120 e.preventDefault(); 121 122 const width = e.width; 123 const height = e.height; 124 const content = e.content; 125 126 if (content.element) { 127 content.element.style.width = width + "px"; 128 content.element.style.height = height + "px"; 129 } 130 131 if (content.slide && content.slide.placeholder) { 132 // override placeholder size, so it more accurately matches the video 133 const placeholderElStyle = 134 content.slide.placeholder.element.style; 135 placeholderElStyle.transform = "none"; 136 placeholderElStyle.width = width + "px"; 137 placeholderElStyle.height = height + "px"; 138 } 139 } 140 } 141 142 isKeepingPlaceholder(isZoomable, content) { 143 if (isVideoContent(content)) { 144 return false;
145 } 146 return isZoomable; 147 } 148 149 isContentZoomable(isZoomable, content) { 150 if (isVideoContent(content)) { 151 return false; 152 } 153 return isZoomable; 154 } 155 156 onContentActivate({ content }) { 157 if (isVideoContent(content) && this.options.autoplay) { 158 this.playVideo(content); 159 } 160 } 161 162 onContentDeactivate({ content }) { 163 if (isVideoContent(content)) { 164 this.pauseVideo(content); 165 } 166 } 167 168 onContentAppend(e) { 169 if (isVideoContent(e.content)) { 170 e.preventDefault(); 171 e.content.isAttached = true; 172 e.content.appendImage(); 173 } 174 } 175 176 onContentLoad(e) { 177 const content = e.content; 178 179 if (!isVideoContent(e.content)) { 180 return; 181 } 182 183 e.preventDefault(); 184 185 if (content.element) { 186 return; 187 } 188 189 content.state = "loading"; 190 content.type = "video"; 191 192 // show loader until video is loaded 193 content.element = document.createElement("div"); 194 content.element.style.width = "100%"; 195 content.element.style.height = "100%"; 196 197 //append spinner 198 const spinner = document.createElement("div"); 199 spinner.classList.add("loader-container-dark"); 200 const spinnerElement = document.createElement("div"); 201 spinnerElement.classList.add("loader-mansory"); 202 spinner.appendChild(spinnerElement); 203 content.element.appendChild(spinner); 204 205 if (content.data.externalHtml) { 206 const externalHtmlContainer = document.createElement("div"); 207 externalHtmlContainer.style.width = "100%"; 208 externalHtmlContainer.style.height = "100%"; 209 externalHtmlContainer.innerHTML = content.data.externalHtml; 210 const iframe = externalHtmlContainer.querySelector("iframe"); 211 if (iframe && iframe.src.includes("vimeo")) { 212 if (iframe.src.includes("?")) { 213 iframe.src += "&"; 214 } else { 215 iframe.src += "?"; 216 } 217 iframe.src += "autoplay=1&loop=1&autopause=0&muted=0"; 218 } else if (iframe && iframe.src.includes("youtube")) { 219 if (iframe.src.includes("?")) { 220 iframe.src += "&"; 221 } else { 222 iframe.src += "?"; 223 } 224 iframe.src += "autoplay=1"; 225 } 226 iframe.style.width = "100%"; 227 iframe.style.height = "100%"; 228 content.element.appendChild(externalHtmlContainer); 229 } else { 230 content.element.appendChild(document.createElement("video")); 231 } 232 233 const video = content.element.querySelector("video"); 234 //check if video is loaded 235 if (content.data.externalHtml) { 236 const iframe = content.element.querySelector("iframe"); 237 iframe.onload = () => { 238 setTimeout(() => { 239 content.element.removeChild(spinner); 240 }, 1000); 241 }; 242 } else { 243 video.onloadeddata = () => { 244 content.element.removeChild(spinner); 245 }; 246 } 247 248 if (this.options.videoAttributes && video) { 249 for (let key in this.options.videoAttributes) { 250 video.setAttribute( 251 key, 252 this.options.videoAttributes[key] || "" 253 ); 254 } 255 } 256 content.element.style.position = "absolute"; 257 content.element.style.left = 0; 258 content.element.style.top = 0; 259 260 if (content.data.isVideoElement) { 261 if (content.data.videoSources) { 262 content.data.videoSources.forEach((source) => { 263 let sourceEl = document.createElement("source"); 264 sourceEl.src = source.src; 265 sourceEl.type = source.type; 266 video.appendChild(sourceEl); 267 }); 268 } else if (content.data.videoSrc) { 269 video.src = content.data.videoSrc; 270 } 271 } 272 } 273 274 playVideo(content) { 275 if (content.element) { 276 if (content.data.isVideoElement) { 277 const video = content.element.querySelector("video"); 278 video.play(); 279 } else if (content.data.externalHtml) { 280 // For iframes, restore src if it was removed 281 const iframe = content.element.querySelector("iframe"); 282 if (iframe && iframe.dataset.originalSrc) { 283 iframe.src = iframe.dataset.originalSrc; 284 } 285 } 286 } 287 } 288 289 pauseVideo(content) { 290 if (content.element) { 291 if (content.data.isVideoElement) { 292 const video = content.element.querySelector("video"); 293 video.pause(); 294 } else if (content.data.externalHtml) { 295 // For iframes (YouTube/Vimeo), remove src to stop playback 296 const iframe = content.element.querySelector("iframe"); 297 if (iframe) {
298 iframe.dataset.originalSrc = iframe.src; 299 iframe.src = ""; 300 } 301 } 302 } 303 } 304 305 useContentPlaceholder(usePlaceholder, content) { 306 if (isVideoContent(content)) { 307 return false; 308 } 309 return usePlaceholder; 310 } 311} 312 313const defaultOptions = { 314 videoAttributes: { controls: "", playsinline: "", preload: "auto" }, 315 autoplay: true, 316 317 // prevent drag/swipe gesture over the bottom part of video 318 // set to 0 to disable 319 preventDragOffset: 40, 320}; 321 322class PhotoSwipeVideoPlugin { 323 constructor(lightbox, options) { 324 new VideoContentSetup(lightbox, { 325 ...defaultOptions, 326 ...options, 327 }); 328 } 329} 330 331function loadPhotoSwipe() { 332 const galleries = document.querySelectorAll(".photo-gallery"); 333 334 const leftArrowSVGString = 335 '<svg width="41" height="34" viewBox="0 0 41 34" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.46875 16.625L39.4451 16.6231" stroke="#AECCE7" stroke-width="1.94444" stroke-linecap="square" stroke-linejoin="round"/><path d="M15.1658 1.55469L1.55469 17.1121L15.1658 32.6658" stroke="#AECCE7" stroke-width="1.94444" stroke-linejoin="round"/></svg>'; 336 const rightArrowSVGString = 337 '<svg width="41" height="34" viewBox="0 0 41 34" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M37.5352 17.5977L1.5588 17.5996" stroke="#AECCE7" stroke-width="1.94444" stroke-linecap="square" stroke-linejoin="round"/><path d="M25.8342 32.668L39.4453 17.1106L25.8342 1.55686" stroke="#AECCE7" stroke-width="1.94444" stroke-linejoin="round"/></svg>'; 338 339 const closeIcon =
340 '<svg width="37" height="37" viewBox="0 0 37 37" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M34.9167 2.25L2.25 34.9167" stroke="#AECCE7" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/><path d="M34.9167 34.9167L2.25 2.25" stroke="#AECCE7" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/></svg>'; 341 342 galleries.forEach((gallery) => { 343 const images = gallery.querySelectorAll("a"); 344 images.forEach((image) => { 345 const img = new Image(); 346 img.src = image.href; 347 img.onload = function () { 348 image.setAttribute("data-pswp-width", this.width); 349 image.setAttribute("data-pswp-height", this.height); 350 }; 351 }); 352 353 const smallScreenPadding = { 354 top: 0, 355 bottom: 0, 356 left: 0, 357 right: 0, 358 }; 359 const largeScreenPadding = { 360 top: 30, 361 bottom: 30, 362 left: 0, 363 right: 0, 364 }; 365 366 const lightbox = new PhotoSwipeLightbox({ 367 gallery: gallery, 368 children: ".photo-gallery-item", 369 pswpModule: PhotoSwipe, 370 paddingFn: (viewportSize) => { 371 return viewportSize.x < 700 372 ? smallScreenPadding 373 : largeScreenPadding; 374 }, 375 preload: [1, 4], 376 showHideAnimationType: "fade", 377 arrowPrevSVG: leftArrowSVGString, 378 arrowNextSVG: rightArrowSVGString, 379 closeSVG: closeIcon, 380 zoom: false, 381 counter: false, 382 }); 383 new PhotoSwipeVideoPlugin(lightbox, {}); 384 new PhotoSwipeDynamicCaption(lightbox, { 385 type: "below", 386 }); 387 388 setTimeout(() => { 389 lightbox.init(); 390 }, 500); 391 }); 392} 393 394loadPhotoSwipe();
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.