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