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