vendor: 5,110 bytes, lines 1-131
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 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==" /><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')); 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 return false; 138 }); 139 140 this.$outerContainer.on('click', function(event) { 141 if ($(event.target).attr('id') === 'lightbox') { 142 self.end(); 143 } 144 return false; 145 }); 146 147 this.$lightbox.find('.lb-prev').on('click', function() { 148 if (self.currentImageIndex === 0) { 149 self.changeImage(self.album.length - 1); 150 } else { 151 self.changeImage(self.currentImageIndex - 1); 152 } 153 return false; 154 }); 155 156 this.$lightbox.find('.lb-next').on('click', function() { 157 if (self.currentImageIndex === self.album.length - 1) { 158 self.changeImage(0); 159 } else { 160 self.changeImage(self.currentImageIndex + 1); 161 } 162 return false; 163 }); 164 165 /* 166 Show context menu for image on right-click 167 168 There is a div containing the navigation that spans the entire image and lives above of it. If 169 you right-click, you are right clicking this div and not the image. This prevents users from 170 saving the image or using other context menu actions with the image. 171 172 To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we 173 set pointer-events to none on the nav div. This is so that the upcoming right-click event on 174 the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs 175 we set the pointer events back to auto for the nav div so it can capture hover and left-click 176 events as usual. 177 */ 178 this.$nav.on('mousedown', function(event) { 179 if (event.which === 3) { 180 self.$nav.css('pointer-events', 'none'); 181 182 self.$lightbox.one('contextmenu', function() { 183 setTimeout(function() { 184 this.$nav.css('pointer-events', 'auto'); 185 }.bind(self), 0); 186 }); 187 } 188 }); 189 190 191 //this.$lightbox.find('.lb-loader, .lb-close').on('click', function() { 192 this.$lightbox.find('.lb-nav').on('click', function() { 193 self.end(); 194 return false; 195 }); 196 }; 197 198 // Show overlay and lightbox. If the image is part of a set, add siblings to album array. 199 Lightbox.prototype.start = function($link) { 200 var self = this; 201 var $window = $(window); 202 203 $window.on('resize', $.proxy(this.sizeOverlay, this)); 204 205 $('select, object, embed').css({ 206 visibility: 'hidden' 207 }); 208 209 this.sizeOverlay(); 210 211 this.album = []; 212 var imageNumber = 0; 213 214 function addToAlbum($link) { 215 self.album.push({ 216 alt: $link.attr('data-alt'), 217 link: $link.attr('href'), 218 title: $link.attr('data-title') || $link.attr('title') 219 }); 220 } 221 222 // Support both data-lightbox attribute and rel attribute implementations 223 var dataLightboxValue = $link.attr('data-lightbox'); 224 var $links; 225 226 if (dataLightboxValue) { 227 $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]'); 228 for (var i = 0; i < $links.length; i = ++i) { 229 addToAlbum($($links[i])); 230 if ($links[i] === $link[0]) { 231 imageNumber = i; 232 } 233 } 234 } else { 235 if ($link.attr('rel') === 'lightbox') { 236 // If image is not part of a set 237 addToAlbum($link); 238 } else { 239 // If image is part of a set 240 $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]'); 241 for (var j = 0; j < $links.length; j = ++j) { 242 addToAlbum($($links[j])); 243 if ($links[j] === $link[0]) { 244 imageNumber = j; 245 } 246 } 247 } 248 } 249 250 // Position Lightbox 251 var top = $window.scrollTop() + this.options.positionFromTop; 252 var left = $window.scrollLeft(); 253 this.$lightbox.css({ 254 top: top + 'px', 255 left: left + 'px' 256 }).fadeIn(this.options.fadeDuration); 257 258 // Disable scrolling of the page while open 259 if (this.options.disableScrolling) { 260 $('html').addClass('lb-disable-scrolling'); 261 } 262 263 this.changeImage(imageNumber); 264 }; 265 266 // Hide most UI elements in preparation for the animated resizing of the lightbox. 267 Lightbox.prototype.changeImage = function(imageNumber) { 268 var self = this; 269 270 this.disableKeyboardNav(); 271 var $image = this.$lightbox.find('.lb-image'); 272 273 this.$overlay.fadeIn(this.options.fadeDuration); 274 275 $('.lb-loader').fadeIn('slow'); 276 this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide(); 277 278 this.$outerContainer.addClass('animating'); 279 280 // When image to show is preloaded, we send the width and height to sizeContainer() 281 var preloader = new Image(); 282 preloader.onload = function() { 283 var $preloader; 284 var imageHeight; 285 var imageWidth; 286 var maxImageHeight; 287 var maxImageWidth; 288 var windowHeight; 289 var windowWidth; 290 291 $image.attr({ 292 'alt': self.album[imageNumber].alt, 293 'src': self.album[imageNumber].link 294 }); 295 296 $preloader = $(preloader); 297 298 $image.width(preloader.width); 299 $image.height(preloader.height); 300 301 if (self.options.fitImagesInViewport) { 302 // Fit image inside the viewport. 303 // Take into account the border around the image and an additional 10px gutter on each side. 304 305 windowWidth = $(window).width(); 306 windowHeight = $(window).height(); 307 maxImageWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20; 308 maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - 120; 309 310 // Check if image size is larger then maxWidth|maxHeight in settings 311 if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) { 312 maxImageWidth = self.options.maxWidth; 313 } 314 if (self.options.maxHeight && self.options.maxHeight < maxImageWidth) { 315 maxImageHeight = self.options.maxHeight; 316 } 317 318 // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight 319 // option than we need to size down while maintaining the aspect ratio. 320 if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { 321 if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { 322 imageWidth = maxImageWidth; 323 imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); 324 $image.width(imageWidth);
325 $image.height(imageHeight); 326 } else { 327 imageHeight = maxImageHeight; 328 imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); 329 $image.width(imageWidth); 330 $image.height(imageHeight); 331 } 332 } 333 } 334 self.sizeContainer($image.width(), $image.height()); 335 }; 336 337 preloader.src = this.album[imageNumber].link; 338 this.currentImageIndex = imageNumber; 339 }; 340 341 // Stretch overlay to fit the viewport 342 Lightbox.prototype.sizeOverlay = function() { 343 this.$overlay 344 .width($(document).width()) 345 .height($(document).height()); 346 }; 347 348 // Animate the size of the lightbox to fit the image we are showing 349 Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { 350 var self = this; 351 352 var oldWidth = this.$outerContainer.outerWidth(); 353 var oldHeight = this.$outerContainer.outerHeight(); 354 var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; 355 var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; 356 357 function postResize() { 358 self.$lightbox.find('.lb-dataContainer').width(newWidth); 359 self.$lightbox.find('.lb-prevLink').height(newHeight); 360 self.$lightbox.find('.lb-nextLink').height(newHeight); 361 self.showImage(); 362 } 363 364 if (oldWidth !== newWidth || oldHeight !== newHeight) { 365 this.$outerContainer.animate({ 366 width: newWidth, 367 height: newHeight 368 }, this.options.resizeDuration, 'swing', function() { 369 postResize(); 370 }); 371 } else { 372 postResize(); 373 } 374 }; 375 376 // Display the image and its details and begin preload neighboring images. 377 Lightbox.prototype.showImage = function() { 378 this.$lightbox.find('.lb-loader').stop(true).hide(); 379 this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration); 380 381 this.updateNav(); 382 this.updateDetails(); 383 this.preloadNeighboringImages(); 384 this.enableKeyboardNav(); 385 }; 386 387 // Display previous and next navigation if appropriate. 388 Lightbox.prototype.updateNav = function() { 389 // Check to see if the browser supports touch events. If so, we take the conservative approach 390 // and assume that mouse hover events are not supported and always show prev/next navigation 391 // arrows in image sets. 392 var alwaysShowNav = false; 393 try { 394 document.createEvent('TouchEvent'); 395 alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false; 396 } catch (e) {} 397 398 this.$lightbox.find('.lb-nav').show(); 399 400 if (this.album.length > 1) { 401 if (this.options.wrapAround) { 402 if (alwaysShowNav) { 403 this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); 404 } 405 this.$lightbox.find('.lb-prev, .lb-next').show(); 406 } else { 407 if (this.currentImageIndex > 0) { 408 this.$lightbox.find('.lb-prev').show(); 409 if (alwaysShowNav) { 410 this.$lightbox.find('.lb-prev').css('opacity', '1'); 411 } 412 } 413 if (this.currentImageIndex < this.album.length - 1) { 414 this.$lightbox.find('.lb-next').show(); 415 if (alwaysShowNav) { 416 this.$lightbox.find('.lb-next').css('opacity', '1'); 417 } 418 } 419 } 420 } 421 }; 422 423 // Display caption, image number, and closing button. 424 Lightbox.prototype.updateDetails = function() { 425 var self = this; 426 427 // Enable anchor clicks in the injected caption html. 428 // Thanks Nate Wright for the fix. @https://github.com/NateWr 429 if (typeof this.album[this.currentImageIndex].title !== 'undefined' && 430 this.album[this.currentImageIndex].title !== '') { 431 var $caption = this.$lightbox.find('.lb-caption'); 432 if (this.options.sanitizeTitle) { 433 $caption.text(this.album[this.currentImageIndex].title); 434 } else { 435 $caption.html(this.album[this.currentImageIndex].title); 436 } 437 $caption.fadeIn('fast') 438 .find('a').on('click', function(event) { 439 if ($(this).attr('target') !== undefined) { 440 window.open($(this).attr('href'), $(this).attr('target')); 441 } else { 442 location.href = $(this).attr('href'); 443 } 444 }); 445 } 446 447 if (this.album.length > 1 && this.options.showImageNumberLabel) { 448 var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length); 449 this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast'); 450 } else { 451 this.$lightbox.find('.lb-number').hide(); 452 } 453 454 this.$outerContainer.removeClass('animating'); 455 456 this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { 457 return self.sizeOverlay(); 458 });
459 }; 460 461 // Preload previous and next images in set. 462 Lightbox.prototype.preloadNeighboringImages = function() { 463 if (this.album.length > this.currentImageIndex + 1) { 464 var preloadNext = new Image(); 465 preloadNext.src = this.album[this.currentImageIndex + 1].link; 466 } 467 if (this.currentImageIndex > 0) { 468 var preloadPrev = new Image(); 469 preloadPrev.src = this.album[this.currentImageIndex - 1].link; 470 } 471 }; 472 473 Lightbox.prototype.enableKeyboardNav = function() { 474 $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this)); 475 }; 476 477 Lightbox.prototype.disableKeyboardNav = function() { 478 $(document).off('.keyboard'); 479 }; 480 481 Lightbox.prototype.keyboardAction = function(event) { 482 var KEYCODE_ESC = 27; 483 var KEYCODE_LEFTARROW = 37; 484 var KEYCODE_RIGHTARROW = 39; 485 486 var keycode = event.keyCode; 487 var key = String.fromCharCode(keycode).toLowerCase(); 488 if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) { 489 this.end(); 490 } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) { 491 if (this.currentImageIndex !== 0) { 492 this.changeImage(this.currentImageIndex - 1); 493 } else if (this.options.wrapAround && this.album.length > 1) { 494 this.changeImage(this.album.length - 1); 495 } 496 } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) { 497 if (this.currentImageIndex !== this.album.length - 1) { 498 this.changeImage(this.currentImageIndex + 1); 499 } else if (this.options.wrapAround && this.album.length > 1) { 500 this.changeImage(0); 501 } 502 } 503 }; 504 505 // Closing time. :-( 506 Lightbox.prototype.end = function() { 507 this.disableKeyboardNav(); 508 $(window).off('resize', this.sizeOverlay); 509 this.$lightbox.fadeOut(this.options.fadeDuration); 510 this.$overlay.fadeOut(this.options.fadeDuration); 511 $('select, object, embed').css({ 512 visibility: 'visible' 513 }); 514 if (this.options.disableScrolling) { 515 $('html').removeClass('lb-disable-scrolling'); 516 } 517 }; 518 519 return new Lightbox(); 520}));
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.