vendor: 7,385 bytes, lines 1-311
1(function (root, factory) { 2 if (typeof define === "function" && define.amd) { 3 define([], factory); 4 } else if (typeof module === "object" && module.exports) { 5 module.exports = factory(); 6 } else { 7 root.SimpleLightbox = factory(); 8 } 9})(this, function () { 10 function assign(target) { 11 for (var i = 1; i < arguments.length; i++) { 12 var obj = arguments[i]; 13 14 if (obj) { 15 for (var key in obj) { 16 obj.hasOwnProperty(key) && (target[key] = obj[key]); 17 } 18 } 19 } 20 21 return target; 22 } 23 24 function addClass(element, className) { 25 if (element && className) { 26 element.className += " " + className; 27 } 28 } 29 30 function removeClass(element, className) { 31 if (element && className) { 32 element.className = element.className 33 .replace(new RegExp("(\\s|^)" + className + "(\\s|$)"), " ") 34 .trim(); 35 } 36 } 37 38 function parseHtml(html) { 39 var div = document.createElement("div"); 40 div.innerHTML = html.trim(); 41 42 return div.childNodes[0]; 43 } 44 45 function matches(el, selector) { 46 return (el.matches || el.matchesSelector || el.msMatchesSelector).call( 47 el, 48 selector 49 ); 50 } 51 52 function getWindowHeight() { 53 return "innerHeight" in window 54 ? window.innerHeight 55 : document.documentElement.offsetHeight; 56 } 57 58 function SimpleLightbox(options) { 59 this.init.apply(this, arguments); 60 } 61 62 SimpleLightbox.defaults = { 63 // add custom classes to lightbox elements 64 elementClass: "", 65 elementLoadingClass: "slbLoading", 66 htmlClass: "slbActive", 67 closeBtnClass: "", 68 nextBtnClass: "", 69 prevBtnClass: "", 70 loadingTextClass: "", 71 72 // customize / localize controls captions 73 closeBtnCaption: "Close", 74 nextBtnCaption: "Next", 75 prevBtnCaption: "Previous", 76 loadingCaption: "Loading...", 77 78 bindToItems: true, // set click event handler to trigger lightbox on provided $items 79 closeOnOverlayClick: true, 80 closeOnEscapeKey: true, 81 nextOnImageClick: true, 82 showCaptions: true, 83 84 captionAttribute: "title", // choose data source for library to glean image caption from 85 urlAttribute: "href", // where to expect large image 86 87 startAt: 0, // start gallery at custom index 88 loadingTimeout: 100, // time after loading element will appear 89 90 appendTarget: "body", // append elsewhere if needed 91 92 beforeSetContent: null, // convenient hooks for extending library behavoiur 93 beforeClose: null, 94 afterClose: null, 95 beforeDestroy: null, 96 afterDestroy: null, 97 98 videoRegex: new RegExp(/youtube.com|vimeo.com/), // regex which tests load url for iframe content 99 }; 100 101 assign(SimpleLightbox.prototype, { 102 init: function (options) { 103 options = this.options = assign({}, SimpleLightbox.defaults, options); 104 105 var self = this; 106 var elements; 107 108 if (options.$items) { 109 elements = options.$items.get(); 110 } 111 112 if (options.elements) { 113 elements = [].slice.call( 114 typeof options.elements === "string" 115 ? document.querySelectorAll(options.elements) 116 : options.elements 117 ); 118 } 119 120 this.eventRegistry = { lightbox: [], thumbnails: [] }; 121 this.items = []; 122 this.captions = []; 123 124 if (elements) { 125 elements.forEach(function (element, index) { 126 self.items.push(element.getAttribute(options.urlAttribute)); 127 self.captions.push(element.getAttribute(options.captionAttribute)); 128 129 if (options.bindToItems) { 130 self.addEvent( 131 element, 132 "click", 133 function (e) { 134 e.preventDefault(); 135 self.showPosition(index); 136 }, 137 "thumbnails" 138 ); 139 } 140 }); 141 } 142 143 if (options.items) { 144 this.items = options.items; 145 } 146 147 if (options.captions) { 148 this.captions = options.captions; 149 } 150 }, 151 152 addEvent: function (element, eventName, callback, scope) { 153 this.eventRegistry[scope || "lightbox"].push({ 154 element: element, 155 eventName: eventName, 156 callback: callback, 157 }); 158 159 element.addEventListener(eventName, callback); 160 161 return this; 162 }, 163 164 removeEvents: function (scope) { 165 this.eventRegistry[scope].forEach(function (item) { 166 item.element.removeEventListener(item.eventName, item.callback); 167 }); 168 169 this.eventRegistry[scope] = []; 170 171 return this; 172 }, 173 174 next: function () { 175 this.showPosition(this.currentPosition + 1); 176 }, 177 178 prev: function () { 179 this.showPosition(this.currentPosition - 1); 180 }, 181 182 normalizePosition: function (position) { 183 if (position >= this.items.length) { 184 position = 0; 185 } else if (position < 0) { 186 position = this.items.length - 1; 187 } 188 189 return position; 190 }, 191 192 showPosition: function (position) { 193 var newPosition = this.normalizePosition(position); 194 195 if (typeof this.currentPosition !== "undefined") { 196 this.direction = newPosition > this.currentPosition ? "next" : "prev"; 197 } 198 199 this.currentPosition = newPosition; 200 201 // Return a promise 202 return new Promise((resolve, reject) => { 203 try { 204 this.setupLightboxHtml() 205 .prepareItem(this.currentPosition, this.setContent) 206 .show(); 207 208 // Use requestAnimationFrame to resolve the promise after the next frame 209 requestAnimationFrame(() => { 210 resolve(); // Resolve promise 211 }); 212 } catch (error) { 213 reject(error); // Reject promise if an error occurs 214 } 215 }); 216 }, 217 218 loading: function (on) { 219 var self = this; 220 var options = this.options; 221 222 if (on) { 223 this.loadingTimeout = setTimeout(function () { 224 addClass(self.$el, options.elementLoadingClass); 225 226 self.$content.innerHTML = 227 '<p class="slbLoadingText ' + 228 options.loadingTextClass + 229 '">' + 230 options.loadingCaption + 231 "</p>"; 232 self.show(); 233 }, options.loadingTimeout); 234 } else { 235 removeClass(this.$el, options.elementLoadingClass); 236 clearTimeout(this.loadingTimeout); 237 } 238 }, 239 240 prepareItem: function (position, callback) { 241 var self = this; 242 var url = this.items[position]; 243 244 this.loading(true); 245 246 if (this.options.videoRegex.test(url)) { 247 callback.call( 248 self, 249 parseHtml( 250 '<div class="slbIframeCont"><iframe class="slbIframe" frameborder="0" allowfullscreen src="' + 251 url + 252 '"></iframe></div>' 253 ) 254 ); 255 } else { 256 var $imageCont = parseHtml( 257 '<div class="slbImageWrap"><img class="slbImage" src="' + 258 url + 259 '" /></div>' 260 ); 261 262 this.$currentImage = $imageCont.querySelector(".slbImage"); 263 264 if (this.options.showCaptions && this.captions[position]) { 265 $imageCont.appendChild( 266 parseHtml( 267 '<div class="slbCaption">' + this.captions[position] + "</div>" 268 ) 269 ); 270 } 271 272 this.loadImage(url, function () { 273 self.setImageDimensions(); 274 275 callback.call(self, $imageCont); 276 277 self.loadImage( 278 self.items[self.normalizePosition(self.currentPosition + 1)] 279 ); 280 }); 281 } 282 283 return this; 284 }, 285 286 loadImage: function (url, callback) { 287 if (!this.options.videoRegex.test(url)) { 288 var image = new Image(); 289 callback && (image.onload = callback); 290 image.src = url; 291 } 292 }, 293 294 setupLightboxHtml: function () { 295 var o = this.options; 296 297 if (!this.$el) { 298 this.$el = parseHtml( 299 '<div class="slbElement ' + 300 o.elementClass + 301 '">' + 302 '<div class="slbOverlay"></div>' + 303 '<div class="slbWrapOuter">' + 304 '<div class="slbWrap">' + 305 '<div class="slbContentOuter">' + 306 '<div class="slbContent"></div>' + 307 '<button type="button" title="' + 308 o.closeBtnCaption + 309 '" class="slbCloseBtn ' + 310 o.closeBtnClass + 311 '">
311Ã</button>' + 312 (this.items.length > 1 313 ? '<div class="slbArrows">' + 314 '<button type="button" title="' + 315 o.prevBtnCaption + 316 '" class="prev slbArrow' + 317 o.prevBtnClass + 318 '">' + 319 o.prevBtnCaption + 320 "</button>" + 321 '<button type="button" title="' + 322 o.nextBtnCaption + 323 '" class="next slbArrow' + 324 o.nextBtnClass + 325 '">' + 326 o.nextBtnCaption + 327 "</button>" + 328 "</div>" 329 : "") + 330 "</div>" + 331 "</div>" + 332 "</div>" + 333 "</div>" 334 ); 335 336 this.$content = this.$el.querySelector(".slbContent"); 337 } 338 339 this.$content.innerHTML = ""; 340 341 return this; 342 }, 343 344 show: function () { 345 if (!this.modalInDom) { 346 document.querySelector(this.options.appendTarget).appendChild(this.$el); 347 addClass(document.documentElement, this.options.htmlClass); 348 this.setupLightboxEvents(); 349 this.modalInDom = true; 350 this.lastFocusedElement = document.activeElement; 351 } 352 353 return this; 354 }, 355 356 setContent: function (content) { 357 var $content = typeof content === "string" ? parseHtml(content) : content; 358 this.loading(false); 359 360 this.setupLightboxHtml(); 361 362 removeClass(this.$content, "slbDirectionNext"); 363 removeClass(this.$content, "slbDirectionPrev"); 364 365 if (this.direction) { 366 addClass( 367 this.$content, 368 this.direction === "next" ? "slbDirectionNext" : "slbDirectionPrev" 369 ); 370 } 371 372 if (this.options.beforeSetContent) { 373 this.options.beforeSetContent($content, this); 374 } 375 376 this.$content.appendChild($content); 377 378 return this; 379 }, 380 381 setImageDimensions: function () { 382 if (this.$currentImage) { 383 this.$currentImage.style.maxHeight = getWindowHeight() + "px"; 384 } 385 }, 386 387 setupLightboxEvents: function () { 388 var self = this; 389 390 if (this.eventRegistry.lightbox.length) { 391 return this; 392 } 393 394 this.addEvent(this.$el, "click", function (e) { 395 var $target = e.target; 396 397 if ( 398 matches($target, ".slbCloseBtn") || 399 (self.options.closeOnOverlayClick && matches($target, ".slbWrap")) 400 ) { 401 self.close(); 402 } else if (matches($target, ".slbArrow")) { 403 matches($target, ".next") ? self.next() : self.prev(); 404 } else if ( 405 self.options.nextOnImageClick && 406 self.items.length > 1 && 407 matches($target, ".slbImage") 408 ) { 409 self.next(); 410 } 411 }) 412 .addEvent(document, "keyup", function (e) { 413 self.options.closeOnEscapeKey && e.keyCode === 27 && self.close(); 414 415 if (self.items.length > 1) { 416 if (e.keyCode === 39 || e.keyCode === 68) { 417 self.next(); 418 } 419 if (e.keyCode === 37 || e.keyCode === 65) { 420 self.prev(); 421 } 422 } 423 }) 424 .addEvent(window, "resize", function () { 425 self.setImageDimensions(); 426 }); 427 428 // Focus trapping logic 429 this.addEvent(document, "keydown", function (e) { 430 var isTabPressed = e.key === "Tab" || e.keyCode === 9; 431 if (!isTabPressed) { 432 return; 433 } 434 var focusableElements = self.$el.querySelectorAll( 435 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])' 436 ); 437 var firstFocusableElement = focusableElements[0]; // First focusable element 438 var lastFocusableElement = 439 focusableElements[focusableElements.length - 1]; // Last focusable element 440 if (![...focusableElements].includes(document.activeElement)) { 441 e.preventDefault(); 442 firstFocusableElement.focus(); // Focus the first element 443 } 444 if (e.shiftKey) { 445 // If Shift + Tab is pressed 446 if (document.activeElement === firstFocusableElement) { 447 e.preventDefault(); 448 lastFocusableElement.focus(); // Focus the last element 449 } 450 } else { 451 // If Tab is pressed 452 if (document.activeElement === lastFocusableElement) { 453 e.preventDefault(); 454 firstFocusableElement.focus(); // Focus the first element 455 } 456 } 457 }); 458 459 return this; 460 }, 461 462 close: function () { 463 if (this.modalInDom) { 464 this.runHook("beforeClose"); 465 this.removeEvents("lightbox"); 466 this.$el && this.$el.parentNode.removeChild(this.$el); 467 removeClass(document.documentElement, this.options.htmlClass); 468 this.modalInDom = false;
469 this.lastFocusedElement && this.lastFocusedElement.focus(); 470 this.lastFocusedElement = null; 471 this.runHook("afterClose"); 472 } 473 474 this.direction = undefined; 475 this.currentPosition = this.options.startAt; 476 }, 477 478 destroy: function () { 479 this.close(); 480 this.runHook("beforeDestroy"); 481 this.removeEvents("thumbnails"); 482 this.runHook("afterDestroy"); 483 }, 484 485 runHook: function (name) { 486 this.options[name] && this.options[name](this); 487 }, 488 }); 489 490 SimpleLightbox.open = function (options) { 491 var instance = new SimpleLightbox(options); 492 493 return options.content 494 ? instance.setContent(options.content).show() 495 : instance.showPosition(instance.options.startAt); 496 }; 497 498 SimpleLightbox.registerAsJqueryPlugin = function ($) { 499 $.fn.simpleLightbox = function (options) { 500 var lightboxInstance; 501 var $items = this; 502 503 return this.each(function () { 504 if (!$.data(this, "simpleLightbox")) { 505 lightboxInstance = 506 lightboxInstance || 507 new SimpleLightbox($.extend({}, options, { $items: $items })); 508 $.data(this, "simpleLightbox", lightboxInstance); 509 } 510 }); 511 }; 512 513 $.SimpleLightbox = SimpleLightbox; 514 }; 515 516 if (typeof window !== "undefined" && window.jQuery) { 517 SimpleLightbox.registerAsJqueryPlugin(window.jQuery); 518 } 519 520 return SimpleLightbox; 521});
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.