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