1/** 2 * Lightbox v2.7.1 3 * by Lokesh Dhakar - http://lokeshdhakar.com/projects/lightbox2/ 4 * 5 * @license http://creativecommons.org/licenses/by/2.5/ 6 * - Free for use in both personal and commercial projects 7 * - Attribution requires leaving author name, author link, and the license info intact 8 */ 9 10(function() { 11 // Use local alias 12 var $ = jQuery; 13 14 var LightboxOptions = (function() { 15 function LightboxOptions() { 16 this.fadeDuration = 500; 17 this.fitImagesInViewport = true; 18 this.resizeDuration = 700; 19 this.positionFromTop = 50; 20 this.showImageNumberLabel = true; 21 this.alwaysShowNavOnTouchDevices = false; 22 this.wrapAround = false; 23 } 24 25 // Change to localize to non-english language 26 LightboxOptions.prototype.albumLabel = function(curImageNum, albumSize) { 27 return "Image " + curImageNum + " of " + albumSize; 28 }; 29 30 return LightboxOptions; 31 })(); 32 33 34 var Lightbox = (function() { 35 function Lightbox(options) { 36 this.options = options; 37 this.album = []; 38 this.currentImageIndex = void 0; 39 this.init(); 40 } 41 42 Lightbox.prototype.init = function() { 43 this.enable(); 44 this.build(); 45 }; 46 47 // Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes 48 // that contain 'lightbox'. When these are clicked, start lightbox. 49 Lightbox.prototype.enable = function() { 50 var self = this; 51 $('#lightbox-top-div').find('img').each(function() { 52 if(!$(this).parent().is('a')){ 53 $(this).attr({'data-lightbox' : 'lights'}); 54 } 55 }); 56 57 $('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox], img[data-lightbox]', function(event) { 58 self.start($(event.currentTarget)); 59 return false; 60 }); 61 }; 62 63 // Build html for the lightbox and the overlay. 64 // Attach event handlers to the new DOM elements. click click click 65 Lightbox.prototype.build = function() { 66 var self = this; 67 $("<div id='lightboxOverlay' class='lightboxOverlay'></div><div id='lightbox' class='lightbox'><div class='lb-outerContainer'><div class='lb-container'><img class='lb-image' src='' /><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')); 68 69 // Cache jQuery objects 70 this.$lightbox = $('#lightbox'); 71 this.$overlay = $('#lightboxOverlay'); 72 this.$outerContainer = this.$lightbox.find('.lb-outerContainer'); 73 this.$container = this.$lightbox.find('.lb-container'); 74 75 // Store css values for future lookup 76 this.containerTopPadding = parseInt(this.$container.css('padding-top'), 10); 77 this.containerRightPadding = parseInt(this.$container.css('padding-right'), 10); 78 this.containerBottomPadding = parseInt(this.$container.css('padding-bottom'), 10); 79 this.containerLeftPadding = parseInt(this.$container.css('padding-left'), 10); 80 81 // Attach event handlers to the newly minted DOM elements 82 this.$overlay.hide().on('click', function() { 83 self.end(); 84 return false; 85 }); 86 87 this.$lightbox.hide().on('click', function(event) { 88 if ($(event.target).attr('id') === 'lightbox') { 89 self.end(); 90 } 91 return false; 92 }); 93 94 this.$outerContainer.on('click', function(event) { 95 if ($(event.target).attr('id') === 'lightbox') { 96 self.end(); 97 } 98 return false; 99 }); 100 101 this.$lightbox.find('.lb-prev').on('click', function() { 102 if (self.currentImageIndex === 0) { 103 self.changeImage(self.album.length - 1); 104 } else { 105 self.changeImage(self.currentImageIndex - 1); 106 } 107 return false;
108 }); 109 110 this.$lightbox.find('.lb-next').on('click', function() { 111 if (self.currentImageIndex === self.album.length - 1) { 112 self.changeImage(0); 113 } else { 114 self.changeImage(self.currentImageIndex + 1); 115 } 116 return false; 117 }); 118 119 this.$lightbox.find('.lb-loader, .lb-close').on('click', function() { 120 self.end(); 121 return false; 122 }); 123 }; 124 125 // Show overlay and lightbox. If the image is part of a set, add siblings to album array. 126 Lightbox.prototype.start = function($link) { 127 var self = this; 128 var $window = $(window); 129 130 $window.on('resize', $.proxy(this.sizeOverlay, this)); 131 132 $('select, object, embed').css({ 133 visibility: "hidden" 134 }); 135 136 this.sizeOverlay(); 137 138 this.album = []; 139 var imageNumber = 0; 140 141 function addToAlbum($link) { 142 self.album.push({ 143 link: $link.attr('href') || $link.attr('src'), 144 title: $link.attr('data-title') || $link.attr('title') 145 }); 146 } 147 148 // Support both data-lightbox attribute and rel attribute implementations 149 var dataLightboxValue = $link.attr('data-lightbox'); 150 var $links; 151 152 if (dataLightboxValue) { 153 $links = $($link.prop("tagName") + '[data-lightbox="' + dataLightboxValue + '"]'); 154 for (var i = 0; i < $links.length; i = ++i) { 155 addToAlbum($($links[i])); 156 if ($links[i] === $link[0]) { 157 imageNumber = i; 158 } 159 } 160 } else { 161 if ($link.attr('rel') === 'lightbox') { 162 // If image is not part of a set 163 addToAlbum($link); 164 } else { 165 // If image is part of a set 166 $links = $($link.prop("tagName") + '[rel="' + $link.attr('rel') + '"]'); 167 for (var j = 0; j < $links.length; j = ++j) { 168 addToAlbum($($links[j])); 169 if ($links[j] === $link[0]) { 170 imageNumber = j; 171 } 172 } 173 } 174 } 175 176 // Position Lightbox 177 var top = $window.scrollTop() + this.options.positionFromTop; 178 var left = $window.scrollLeft(); 179 this.$lightbox.css({ 180 top: top + 'px', 181 left: left + 'px' 182 }).fadeIn(this.options.fadeDuration); 183 184 this.changeImage(imageNumber); 185 }; 186 187 // Hide most UI elements in preparation for the animated resizing of the lightbox. 188 Lightbox.prototype.changeImage = function(imageNumber) { 189 var self = this; 190 191 this.disableKeyboardNav(); 192 var $image = this.$lightbox.find('.lb-image'); 193 194 this.$overlay.fadeIn(this.options.fadeDuration); 195 196 $('.lb-loader').fadeIn('slow'); 197 this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide(); 198 199 this.$outerContainer.addClass('animating'); 200 201 // When image to show is preloaded, we send the width and height to sizeContainer() 202 var preloader = new Image(); 203 preloader.onload = function() { 204 var $preloader, imageHeight, imageWidth, maxImageHeight, maxImageWidth, windowHeight, windowWidth; 205 $image.attr('src', self.album[imageNumber].link); 206 207 $preloader = $(preloader); 208 209 $image.width(preloader.width); 210 $image.height(preloader.height); 211 212 if (self.options.fitImagesInViewport) { 213 // Fit image inside the viewport. 214 // Take into account the border around the image and an additional 10px gutter on each side. 215 216 windowWidth = $(window).width(); 217 windowHeight = $(window).height(); 218 maxImageWidth = windowWidth - self.containerLeftPadding - self.containerRightPadding - 20; 219 maxImageHeight = windowHeight - self.containerTopPadding - self.containerBottomPadding - 120; 220 221 // Is there a fitting issue? 222 if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { 223 if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { 224 imageWidth = maxImageWidth; 225 imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); 226 $image.width(imageWidth); 227 $image.height(imageHeight); 228 } else { 229 imageHeight = maxImageHeight; 230 imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); 231 $image.width(imageWidth); 232 $image.height(imageHeight); 233 } 234 } 235 } 236 self.sizeContainer($image.width(), $image.height()); 237 }; 238 239 preloader.src = this.album[imageNumber].link; 240 this.currentImageIndex = imageNumber; 241 }; 242 243 // Stretch overlay to fit the viewport 244 Lightbox.prototype.sizeOverlay = function() { 245 this.$overlay 246 .width($(window).width()) 247 .height($(document).height()); 248 }; 249 250 // Animate the size of the lightbox to fit the image we are showing 251 Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { 252 var self = this; 253 254 var oldWidth = this.$outerContainer.outerWidth(); 255 var oldHeight = this.$outerContainer.outerHeight(); 256 var newWidth = imageWidth + this.containerLeftPadding + this.containerRightPadding; 257 var newHeight = imageHeight + this.containerTopPadding + this.containerBottomPadding; 258 259 function postResize() { 260 self.$lightbox.find('.lb-dataContainer').width(newWidth); 261 self.$lightbox.find('.lb-prevLink').height(newHeight); 262 self.$lightbox.find('.lb-nextLink').height(newHeight); 263 self.showImage(); 264 } 265 266 if (oldWidth !== newWidth || oldHeight !== newHeight) { 267 this.$outerContainer.animate({ 268 width: newWidth, 269 height: newHeight 270 }, this.options.resizeDuration, 'swing', function() { 271 postResize(); 272 }); 273 } else { 274 postResize(); 275 } 276 }; 277 278 // Display the image and it's details and begin preload neighboring images. 279 Lightbox.prototype.showImage = function() { 280 this.$lightbox.find('.lb-loader').hide(); 281 this.$lightbox.find('.lb-image').fadeIn('slow'); 282 283 this.updateNav(); 284 this.updateDetails(); 285 this.preloadNeighboringImages(); 286 this.enableKeyboardNav(); 287 }; 288 289 // Display previous and next navigation if appropriate. 290 Lightbox.prototype.updateNav = function() { 291 // Check to see if the browser supports touch events. If so, we take the conservative approach 292 // and assume that mouse hover events are not supported and always show prev/next navigation 293 // arrows in image sets. 294 var alwaysShowNav = false;
295 try { 296 document.createEvent("TouchEvent"); 297 alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices)? true: false; 298 } catch (e) {} 299 300 this.$lightbox.find('.lb-nav').show(); 301 302 if (this.album.length > 1) { 303 if (this.options.wrapAround) { 304 if (alwaysShowNav) { 305 this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); 306 } 307 this.$lightbox.find('.lb-prev, .lb-next').show(); 308 } else { 309 if (this.currentImageIndex > 0) { 310 this.$lightbox.find('.lb-prev').show(); 311 if (alwaysShowNav) { 312 this.$lightbox.find('.lb-prev').css('opacity', '1'); 313 } 314 } 315 if (this.currentImageIndex < this.album.length - 1) { 316 this.$lightbox.find('.lb-next').show(); 317 if (alwaysShowNav) { 318 this.$lightbox.find('.lb-next').css('opacity', '1'); 319 } 320 } 321 } 322 } 323 }; 324 325 // Display caption, image number, and closing button. 326 Lightbox.prototype.updateDetails = function() { 327 var self = this; 328 329 // Enable anchor clicks in the injected caption html. 330 // Thanks Nate Wright for the fix. @https://github.com/NateWr 331 if (typeof this.album[this.currentImageIndex].title !== 'undefined' && this.album[this.currentImageIndex].title !== "") { 332 this.$lightbox.find('.lb-caption') 333 .html(this.album[this.currentImageIndex].title) 334 .fadeIn('fast') 335 .find('a').on('click', function(event){ 336 location.href = $(this).attr('href'); 337 }); 338 } 339 340 if (this.album.length > 1 && this.options.showImageNumberLabel) { 341 this.$lightbox.find('.lb-number').text(this.options.albumLabel(this.currentImageIndex + 1, this.album.length)).fadeIn('fast'); 342 } else { 343 this.$lightbox.find('.lb-number').hide(); 344 } 345 346 this.$outerContainer.removeClass('animating'); 347 348 this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { 349 return self.sizeOverlay(); 350 }); 351 }; 352 353 // Preload previous and next images in set. 354 Lightbox.prototype.preloadNeighboringImages = function() { 355 if (this.album.length > this.currentImageIndex + 1) { 356 var preloadNext = new Image(); 357 preloadNext.src = this.album[this.currentImageIndex + 1].link; 358 } 359 if (this.currentImageIndex > 0) { 360 var preloadPrev = new Image(); 361 preloadPrev.src = this.album[this.currentImageIndex - 1].link; 362 } 363 }; 364 365 Lightbox.prototype.enableKeyboardNav = function() { 366 $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this)); 367 }; 368 369 Lightbox.prototype.disableKeyboardNav = function() { 370 $(document).off('.keyboard'); 371 }; 372 373 Lightbox.prototype.keyboardAction = function(event) { 374 var KEYCODE_ESC = 27; 375 var KEYCODE_LEFTARROW = 37; 376 var KEYCODE_RIGHTARROW = 39; 377 378 var keycode = event.keyCode; 379 var key = String.fromCharCode(keycode).toLowerCase(); 380 if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) { 381 this.end(); 382 } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) { 383 if (this.currentImageIndex !== 0) { 384 this.changeImage(this.currentImageIndex - 1); 385 } else if (this.options.wrapAround && this.album.length > 1) { 386 this.changeImage(this.album.length - 1); 387 } 388 } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) { 389 if (this.currentImageIndex !== this.album.length - 1) { 390 this.changeImage(this.currentImageIndex + 1); 391 } else if (this.options.wrapAround && this.album.length > 1) { 392 this.changeImage(0); 393 } 394 } 395 }; 396 397 // Closing time. :-( 398 Lightbox.prototype.end = function() { 399 this.disableKeyboardNav(); 400 $(window).off("resize", this.sizeOverlay); 401 this.$lightbox.fadeOut(this.options.fadeDuration); 402 this.$overlay.fadeOut(this.options.fadeDuration); 403 $('select, object, embed').css({ 404 visibility: "visible" 405 }); 406 }; 407 408 return Lightbox; 409 410 })(); 411 412 $(function() { 413 var options = new LightboxOptions(); 414 var lightbox = new Lightbox(options); 415 }); 416 417}).call(this);
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.