vendor: 4,970 bytes, lines 1-129
1/*! 2 * Lightbox v2.10.0 3 * by Lokesh Dhakar 4 * 5 * More info: 6 * http://lokeshdhakar.com/projects/lightbox2/ 7 * 8 * Copyright 2007, 2018 Lokesh Dhakar 9 * Released under the MIT license 10 * https://github.com/lokesh/lightbox2/blob/master/LICENSE 11 * 12 * @preserve 13 */ 14 15// Uses Node, AMD or browser globals to create a module. 16(function (root, factory) { 17 if (typeof define === 'function' && define.amd) { 18 // AMD. Register as an anonymous module. 19 define(['jquery'], factory); 20 } else if (typeof exports === 'object') { 21 // Node. Does not work with strict CommonJS, but 22 // only CommonJS-like environments that support module.exports, 23 // like Node. 24 module.exports = factory(require('jquery')); 25 } else { 26 // Browser globals (root is window) 27 root.lightbox = factory(root.jQuery); 28 } 29}(this, function ($) { 30 31 function Lightbox(options) { 32 this.album = []; 33 this.currentImageIndex = void 0; 34 this.init(); 35 36 // options 37 this.options = $.extend({}, this.constructor.defaults); 38 this.option(options); 39 } 40 41 // Descriptions of all options available on the demo site: 42 // http://lokeshdhakar.com/projects/lightbox2/index.html#options 43 Lightbox.defaults = { 44 albumLabel: 'Image %1 of %2', 45 alwaysShowNavOnTouchDevices: false, 46 fadeDuration: 600, 47 fitImagesInViewport: true, 48 imageFadeDuration: 600, 49 // maxWidth: 800, 50 // maxHeight: 600, 51 positionFromTop: 50, 52 resizeDuration: 700, 53 showImageNumberLabel: true, 54 wrapAround: false, 55 disableScrolling: false, 56 /* 57 Sanitize Title 58 If the caption data is trusted, for example you are hardcoding it in, then leave this to false. 59 This will free you to add html tags, such as links, in the caption. 60 If the caption data is user submitted or from some other untrusted source, then set this to true 61 to prevent xss and other injection attacks. 62 */ 63 sanitizeTitle: false 64 }; 65 66 Lightbox.prototype.option = function(options) { 67 $.extend(this.options, options); 68 }; 69 70 Lightbox.prototype.imageCountLabel = function(currentImageNum, totalImages) { 71 return this.options.albumLabel.replace(/%1/g, currentImageNum).replace(/%2/g, totalImages); 72 }; 73 74 Lightbox.prototype.init = function() { 75 var self = this; 76 // Both enable and build methods require the body tag to be in the DOM. 77 $(document).ready(function() { 78 self.enable(); 79 self.build(); 80 }); 81 }; 82 83 // Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes 84 // that contain 'lightbox'. When these are clicked, start lightbox. 85 Lightbox.prototype.enable = function() { 86 var self = this; 87 $('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) { 88 self.start($(event.currentTarget)); 89 return false; 90 }); 91 }; 92 93 // Build html for the lightbox and the overlay. 94 // Attach event handlers to the new DOM elements. click click click 95 Lightbox.prototype.build = function() { 96 if ($('#lightbox').length > 0) { 97 return; 98 } 99 100 var self = this; 101 $('<div id="lightboxOverlay" class="lightboxOverlay"></div><div id="lightbox" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" /><div class="lb-nav"><a class="lb-prev" href="" ></a><a class="lb-next" href="" ></a></div><div class="lb-loader"><a class="lb-cancel"></a></div></div></div><div class="lb-dataContainer"><div class="lb-data"><div class="lb-details"><span class="lb-caption"></span><span class="lb-number"></span></div><div class="lb-closeContainer"><a class="lb-close"></a></div></div></div></div>').appendTo($('body')); 102 103 // Cache jQuery objects 104 this.$lightbox = $('#lightbox'); 105 this.$overlay = $('#lightboxOverlay'); 106 this.$outerContainer = this.$lightbox.find('.lb-outerContainer'); 107 this.$container = this.$lightbox.find('.lb-container'); 108 this.$image = this.$lightbox.find('.lb-image'); 109 this.$nav = this.$lightbox.find('.lb-nav'); 110 111 // Store css values for future lookup 112 this.containerPadding = { 113 top: parseInt(this.$container.css('padding-top'), 10), 114 right: parseInt(this.$container.css('padding-right'), 10), 115 bottom: parseInt(this.$container.css('padding-bottom'), 10), 116 left: parseInt(this.$container.css('padding-left'), 10) 117 }; 118 119 this.imageBorderWidth = { 120 top: parseInt(this.$image.css('border-top-width'), 10), 121 right: parseInt(this.$image.css('border-right-width'), 10), 122 bottom: parseInt(this.$image.css('border-bottom-width'), 10), 123 left: parseInt(this.$image.css('border-left-width'), 10) 124 }; 125 126 // Attach event handlers to the newly minted DOM elements 127 this.$overlay.hide().on('click', function() { 128 self.end(); 129 return false;
130 }); 131 132 this.$lightbox.hide().on('click', function(event) { 133 if ($(event.target).attr('id') === 'lightbox') { 134 self.end(); 135 } 136 return false; 137 }); 138 139 this.$outerContainer.on('click', function(event) { 140 if ($(event.target).attr('id') === 'lightbox') { 141 self.end(); 142 } 143 return false; 144 }); 145 146 this.$lightbox.find('.lb-prev').on('click', function() { 147 if (self.currentImageIndex === 0) { 148 self.changeImage(self.album.length - 1); 149 } else { 150 self.changeImage(self.currentImageIndex - 1); 151 } 152 return false; 153 }); 154 155 this.$lightbox.find('.lb-next').on('click', function() { 156 if (self.currentImageIndex === self.album.length - 1) { 157 self.changeImage(0); 158 } else { 159 self.changeImage(self.currentImageIndex + 1); 160 } 161 return false; 162 }); 163 164 /* 165 Show context menu for image on right-click 166 There is a div containing the navigation that spans the entire image and lives above of it. If 167 you right-click, you are right clicking this div and not the image. This prevents users from 168 saving the image or using other context menu actions with the image. 169 To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we 170 set pointer-events to none on the nav div. This is so that the upcoming right-click event on 171 the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs 172 we set the pointer events back to auto for the nav div so it can capture hover and left-click 173 events as usual. 174 */ 175 this.$nav.on('mousedown', function(event) { 176 if (event.which === 3) { 177 self.$nav.css('pointer-events', 'none'); 178 179 self.$lightbox.one('contextmenu', function() { 180 setTimeout(function() { 181 this.$nav.css('pointer-events', 'auto'); 182 }.bind(self), 0); 183 }); 184 } 185 }); 186 187 188 this.$lightbox.find('.lb-loader, .lb-close').on('click', function() { 189 self.end(); 190 return false; 191 }); 192 }; 193 194 // Show overlay and lightbox. If the image is part of a set, add siblings to album array. 195 Lightbox.prototype.start = function($link) { 196 var self = this; 197 var $window = $(window); 198 199 $window.on('resize', $.proxy(this.sizeOverlay, this)); 200 201 $('select, object, embed').css({ 202 visibility: 'hidden' 203 }); 204 205 this.sizeOverlay(); 206 207 this.album = []; 208 var imageNumber = 0; 209 210 function addToAlbum($link) { 211 self.album.push({ 212 alt: $link.attr('data-alt'), 213 link: $link.attr('href'), 214 title: $link.attr('data-title') || $link.attr('title') 215 }); 216 } 217 218 // Support both data-lightbox attribute and rel attribute implementations 219 var dataLightboxValue = $link.attr('data-lightbox'); 220 var $links; 221 222 if (dataLightboxValue) { 223 $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]'); 224 for (var i = 0; i < $links.length; i = ++i) { 225 addToAlbum($($links[i])); 226 if ($links[i] === $link[0]) { 227 imageNumber = i; 228 } 229 } 230 } else { 231 if ($link.attr('rel') === 'lightbox') { 232 // If image is not part of a set 233 addToAlbum($link); 234 } else { 235 // If image is part of a set 236 $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]'); 237 for (var j = 0; j < $links.length; j = ++j) { 238 addToAlbum($($links[j])); 239 if ($links[j] === $link[0]) { 240 imageNumber = j; 241 } 242 } 243 } 244 } 245 246 // Position Lightbox 247 var top = $window.scrollTop() + this.options.positionFromTop; 248 var left = $window.scrollLeft(); 249 this.$lightbox.css({ 250 top: top + 'px', 251 left: left + 'px' 252 }).fadeIn(this.options.fadeDuration); 253 254 // Disable scrolling of the page while open 255 if (this.options.disableScrolling) { 256 $('html').addClass('lb-disable-scrolling'); 257 } 258 259 this.changeImage(imageNumber); 260 }; 261 262 // Hide most UI elements in preparation for the animated resizing of the lightbox. 263 Lightbox.prototype.changeImage = function(imageNumber) { 264 var self = this; 265 266 this.disableKeyboardNav(); 267 var $image = this.$lightbox.find('.lb-image'); 268 269 this.$overlay.fadeIn(this.options.fadeDuration); 270 271 $('.lb-loader').fadeIn('slow'); 272 this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide(); 273 274 this.$outerContainer.addClass('animating'); 275 276 // When image to show is preloaded, we send the width and height to sizeContainer() 277 var preloader = new Image(); 278 preloader.onload = function() { 279 var $preloader; 280 var imageHeight; 281 var imageWidth; 282 var maxImageHeight; 283 var maxImageWidth; 284 var windowHeight; 285 var windowWidth; 286 287 $image.attr({ 288 'alt': self.album[imageNumber].alt, 289 'src': self.album[imageNumber].link 290 }); 291 292 $preloader = $(preloader); 293 294 $image.width(preloader.width); 295 $image.height(preloader.height); 296 297 if (self.options.fitImagesInViewport) { 298 // Fit image inside the viewport. 299 // Take into account the border around the image and an additional 10px gutter on each side. 300 301 windowWidth = $(window).width(); 302 windowHeight = $(window).height(); 303 maxImageWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20; 304 maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - 120; 305 306 // Check if image size is larger then maxWidth|maxHeight in settings 307 if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) { 308 maxImageWidth = self.options.maxWidth; 309 } 310 if (self.options.maxHeight && self.options.maxHeight < maxImageWidth) { 311 maxImageHeight = self.options.maxHeight; 312 } 313 314 // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight 315 // option than we need to size down while maintaining the aspect ratio. 316 if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { 317 if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { 318 imageWidth = maxImageWidth; 319 imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); 320 $image.width(imageWidth); 321 $image.height(imageHeight); 322 } else { 323 imageHeight = maxImageHeight; 324 imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); 325 $image.width(imageWidth); 326 $image.height(imageHeight); 327 } 328 } 329 } 330 self.sizeContainer($image.width(), $image.height()); 331 }; 332 333 preloader.src = this.album[imageNumber].link; 334 this.currentImageIndex = imageNumber; 335 }; 336 337 // Stretch overlay to fit the viewport 338 Lightbox.prototype.sizeOverlay = function() { 339 this.$overlay 340 .width($(document).width()) 341 .height($(document).height()); 342 }; 343 344 // Animate the size of the lightbox to fit the image we are showing 345 Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { 346 var self = this; 347 348 var oldWidth = this.$outerContainer.outerWidth(); 349 var oldHeight = this.$outerContainer.outerHeight(); 350 var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; 351 var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; 352 353 function postResize() { 354 self.$lightbox.find('.lb-dataContainer').width(newWidth); 355 self.$lightbox.find('.lb-prevLink').height(newHeight); 356 self.$lightbox.find('.lb-nextLink').height(newHeight); 357 self.showImage(); 358 } 359 360 if (oldWidth !== newWidth || oldHeight !== newHeight) { 361 this.$outerContainer.animate({ 362 width: newWidth, 363 height: newHeight 364 }, this.options.resizeDuration, 'swing', function() { 365 postResize(); 366 }); 367 } else { 368 postResize(); 369 } 370 }; 371 372 // Display the image and its details and begin preload neighboring images. 373 Lightbox.prototype.showImage = function() { 374 this.$lightbox.find('.lb-loader').stop(true).hide(); 375 this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration); 376 377 this.updateNav(); 378 this.updateDetails(); 379 this.preloadNeighboringImages(); 380 this.enableKeyboardNav(); 381 }; 382 383 // Display previous and next navigation if appropriate. 384 Lightbox.prototype.updateNav = function() { 385 // Check to see if the browser supports touch events. If so, we take the conservative approach 386 // and assume that mouse hover events are not supported and always show prev/next navigation 387 // arrows in image sets. 388 var alwaysShowNav = false;
389 try { 390 document.createEvent('TouchEvent'); 391 alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false; 392 } catch (e) {} 393 394 this.$lightbox.find('.lb-nav').show(); 395 396 if (this.album.length > 1) { 397 if (this.options.wrapAround) { 398 if (alwaysShowNav) { 399 this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); 400 } 401 this.$lightbox.find('.lb-prev, .lb-next').show(); 402 } else { 403 if (this.currentImageIndex > 0) { 404 this.$lightbox.find('.lb-prev').show(); 405 if (alwaysShowNav) { 406 this.$lightbox.find('.lb-prev').css('opacity', '1'); 407 } 408 } 409 if (this.currentImageIndex < this.album.length - 1) { 410 this.$lightbox.find('.lb-next').show(); 411 if (alwaysShowNav) { 412 this.$lightbox.find('.lb-next').css('opacity', '1'); 413 } 414 } 415 } 416 } 417 }; 418 419 // Display caption, image number, and closing button. 420 Lightbox.prototype.updateDetails = function() { 421 var self = this; 422 423 // Enable anchor clicks in the injected caption html. 424 // Thanks Nate Wright for the fix. @https://github.com/NateWr 425 if (typeof this.album[this.currentImageIndex].title !== 'undefined' && 426 this.album[this.currentImageIndex].title !== '') { 427 var $caption = this.$lightbox.find('.lb-caption'); 428 if (this.options.sanitizeTitle) { 429 $caption.text(this.album[this.currentImageIndex].title); 430 } else { 431 $caption.html(this.album[this.currentImageIndex].title); 432 } 433 $caption.fadeIn('fast') 434 .find('a').on('click', function(event) { 435 if ($(this).attr('target') !== undefined) { 436 window.open($(this).attr('href'), $(this).attr('target')); 437 } else { 438 location.href = $(this).attr('href'); 439 } 440 }); 441 } 442 443 if (this.album.length > 1 && this.options.showImageNumberLabel) { 444 var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length); 445 this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast'); 446 } else { 447 this.$lightbox.find('.lb-number').hide(); 448 } 449 450 this.$outerContainer.removeClass('animating'); 451 452 this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { 453 return self.sizeOverlay(); 454 }); 455 }; 456 457 // Preload previous and next images in set. 458 Lightbox.prototype.preloadNeighboringImages = function() { 459 if (this.album.length > this.currentImageIndex + 1) { 460 var preloadNext = new Image(); 461 preloadNext.src = this.album[this.currentImageIndex + 1].link; 462 } 463 if (this.currentImageIndex > 0) { 464 var preloadPrev = new Image(); 465 preloadPrev.src = this.album[this.currentImageIndex - 1].link; 466 } 467 }; 468 469 Lightbox.prototype.enableKeyboardNav = function() { 470 $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this)); 471 }; 472 473 Lightbox.prototype.disableKeyboardNav = function() { 474 $(document).off('.keyboard'); 475 }; 476 477 Lightbox.prototype.keyboardAction = function(event) { 478 var KEYCODE_ESC = 27; 479 var KEYCODE_LEFTARROW = 37; 480 var KEYCODE_RIGHTARROW = 39; 481 482 var keycode = event.keyCode; 483 var key = String.fromCharCode(keycode).toLowerCase(); 484 if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) { 485 this.end(); 486 } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) { 487 if (this.currentImageIndex !== 0) { 488 this.changeImage(this.currentImageIndex - 1); 489 } else if (this.options.wrapAround && this.album.length > 1) { 490 this.changeImage(this.album.length - 1); 491 } 492 } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) { 493 if (this.currentImageIndex !== this.album.length - 1) { 494 this.changeImage(this.currentImageIndex + 1); 495 } else if (this.options.wrapAround && this.album.length > 1) { 496 this.changeImage(0); 497 } 498 } 499 }; 500 501 // Closing time. :-( 502 Lightbox.prototype.end = function() { 503 this.disableKeyboardNav(); 504 $(window).off('resize', this.sizeOverlay); 505 this.$lightbox.fadeOut(this.options.fadeDuration); 506 this.$overlay.fadeOut(this.options.fadeDuration); 507 $('select, object, embed').css({ 508 visibility: 'visible' 509 }); 510 if (this.options.disableScrolling) { 511 $('html').removeClass('lb-disable-scrolling'); 512 } 513 }; 514 515 return new Lightbox(); 516}));
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.