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