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