vendor: 5,018 bytes, lines 1-130
1/*! 2 * Lightbox v2.11.0 3 * by Lokesh Dhakar 4 * 5 * More info: 6 * http://lokeshdhakar.com/projects/lightbox2/ 7 * 8 * Copyright 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 61 If the caption data is user submitted or from some other untrusted source, then set this to true 62 to prevent xss and other injection attacks. 63 */ 64 sanitizeTitle: false 65 }; 66 67 Lightbox.prototype.option = function(options) { 68 $.extend(this.options, options); 69 }; 70 71 Lightbox.prototype.imageCountLabel = function(currentImageNum, totalImages) { 72 return this.options.albumLabel.replace(/%1/g, currentImageNum).replace(/%2/g, totalImages); 73 }; 74 75 Lightbox.prototype.init = function() { 76 var self = this; 77 // Both enable and build methods require the body tag to be in the DOM. 78 $(document).ready(function() { 79 self.enable(); 80 self.build(); 81 }); 82 }; 83 84 // Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes 85 // that contain 'lightbox'. When these are clicked, start lightbox. 86 Lightbox.prototype.enable = function() { 87 var self = this; 88 $('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) { 89 self.start($(event.currentTarget)); 90 return false; 91 }); 92 }; 93 94 // Build html for the lightbox and the overlay. 95 // Attach event handlers to the new DOM elements. click click click 96 Lightbox.prototype.build = function() { 97 if ($('#lightbox').length > 0) { 98 return; 99 } 100 101 var self = this; 102 $('<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==" alt=""/><div class="lb-nav"><a class="lb-prev" aria-label="Previous image" href="" ></a><a class="lb-next" aria-label="Next image" 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')); 103 104 // Cache jQuery objects 105 this.$lightbox = $('#lightbox'); 106 this.$overlay = $('#lightboxOverlay'); 107 this.$outerContainer = this.$lightbox.find('.lb-outerContainer'); 108 this.$container = this.$lightbox.find('.lb-container'); 109 this.$image = this.$lightbox.find('.lb-image'); 110 this.$nav = this.$lightbox.find('.lb-nav'); 111 112 // Store css values for future lookup 113 this.containerPadding = { 114 top: parseInt(this.$container.css('padding-top'), 10), 115 right: parseInt(this.$container.css('padding-right'), 10), 116 bottom: parseInt(this.$container.css('padding-bottom'), 10), 117 left: parseInt(this.$container.css('padding-left'), 10) 118 }; 119 120 this.imageBorderWidth = { 121 top: parseInt(this.$image.css('border-top-width'), 10), 122 right: parseInt(this.$image.css('border-right-width'), 10), 123 bottom: parseInt(this.$image.css('border-bottom-width'), 10), 124 left: parseInt(this.$image.css('border-left-width'), 10) 125 }; 126 127 // Attach event handlers to the newly minted DOM elements 128 this.$overlay.hide().on('click', function() { 129 self.end(); 130 return false;
131 }); 132 133 this.$lightbox.hide().on('click', function(event) { 134 if ($(event.target).attr('id') === 'lightbox') { 135 self.end(); 136 } 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 167 There is a div containing the navigation that spans the entire image and lives above of it. If 168 you right-click, you are right clicking this div and not the image. This prevents users from 169 saving the image or using other context menu actions with the image. 170 171 To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we 172 set pointer-events to none on the nav div. This is so that the upcoming right-click event on 173 the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs 174 we set the pointer events back to auto for the nav div so it can capture hover and left-click 175 events as usual. 176 */ 177 this.$nav.on('mousedown', function(event) { 178 if (event.which === 3) { 179 self.$nav.css('pointer-events', 'none'); 180 181 self.$lightbox.one('contextmenu', function() { 182 setTimeout(function() { 183 this.$nav.css('pointer-events', 'auto'); 184 }.bind(self), 0); 185 }); 186 } 187 }); 188 189 190 this.$lightbox.find('.lb-loader, .lb-close').on('click', function() { 191 self.end(); 192 return false; 193 }); 194 }; 195 196 // Show overlay and lightbox. If the image is part of a set, add siblings to album array. 197 Lightbox.prototype.start = function($link) { 198 var self = this; 199 var $window = $(window); 200 201 $window.on('resize', $.proxy(this.sizeOverlay, this)); 202 203 this.sizeOverlay(); 204 205 this.album = []; 206 var imageNumber = 0; 207 208 function addToAlbum($link) { 209 self.album.push({ 210 alt: $link.attr('data-alt'), 211 link: $link.attr('href'), 212 title: $link.attr('data-title') || $link.attr('title') 213 }); 214 } 215 216 // Support both data-lightbox attribute and rel attribute implementations 217 var dataLightboxValue = $link.attr('data-lightbox'); 218 var $links; 219 220 if (dataLightboxValue) { 221 $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]'); 222 for (var i = 0; i < $links.length; i = ++i) { 223 addToAlbum($($links[i])); 224 if ($links[i] === $link[0]) { 225 imageNumber = i; 226 } 227 } 228 } else { 229 if ($link.attr('rel') === 'lightbox') { 230 // If image is not part of a set 231 addToAlbum($link); 232 } else { 233 // If image is part of a set 234 $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]'); 235 for (var j = 0; j < $links.length; j = ++j) { 236 addToAlbum($($links[j])); 237 if ($links[j] === $link[0]) { 238 imageNumber = j; 239 } 240 } 241 } 242 } 243 244 // Position Lightbox 245 var top = $window.scrollTop() + this.options.positionFromTop; 246 var left = $window.scrollLeft(); 247 this.$lightbox.css({ 248 top: top + 'px', 249 left: left + 'px' 250 }).fadeIn(this.options.fadeDuration); 251 252 // Disable scrolling of the page while open 253 if (this.options.disableScrolling) { 254 $('body').addClass('lb-disable-scrolling'); 255 } 256 257 this.changeImage(imageNumber); 258 }; 259 260 // Hide most UI elements in preparation for the animated resizing of the lightbox. 261 Lightbox.prototype.changeImage = function(imageNumber) { 262 var self = this; 263 var filename = this.album[imageNumber].link; 264 var filetype = filename.split('.').slice(-1)[0]; 265 var $image = this.$lightbox.find('.lb-image'); 266 267 // Disable keyboard nav during transitions 268 this.disableKeyboardNav(); 269 270 // Show loading state 271 this.$overlay.fadeIn(this.options.fadeDuration); 272 $('.lb-loader').fadeIn('slow'); 273 this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide(); 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': filename 290 }); 291 292 $preloader = $(preloader); 293 294 $image.width(preloader.width); 295 $image.height(preloader.height); 296 windowWidth = $(window).width(); 297 windowHeight = $(window).height(); 298 299 // Calculate the max image dimensions for the current viewport. 300 // Take into account the border around the image and an additional 10px gutter on each side. 301 maxImageWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20; 302 maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - self.options.positionFromTop - 70; 303 304 /* 305 SVGs that don't have width and height attributes specified are reporting width and height 306 values of 0 in Firefox 47 and IE11 on Windows. To fix, we set the width and height to the max 307 dimensions for the viewport rather than 0 x 0. 308 309 https://github.com/lokesh/lightbox2/issues/552 310 */ 311 312 if (filetype === 'svg') { 313 if ((preloader.width === 0) || preloader.height === 0) { 314 $image.width(maxImageWidth); 315 $image.height(maxImageHeight); 316 } 317 } 318 319 // Fit image inside the viewport. 320 if (self.options.fitImagesInViewport) { 321 322 // Check if image size is larger then maxWidth|maxHeight in settings 323 if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) { 324 maxImageWidth = self.options.maxWidth; 325 } 326 if (self.options.maxHeight && self.options.maxHeight < maxImageWidth) { 327 maxImageHeight = self.options.maxHeight; 328 } 329 330 // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight 331 // option than we need to size down while maintaining the aspect ratio. 332 if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { 333 if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { 334 imageWidth = maxImageWidth; 335 imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); 336 $image.width(imageWidth); 337 $image.height(imageHeight); 338 } else { 339 imageHeight = maxImageHeight; 340 imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); 341 $image.width(imageWidth); 342 $image.height(imageHeight); 343 } 344 } 345 } 346 self.sizeContainer($image.width(), $image.height()); 347 }; 348 349 // Preload image before showing 350 preloader.src = this.album[imageNumber].link; 351 this.currentImageIndex = imageNumber; 352 }; 353 354 // Stretch overlay to fit the viewport 355 Lightbox.prototype.sizeOverlay = function() { 356 var self = this; 357 /* 358 We use a setTimeout 0 to pause JS execution and let the rendering catch-up. 359 Why do this? If the `disableScrolling` option is set to true, a class is added to the body 360 tag that disables scrolling and hides the scrollbar. We want to make sure the scrollbar is 361 hidden before we measure the document width, as the presence of the scrollbar will affect the 362 number. 363 */ 364 setTimeout(function() { 365 self.$overlay 366 .width($(document).width()) 367 .height($(document).height()); 368 369 }, 0); 370 }; 371 372 // Animate the size of the lightbox to fit the image we are showing 373 // This method also shows the the image. 374 Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { 375 var self = this; 376 377 var oldWidth = this.$outerContainer.outerWidth(); 378 var oldHeight = this.$outerContainer.outerHeight(); 379 var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; 380 var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; 381 382 function postResize() { 383 self.$lightbox.find('.lb-dataContainer').width(newWidth); 384 self.$lightbox.find('.lb-prevLink').height(newHeight); 385 self.$lightbox.find('.lb-nextLink').height(newHeight); 386 self.showImage(); 387 } 388 389 if (oldWidth !== newWidth || oldHeight !== newHeight) { 390 this.$outerContainer.animate({ 391 width: newWidth, 392 height: newHeight 393 }, this.options.resizeDuration, 'swing', function() { 394 postResize(); 395 }); 396 } else { 397 postResize(); 398 } 399 }; 400 401 // Display the image and its details and begin preload neighboring images. 402 Lightbox.prototype.showImage = function() { 403 this.$lightbox.find('.lb-loader').stop(true).hide(); 404 this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration); 405 406 this.updateNav(); 407 this.updateDetails(); 408 this.preloadNeighboringImages(); 409 this.enableKeyboardNav(); 410 }; 411 412 // Display previous and next navigation if appropriate. 413 Lightbox.prototype.updateNav = function() { 414 // Check to see if the browser supports touch events. If so, we take the conservative approach 415 // and assume that mouse hover events are not supported and always show prev/next navigation 416 // arrows in image sets. 417 var alwaysShowNav = false;
418 try { 419 document.createEvent('TouchEvent'); 420 alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false; 421 } catch (e) {} 422 423 this.$lightbox.find('.lb-nav').show(); 424 425 if (this.album.length > 1) { 426 if (this.options.wrapAround) { 427 if (alwaysShowNav) { 428 this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); 429 } 430 this.$lightbox.find('.lb-prev, .lb-next').show(); 431 } else { 432 if (this.currentImageIndex > 0) { 433 this.$lightbox.find('.lb-prev').show(); 434 if (alwaysShowNav) { 435 this.$lightbox.find('.lb-prev').css('opacity', '1'); 436 } 437 } 438 if (this.currentImageIndex < this.album.length - 1) { 439 this.$lightbox.find('.lb-next').show(); 440 if (alwaysShowNav) { 441 this.$lightbox.find('.lb-next').css('opacity', '1'); 442 } 443 } 444 } 445 } 446 }; 447 448 // Display caption, image number, and closing button. 449 Lightbox.prototype.updateDetails = function() { 450 var self = this; 451 452 // Enable anchor clicks in the injected caption html. 453 // Thanks Nate Wright for the fix. @https://github.com/NateWr 454 if (typeof this.album[this.currentImageIndex].title !== 'undefined' && 455 this.album[this.currentImageIndex].title !== '') { 456 var $caption = this.$lightbox.find('.lb-caption'); 457 if (this.options.sanitizeTitle) { 458 $caption.text(this.album[this.currentImageIndex].title); 459 } else { 460 $caption.html(this.album[this.currentImageIndex].title); 461 } 462 $caption.fadeIn('fast'); 463 } 464 465 if (this.album.length > 1 && this.options.showImageNumberLabel) { 466 var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length); 467 this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast'); 468 } else { 469 this.$lightbox.find('.lb-number').hide(); 470 } 471 472 this.$outerContainer.removeClass('animating'); 473 474 this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { 475 return self.sizeOverlay(); 476 }); 477 }; 478 479 // Preload previous and next images in set. 480 Lightbox.prototype.preloadNeighboringImages = function() { 481 if (this.album.length > this.currentImageIndex + 1) { 482 var preloadNext = new Image(); 483 preloadNext.src = this.album[this.currentImageIndex + 1].link; 484 } 485 if (this.currentImageIndex > 0) { 486 var preloadPrev = new Image(); 487 preloadPrev.src = this.album[this.currentImageIndex - 1].link; 488 } 489 }; 490 491 Lightbox.prototype.enableKeyboardNav = function() { 492 $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this)); 493 }; 494 495 Lightbox.prototype.disableKeyboardNav = function() { 496 $(document).off('.keyboard'); 497 }; 498 499 Lightbox.prototype.keyboardAction = function(event) { 500 var KEYCODE_ESC = 27; 501 var KEYCODE_LEFTARROW = 37; 502 var KEYCODE_RIGHTARROW = 39; 503 504 var keycode = event.keyCode; 505 if (keycode === KEYCODE_ESC) { 506 this.end(); 507 } else if (keycode === KEYCODE_LEFTARROW) { 508 if (this.currentImageIndex !== 0) { 509 this.changeImage(this.currentImageIndex - 1); 510 } else if (this.options.wrapAround && this.album.length > 1) { 511 this.changeImage(this.album.length - 1); 512 } 513 } else if (keycode === KEYCODE_RIGHTARROW) { 514 if (this.currentImageIndex !== this.album.length - 1) { 515 this.changeImage(this.currentImageIndex + 1); 516 } else if (this.options.wrapAround && this.album.length > 1) { 517 this.changeImage(0); 518 } 519 } 520 }; 521 522 // Closing time. :-( 523 Lightbox.prototype.end = function() { 524 this.disableKeyboardNav(); 525 $(window).off('resize', this.sizeOverlay); 526 this.$lightbox.fadeOut(this.options.fadeDuration); 527 this.$overlay.fadeOut(this.options.fadeDuration); 528 529 if (this.options.disableScrolling) { 530 $('body').removeClass('lb-disable-scrolling'); 531 } 532 }; 533 534 return new Lightbox(); 535}));
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.