vendor: 5,440 bytes, lines 1-125
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 13// Uses Node, AMD or browser globals to create a module. 14(function (root, factory) { 15 if (false && typeof define === 'function' && define.amd) { 16 // AMD. Register as an anonymous module. 17 //define(['jquery'], factory); 18 } else if (false && typeof exports === 'object') { 19 // Node. Does not work with strict CommonJS, but 20 // only CommonJS-like environments that support module.exports, 21 // like Node. 22 //module.exports = factory(require('jquery')); 23 } else { 24 // Browser globals (root is window) 25 root.lightbox = factory(root.jQuery); 26 } 27}(this, function ($) { 28 29 function Lightbox(options) { 30 this.album = []; 31 this.currentImageIndex = void 0; 32 this.init(); 33 34 // options 35 this.options = $.extend({}, this.constructor.defaults); 36 this.option(options); 37 } 38 39 // Descriptions of all options available on the demo site: 40 // http://lokeshdhakar.com/projects/lightbox2/index.html#options 41 Lightbox.defaults = { 42 albumLabel: 'Image %1 of %2', 43 alwaysShowNavOnTouchDevices: false, 44 fadeDuration: 600, 45 fitImagesInViewport: true, 46 imageFadeDuration: 600, 47 // maxWidth: 800, 48 // maxHeight: 600, 49 positionFromTop: 50, 50 resizeDuration: 700, 51 showImageNumberLabel: true, 52 wrapAround: false, 53 disableScrolling: false, 54 /* 55 Sanitize Title 56 If the caption data is trusted, for example you are hardcoding it in, then leave this to false. 57 This will free you to add html tags, such as links, in the caption. 58 59 If the caption data is user submitted or from some other untrusted source, then set this to true 60 to prevent xss and other injection attacks. 61 */ 62 sanitizeTitle: false 63 }; 64 65 Lightbox.prototype.option = function(options) { 66 $.extend(this.options, options); 67 }; 68 69 Lightbox.prototype.imageCountLabel = function(currentImageNum, totalImages) { 70 return this.options.albumLabel.replace(/%1/g, currentImageNum).replace(/%2/g, totalImages); 71 }; 72 73 Lightbox.prototype.init = function() { 74 var self = this; 75 // Both enable and build methods require the body tag to be in the DOM. 76 $(document).ready(function() { 77 self.enable(); 78 self.build(); 79 }); 80 }; 81 82 // Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes 83 // that contain 'lightbox'. When these are clicked, start lightbox. 84 Lightbox.prototype.enable = function() { 85 var self = this; 86 $('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) { 87 self.start($(event.currentTarget)); 88 return false; 89 }); 90 }; 91 92 // Build html for the lightbox and the overlay. 93 // Attach event handlers to the new DOM elements. click click click 94 Lightbox.prototype.build = function() { 95 var self = this; 96 if (!$('#lightbox').length && !$('#lightboxOverlay').length) { 97 $('<div id="lightboxOverlay" class="lightboxOverlay"></div><div id="lightbox" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" alt="Lightbox Image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" /><div class="lb-nav"><a class="lb-prev" name="Previous" href="" ><span>Previous</span></a><a class="lb-next" name="Next" href="" ><span>Next</span></a></div><div class="lb-loader"><a class="lb-cancel" name="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" name="Close"></a></div></div></div></div>').appendTo($('body')); 98 } 99 // Cache jQuery objects 100 this.$lightbox = $('#lightbox'); 101 this.$overlay = $('#lightboxOverlay'); 102 this.$outerContainer = this.$lightbox.find('.lb-outerContainer'); 103 this.$container = this.$lightbox.find('.lb-container'); 104 this.$image = this.$lightbox.find('.lb-image'); 105 this.$nav = this.$lightbox.find('.lb-nav'); 106 107 // Store css values for future lookup 108 this.containerPadding = { 109 top: parseInt(this.$container.css('padding-top'), 10), 110 right: parseInt(this.$container.css('padding-right'), 10), 111 bottom: parseInt(this.$container.css('padding-bottom'), 10), 112 left: parseInt(this.$container.css('padding-left'), 10) 113 }; 114 115 this.imageBorderWidth = { 116 top: parseInt(this.$image.css('border-top-width'), 10), 117 right: parseInt(this.$image.css('border-right-width'), 10), 118 bottom: parseInt(this.$image.css('border-bottom-width'), 10), 119 left: parseInt(this.$image.css('border-left-width'), 10) 120 }; 121 122 // Attach event handlers to the newly minted DOM elements 123 this.$overlay.hide().on('click', function() { 124 self.end(); 125 return false;
126 }); 127 128 this.$lightbox.hide().on('click', function(event) { 129 if ($(event.target).attr('id') === 'lightbox') { 130 self.end(); 131 } 132 return false; 133 }); 134 135 this.$outerContainer.on('click', function(event) { 136 if ($(event.target).attr('id') === 'lightbox') { 137 self.end(); 138 } 139 return false; 140 }); 141 142 this.$lightbox.find('.lb-prev').on('click', function() { 143 if (self.currentImageIndex === 0) { 144 self.changeImage(self.album.length - 1); 145 } else { 146 self.changeImage(self.currentImageIndex - 1); 147 } 148 return false; 149 }); 150 151 this.$lightbox.find('.lb-next').on('click', function() { 152 if (self.currentImageIndex === self.album.length - 1) { 153 self.changeImage(0); 154 } else { 155 self.changeImage(self.currentImageIndex + 1); 156 } 157 return false; 158 }); 159 160 /* 161 Show context menu for image on right-click 162 163 There is a div containing the navigation that spans the entire image and lives above of it. If 164 you right-click, you are right clicking this div and not the image. This prevents users from 165 saving the image or using other context menu actions with the image. 166 167 To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we 168 set pointer-events to none on the nav div. This is so that the upcoming right-click event on 169 the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs 170 we set the pointer events back to auto for the nav div so it can capture hover and left-click 171 events as usual. 172 */ 173 this.$nav.on('mousedown', function(event) { 174 if (event.which === 3) { 175 self.$nav.css('pointer-events', 'none'); 176 177 self.$lightbox.one('contextmenu', function() { 178 setTimeout(function() { 179 this.$nav.css('pointer-events', 'auto'); 180 }.bind(self), 0); 181 }); 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 there a fitting issue? 309 if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { 310 if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { 311 imageWidth = maxImageWidth; 312 imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); 313 $image.width(imageWidth); 314 $image.height(imageHeight); 315 } else { 316 imageHeight = maxImageHeight; 317 imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); 318 $image.width(imageWidth); 319 $image.height(imageHeight); 320 } 321 } 322 } 323 self.sizeContainer($image.width(), $image.height()); 324 }; 325 326 preloader.src = this.album[imageNumber].link; 327 this.currentImageIndex = imageNumber; 328 }; 329 330 // Stretch overlay to fit the viewport 331 Lightbox.prototype.sizeOverlay = function() { 332 this.$overlay 333 .width($(document).width()) 334 .height($(document).height()); 335 }; 336 337 // Animate the size of the lightbox to fit the image we are showing 338 Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { 339 var self = this; 340 341 var oldWidth = this.$outerContainer.outerWidth(); 342 var oldHeight = this.$outerContainer.outerHeight(); 343 var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; 344 var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; 345 346 function postResize() { 347 self.$lightbox.find('.lb-dataContainer').width(newWidth); 348 self.$lightbox.find('.lb-prevLink').height(newHeight); 349 self.$lightbox.find('.lb-nextLink').height(newHeight); 350 self.showImage(); 351 } 352 353 if (oldWidth !== newWidth || oldHeight !== newHeight) { 354 this.$outerContainer.animate({ 355 width: newWidth, 356 height: newHeight 357 }, this.options.resizeDuration, 'swing', function() { 358 postResize(); 359 }); 360 } else { 361 postResize(); 362 } 363 }; 364 365 // Display the image and its details and begin preload neighboring images. 366 Lightbox.prototype.showImage = function() { 367 this.$lightbox.find('.lb-loader').stop(true).hide(); 368 this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration); 369 370 this.updateNav(); 371 this.updateDetails(); 372 this.preloadNeighboringImages(); 373 this.enableKeyboardNav(); 374 }; 375 376 // Display previous and next navigation if appropriate. 377 Lightbox.prototype.updateNav = function() { 378 // Check to see if the browser supports touch events. If so, we take the conservative approach 379 // and assume that mouse hover events are not supported and always show prev/next navigation 380 // arrows in image sets. 381 var alwaysShowNav = false;
382 try { 383 document.createEvent('TouchEvent'); 384 alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false; 385 } catch (e) {} 386 387 this.$lightbox.find('.lb-nav').show(); 388 389 if (this.album.length > 1) { 390 if (this.options.wrapAround) { 391 if (alwaysShowNav) { 392 this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); 393 } 394 this.$lightbox.find('.lb-prev, .lb-next').show(); 395 } else { 396 if (this.currentImageIndex > 0) { 397 this.$lightbox.find('.lb-prev').show(); 398 if (alwaysShowNav) { 399 this.$lightbox.find('.lb-prev').css('opacity', '1'); 400 } 401 } 402 if (this.currentImageIndex < this.album.length - 1) { 403 this.$lightbox.find('.lb-next').show(); 404 if (alwaysShowNav) { 405 this.$lightbox.find('.lb-next').css('opacity', '1'); 406 } 407 } 408 } 409 } 410 }; 411 412 // Display caption, image number, and closing button. 413 Lightbox.prototype.updateDetails = function() { 414 var self = this; 415 416 // Enable anchor clicks in the injected caption html. 417 // Thanks Nate Wright for the fix. @https://github.com/NateWr 418 if (typeof this.album[this.currentImageIndex].title !== 'undefined' && 419 this.album[this.currentImageIndex].title !== '') { 420 var $caption = this.$lightbox.find('.lb-caption'); 421 if (this.options.sanitizeTitle) { 422 $caption.text(this.album[this.currentImageIndex].title); 423 } else { 424 $caption.html(this.album[this.currentImageIndex].title); 425 } 426 $caption.fadeIn('fast') 427 .find('a').on('click', function(event) { 428 if ($(this).attr('target') !== undefined) { 429 window.open($(this).attr('href'), $(this).attr('target')); 430 } else { 431 location.href = $(this).attr('href'); 432 } 433 }); 434 } 435 436 if (this.album.length > 1 && this.options.showImageNumberLabel) { 437 var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length); 438 this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast'); 439 } else { 440 this.$lightbox.find('.lb-number').hide(); 441 } 442 443 this.$outerContainer.removeClass('animating'); 444 445 this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { 446 return self.sizeOverlay(); 447 }); 448 }; 449 450 // Preload previous and next images in set. 451 Lightbox.prototype.preloadNeighboringImages = function() { 452 if (this.album.length > this.currentImageIndex + 1) { 453 var preloadNext = new Image(); 454 preloadNext.src = this.album[this.currentImageIndex + 1].link; 455 } 456 if (this.currentImageIndex > 0) { 457 var preloadPrev = new Image(); 458 preloadPrev.src = this.album[this.currentImageIndex - 1].link; 459 } 460 }; 461 462 Lightbox.prototype.enableKeyboardNav = function() { 463 $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this)); 464 }; 465 466 Lightbox.prototype.disableKeyboardNav = function() { 467 $(document).off('.keyboard'); 468 }; 469 470 Lightbox.prototype.keyboardAction = function(event) { 471 var KEYCODE_ESC = 27; 472 var KEYCODE_LEFTARROW = 37; 473 var KEYCODE_RIGHTARROW = 39; 474 475 var keycode = event.keyCode; 476 var key = String.fromCharCode(keycode).toLowerCase(); 477 if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) { 478 this.end(); 479 } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) { 480 if (this.currentImageIndex !== 0) { 481 this.changeImage(this.currentImageIndex - 1); 482 } else if (this.options.wrapAround && this.album.length > 1) { 483 this.changeImage(this.album.length - 1); 484 } 485 } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) { 486 if (this.currentImageIndex !== this.album.length - 1) { 487 this.changeImage(this.currentImageIndex + 1); 488 } else if (this.options.wrapAround && this.album.length > 1) { 489 this.changeImage(0); 490 } 491 } 492 }; 493 494 // Closing time. :-( 495 Lightbox.prototype.end = function() { 496 this.disableKeyboardNav(); 497 $(window).off('resize', this.sizeOverlay); 498 this.$lightbox.fadeOut(this.options.fadeDuration); 499 this.$overlay.fadeOut(this.options.fadeDuration); 500 $('select, object, embed').css({ 501 visibility: 'visible' 502 }); 503 if (this.options.disableScrolling) { 504 $('body').removeClass('lb-disable-scrolling'); 505 } 506 }; 507 508 return new Lightbox(); 509}));
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.