1 2/*! 3 * Lightbox for Bootstrap 5 v1.8.3 (https://trvswgnr.github.io/bs5-lightbox/) 4 * Copyright 2024 Travis Aaron Wagner (https://github.com/trvswgnr/) 5 * Licensed under MIT (https://github.com/trvswgnr/bs5-lightbox/blob/main/LICENSE) 6 */ 7(function webpackUniversalModuleDefinition(root, factory) { 8 if (typeof exports === 'object' && typeof module === 'object') 9 module.exports = factory(require("bootstrap")); 10 else if (typeof define === 'function' && define.amd) 11 define("Lightbox", ["bootstrap"], factory); 12 else if (typeof exports === 'object') 13 exports["Lightbox"] = factory(require("bootstrap")); 14 else 15 root["Lightbox"] = factory(root["bootstrap"]); 16})(self, function (__WEBPACK_EXTERNAL_MODULE__814__) { 17 return /******/ (function () { // webpackBootstrap 18/******/ "use strict"; 19/******/ var __webpack_modules__ = ({ 20 21/***/ 814: 22/***/ (function (module) { 23 24 module.exports = __WEBPACK_EXTERNAL_MODULE__814__; 25 26 /***/ 27}) 28 29 /******/ 30}); 31/************************************************************************/ 32/******/ // The module cache 33/******/ var __webpack_module_cache__ = {}; 34/******/ 35/******/ // The require function 36/******/ function __webpack_require__(moduleId) { 37/******/ // Check if module is in cache 38/******/ var cachedModule = __webpack_module_cache__[moduleId]; 39/******/ if (cachedModule !== undefined) { 40/******/ return cachedModule.exports; 41 /******/ 42} 43/******/ // Create a new module (and put it into the cache) 44/******/ var module = __webpack_module_cache__[moduleId] = { 45/******/ // no module.id needed 46/******/ // no module.loaded needed 47/******/ exports: {} 48 /******/ 49}; 50/******/ 51/******/ // Execute the module function 52/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 53/******/ 54/******/ // Return the exports of the module 55/******/ return module.exports; 56 /******/ 57} 58/******/ 59/************************************************************************/ 60/******/ /* webpack/runtime/compat get default export */ 61/******/ !function () { 62/******/ // getDefaultExport function for compatibility with non-harmony modules 63/******/ __webpack_require__.n = function (module) { 64/******/ var getter = module && module.__esModule ? 65/******/ function () { return module['default']; } : 66/******/ function () { return module; }; 67/******/ __webpack_require__.d(getter, { a: getter }); 68/******/ return getter; 69 /******/ 70}; 71 /******/ 72}(); 73/******/ 74/******/ /* webpack/runtime/define property getters */ 75/******/ !function () { 76/******/ // define getter functions for harmony exports 77/******/ __webpack_require__.d = function (exports, definition) { 78/******/ for (var key in definition) { 79/******/ if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 80/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 81 /******/ 82} 83 /******/ 84} 85 /******/ 86}; 87 /******/ 88}(); 89/******/ 90/******/ /* webpack/runtime/hasOwnProperty shorthand */ 91/******/ !function () { 92/******/ __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } 93 /******/ 94}(); 95 /******/ 96 /************************************************************************/ 97 var __webpack_exports__ = {}; 98/* harmony import */ var bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(814); 99/* harmony import */ var bootstrap__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(bootstrap__WEBPACK_IMPORTED_MODULE_0__); 100 /** 101 * Lightbox for Bootstrap 5 102 * 103 * @file Creates a modal with a lightbox carousel. 104 * @module bs5-lightbox 105 */ 106 107 108 const bootstrap = { 109 Modal: bootstrap__WEBPACK_IMPORTED_MODULE_0__.Modal, 110 Carousel: bootstrap__WEBPACK_IMPORTED_MODULE_0__.Carousel 111 }; 112 class Lightbox { 113 constructor(el, options = {}) { 114 this.hash = this.randomHash(); 115 this.settings = Object.assign(Object.assign(Object.assign({}, bootstrap.Modal.Default), bootstrap.Carousel.Default), { 116 interval: false, 117 target: '[data-toggle="lightbox"]', 118 gallery: '', 119 size: 'xl', 120 constrain: true, 121 type: 'image' 122 }); 123 this.settings = Object.assign(Object.assign({}, this.settings), options); 124 this.modalOptions = (() => this.setOptionsFromSettings(bootstrap.Modal.Default))(); 125 this.carouselOptions = (() => this.setOptionsFromSettings(bootstrap.Carousel.Default))(); 126 if (this.settings.gallery.length == 0) { 127 this.settings.gallery = el.dataset['lbgroup']; 128 } 129 if (typeof el === 'string') { 130 this.settings.target = el; 131 el = document.querySelector(this.settings.target); 132 } 133 this.el = el; 134 this.type = el.dataset.type || this.settings.type; 135
136 this.src = this.getSrc(el); 137 this.sources = this.getGalleryItems(); 138 this.createCarousel(); 139 this.createModal(); 140 } 141 show() { 142 document.body.appendChild(this.modalElement); 143 this.modal.show(); 144 } 145 hide() { 146 this.modal.hide(); 147 } 148 setOptionsFromSettings(obj) { 149 return Object.keys(obj).reduce((p, c) => Object.assign(p, { [c]: this.settings[c] }), {}); 150 } 151 getSrc(el) { 152 let src = el.dataset.src || el.dataset.remote || el.href || 'http://imgs.gem2go.dev/1600x900'; 153 if (el.dataset.type === 'html') { 154 return src; 155 } 156 if (!/\:\/\//.test(src)) { 157 src = window.location.origin + src; 158 } 159 const url = new URL(src); 160 if (el.dataset.footer || el.dataset.description) { 161 let description = decodeURIComponent(el.dataset.description); 162 if (el.dataset.copyright) 163 description += ' ' + decodeURIComponent(el.dataset.copyright); 164 165 url.searchParams.set('caption', el.dataset.footer || description); 166 } 167 return url.toString(); 168 } 169 getGalleryItems() { 170 let galleryTarget; 171 if (this.settings.gallery) { 172 if (Array.isArray(this.settings.gallery)) { 173 return this.settings.gallery; 174 } 175 galleryTarget = this.settings.gallery; 176 } else if (this.el.dataset.gallery) { 177 galleryTarget = this.el.dataset.gallery; 178 } 179 const gallery = galleryTarget 180 ? [...new Set(Array.from(document.querySelectorAll(`[data-lbgroup="${galleryTarget}"]`), (v) => `${v.dataset.type ? v.dataset.type : this.type}${this.getSrc(v)}`))] 181 : [`${this.type ? this.type : ''}${this.src}`]; 182 return gallery; 183 } 184 getYoutubeId(src) { 185 if (!src) return false; 186 const matches = src.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/); 187 return matches && matches[2].length === 11 ? matches[2] : false; 188 } 189 getYoutubeLink(src) { 190 const youtubeId = this.getYoutubeId(src); 191 if (!youtubeId) { 192 return false; 193 } 194 195 const arr = src.split('?'); 196 let params = arr.length > 1 ? '?' + arr[1] : ''; 197 198 return `https://www.youtube.com/embed/${youtubeId}${params}`; 199 } 200 getInstagramEmbed(src) { 201 if (/instagram/.test(src)) { 202 src += /\/embed$/.test(src) ? '' : '/embed'; 203 return `<iframe src="${src}" class="start-50 translate-middle-x" style="max-width: 500px" frameborder="0" scrolling="no" allowtransparency="true"></iframe>`; 204 } 205 } 206 isEmbed(src) { 207 const regex = new RegExp('(' + Lightbox.allowedEmbedTypes.join('|') + ')'); 208 const isEmbed = regex.test(src); 209 const isImg = /\.(png|jpe?g|gif|svg|webp)/i.test(src) || this.el.dataset.type === 'image'; 210 211 return isEmbed || !isImg; 212 } 213 createCarousel() { 214 const template = document.createElement('template'); 215 const types = Lightbox.allowedMediaTypes.join('|'); 216 const slidesHtml = this.sources 217 .map((src, i) => { 218 src = src.replace(/\/$/, ''); 219 const regex = new RegExp(`^(${types})`, 'i'); 220 const isHtml = /^html/.test(src); 221 const isForcedImage = /^image/.test(src); 222 223 if (regex.test(src)) { 224 src = src.replace(regex, ''); 225 } 226 const imgClasses = this.settings.constrain ? 'mw-100 mh-100 h-auto w-auto m-auto top-0 end-0 bottom-0 start-0' : 'h-100 w-100'; 227 const params = new URLSearchParams(src.split('?')[1]); 228 let caption = ''; 229 let url = src; 230 if (params.get('caption')) { 231 try { 232 url = new URL(src); 233 url.searchParams.delete('caption'); 234 url = url.toString(); 235 } catch (e) { 236 url = src; 237 } 238 caption = `<div style="background-color:#fff;" class="lightbox-caption m-0 p-2 text-center small"><em>${params.get('caption')}</em></div>`; 239 } 240 let inner = `<img src="${url}" class="d-block ${imgClasses} img-fluid" style="z-index: 1; object-fit: contain;" />`; 241 let attributes = ''; 242 const instagramEmbed = this.getInstagramEmbed(src); 243 const youtubeLink = this.getYoutubeLink(src); 244 if (this.isEmbed(src) && !isForcedImage) { 245 if (youtubeLink) { 246 src = youtubeLink; 247 attributes = 'title="YouTube video player" frameborder="0" allow="accelerometer autoplay clipboard-write encrypted-media gyroscope picture-in-picture"'; 248 } 249 inner = instagramEmbed || `<iframe src="${src}" ${attributes} allowfullscreen></iframe>`; 250 } 251 if (isHtml) { 252 inner = src; 253 } 254 const spinner = `<div class="position-absolute top-50 start-50 translate-middle text-white"><div class="spinner-border" style="width: 3rem height: 3rem" role="status"></div></div>`; 255 return ` 256 <div class="carousel-item ${!i ? 'active' : ''}" style="min-height: 100px"> 257 ${spinner} 258 <div class="ratio ratio-16x9 " style="background-color: #fff;">${inner}</div> 259 ${caption} 260 </div>`; 261 }) 262 .join(''); 263 const controlsHtml = 264 this.sources.length < 2 265 ? '' 266 : ` 267 <button id="#lightboxCarousel-${this.hash}-prev" class="carousel-control carousel-control-prev h-75 m-auto" type="button" data-bs-target="#lightboxCarousel-${this.hash}" data-bs-slide="prev">
268 <span class="carousel-control-prev-icon p-4 rounded-4 bg-black" aria-hidden="true"></span> 269 <span class="visually-hidden">Previous</span> 270 </button> 271 <button id="#lightboxCarousel-${this.hash}-next" class="carousel-control carousel-control-next h-75 m-auto" type="button" data-bs-target="#lightboxCarousel-${this.hash}" data-bs-slide="next"> 272 <span class="carousel-control-next-icon p-4 rounded-4 bg-black" aria-hidden="true"></span> 273 <span class="visually-hidden">Next</span> 274 </button>`; 275 let classes = 'lightbox-carousel carousel slide'; 276 if (this.settings.size === 'fullscreen') { 277 classes += ' position-absolute w-100 translate-middle top-50 start-50'; 278 } 279 const html = ` 280 <div id="lightboxCarousel-${this.hash}" class="${classes}" data-bs-ride="carousel" data-bs-interval="${this.carouselOptions.interval}"> 281 <div class="carousel-inner"> 282 ${slidesHtml} 283 </div> 284 ${controlsHtml} 285 </div>`; 286 template.innerHTML = html.trim(); 287 this.carouselElement = template.content.firstChild; 288 const carouselOptions = Object.assign(Object.assign({}, this.carouselOptions), { keyboard: false }); 289 this.carousel = new bootstrap.Carousel(this.carouselElement, carouselOptions); 290 const elSrc = this.type && this.type !== 'image' ? this.type + this.src : this.src; 291 this.carousel.to(this.findGalleryItemIndex(this.sources, elSrc)); 292 if (this.carouselOptions.keyboard === true) { 293 document.addEventListener('keydown', (e) => { 294 if (e.code === 'ArrowLeft') { 295 const prev = document.getElementById(`#lightboxCarousel-${this.hash}-prev`); 296 if (prev) { 297 prev.click(); 298 } 299 return false; 300 } 301 if (e.code === 'ArrowRight') { 302 const next = document.getElementById(`#lightboxCarousel-${this.hash}-next`); 303 if (next) { 304 next.click(); 305 } 306 return false; 307 } 308 }); 309 } 310 return this.carousel; 311 } 312 findGalleryItemIndex(haystack, needle) { 313 let index = 0; 314 for (const item of haystack) { 315 if (item.includes(needle)) { 316 return index; 317 } 318 index++; 319 } 320 return 0; 321 } 322 createModal() { 323 324 const template = document.createElement('template'); 325 const btnInner = 326 '<svg xmlns="http://www.w3.org/2000/svg" style="position: relative; top: -5px;" viewBox="0 0 16 16" fill="#000"><path d="M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z"/></svg>'; 327 const html = ` 328 <div class="modal lightbox fade" id="lightboxModal-${this.hash}" tabindex="-1" aria-hidden="true"> 329 <div class="modal-dialog modal-dialog-centered modal-${this.settings.size}"> 330 <div class="modal-content border-0 bg-white"> 331 <div class="modal-body p-2"> 332 <button type="button" class="btn-close position-absolute top-0 end-0 p-3" data-bs-dismiss="modal" aria-label="Close" style="z-index: 2; background: none;">${btnInner}</button> 333 </div> 334 </div> 335 </div> 336 </div>`; 337 template.innerHTML = html.trim(); 338 this.modalElement = template.content.firstChild; 339 this.modalElement.querySelector('.modal-body').appendChild(this.carouselElement); 340 this.modalElement.addEventListener('hidden.bs.modal', () => this.modalElement.remove()); 341 this.modalElement.querySelector('[data-bs-dismiss]').addEventListener('click', () => this.modal.hide()); 342 this.modal = new bootstrap.Modal(this.modalElement, this.modalOptions); 343 return this.modal; 344 } 345 randomHash(length = 8) { 346 return Array.from({ length }, () => Math.floor(Math.random() * 36).toString(36)).join(''); 347 } 348 } 349 Lightbox.allowedEmbedTypes = ['embed', 'youtube', 'vimeo', 'instagram', 'url']; 350 Lightbox.allowedMediaTypes = [...Lightbox.allowedEmbedTypes, 'image', 'html']; 351 Lightbox.defaultSelector = '[data-toggle="lightbox"]'; 352 Lightbox.initialize = function (e) { 353 e.preventDefault(); 354 const lightbox = new Lightbox(this); 355 lightbox.show(); 356 }; 357 document.querySelectorAll(Lightbox.defaultSelector).forEach((el) => el.addEventListener('click', Lightbox.initialize)); 358 if (typeof window !== 'undefined' && window.bootstrap) { 359 window.bootstrap.Lightbox = Lightbox; 360 } 361/* harmony default export */ __webpack_exports__["default"] = (Lightbox); 362 363 __webpack_exports__ = __webpack_exports__["default"]; 364/******/ return __webpack_exports__; 365 /******/ 366})() 367 ; 368}); 369//# sourceMappingURL=index.js.map
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.