vendor: 5,841 bytes, lines 1-141
1/* 2 * Percussion CMS 3 * Copyright (C) 1999-2020 Percussion Software, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU Affero General Public License for more details. 12 * 13 * Mailing Address: 14 * 15 * Percussion Software, Inc. 16 * PO Box 767 17 * Burlington, MA 01803, USA 18 * +01-781-438-9900 19 * [email protected] 20 * https://www.percussion.com 21 * 22 * You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/> 23 */ 24 25// Uses Node, AMD or browser globals to create a module. 26(function (root, factory) { 27 if (typeof define === 'function' && define.amd) { 28 // AMD. Register as an anonymous module. 29 define(['jquery'], factory); 30 } else if (typeof exports === 'object') { 31 // Node. Does not work with strict CommonJS, but 32 // only CommonJS-like environments that support module.exports, 33 // like Node. 34 module.exports = factory(require('jquery')); 35 } else { 36 // Browser globals (root is window) 37 root.lightbox = factory(root.jQuery); 38 } 39}(this, function ($) { 40 41 function Lightbox(options) { 42 this.album = []; 43 this.currentImageIndex = void 0; 44 this.init(); 45 46 // options 47 this.options = $.extend({}, this.constructor.defaults); 48 this.option(options); 49 } 50 51 // Descriptions of all options available on the demo site: 52 // http://lokeshdhakar.com/projects/lightbox2/index.html#options 53 Lightbox.defaults = { 54 albumLabel: 'Image %1 of %2', 55 alwaysShowNavOnTouchDevices: false, 56 fadeDuration: 600, 57 fitImagesInViewport: true, 58 imageFadeDuration: 600, 59 // maxWidth: 800, 60 // maxHeight: 600, 61 positionFromTop: 50, 62 resizeDuration: 700, 63 showImageNumberLabel: true, 64 wrapAround: false, 65 disableScrolling: false, 66 /* 67 Sanitize Title 68 If the caption data is trusted, for example you are hardcoding it in, then leave this to false. 69 This will free you to add html tags, such as links, in the caption. 70 71 If the caption data is user submitted or from some other untrusted source, then set this to true 72 to prevent xss and other injection attacks. 73 */ 74 sanitizeTitle: false 75 }; 76 77 Lightbox.prototype.option = function(options) { 78 $.extend(this.options, options); 79 }; 80 81 Lightbox.prototype.imageCountLabel = function(currentImageNum, totalImages) { 82 return this.options.albumLabel.replace(/%1/g, currentImageNum).replace(/%2/g, totalImages); 83 }; 84 85 Lightbox.prototype.init = function() { 86 var self = this; 87 // Both enable and build methods require the body tag to be in the DOM. 88 $(document).ready(function() { 89 self.enable(); 90 self.build(); 91 }); 92 }; 93 94 // Loop through anchors and areamaps looking for either data-lightbox attributes or rel attributes 95 // that contain 'lightbox'. When these are clicked, start lightbox. 96 Lightbox.prototype.enable = function() { 97 var self = this; 98 $('body').on('click', 'a[rel^=lightbox], area[rel^=lightbox], a[data-lightbox], area[data-lightbox]', function(event) { 99 self.start($(event.currentTarget)); 100 return false; 101 }); 102 }; 103 104 // Build html for the lightbox and the overlay. 105 // Attach event handlers to the new DOM elements. click click click 106 Lightbox.prototype.build = function() { 107 if ($('#lightbox').length > 0) { 108 return; 109 } 110 111 var self = this; 112 $('<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')); 113 114 // Cache jQuery objects 115 this.$lightbox = $('#lightbox'); 116 this.$overlay = $('#lightboxOverlay'); 117 this.$outerContainer = this.$lightbox.find('.lb-outerContainer'); 118 this.$container = this.$lightbox.find('.lb-container'); 119 this.$image = this.$lightbox.find('.lb-image'); 120 this.$nav = this.$lightbox.find('.lb-nav'); 121 122 // Store css values for future lookup 123 this.containerPadding = { 124 top: parseInt(this.$container.css('padding-top'), 10), 125 right: parseInt(this.$container.css('padding-right'), 10), 126 bottom: parseInt(this.$container.css('padding-bottom'), 10), 127 left: parseInt(this.$container.css('padding-left'), 10) 128 }; 129 130 this.imageBorderWidth = { 131 top: parseInt(this.$image.css('border-top-width'), 10), 132 right: parseInt(this.$image.css('border-right-width'), 10), 133 bottom: parseInt(this.$image.css('border-bottom-width'), 10), 134 left: parseInt(this.$image.css('border-left-width'), 10) 135 }; 136 137 // Attach event handlers to the newly minted DOM elements 138 this.$overlay.hide().on('click', function() { 139 self.end(); 140 return false; 141 });
142 143 this.$lightbox.hide().on('click', function(event) { 144 if ($(event.target).attr('id') === 'lightbox') { 145 self.end(); 146 } 147 return false; 148 }); 149 150 this.$outerContainer.on('click', function(event) { 151 if ($(event.target).attr('id') === 'lightbox') { 152 self.end(); 153 } 154 return false; 155 }); 156 157 this.$lightbox.find('.lb-prev').on('click', function() { 158 if (self.currentImageIndex === 0) { 159 self.changeImage(self.album.length - 1); 160 } else { 161 self.changeImage(self.currentImageIndex - 1); 162 } 163 return false; 164 }); 165 166 this.$lightbox.find('.lb-next').on('click', function() { 167 if (self.currentImageIndex === self.album.length - 1) { 168 self.changeImage(0); 169 } else { 170 self.changeImage(self.currentImageIndex + 1); 171 } 172 return false; 173 }); 174 175 /* 176 Show context menu for image on right-click 177 178 There is a div containing the navigation that spans the entire image and lives above of it. If 179 you right-click, you are right clicking this div and not the image. This prevents users from 180 saving the image or using other context menu actions with the image. 181 182 To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we 183 set pointer-events to none on the nav div. This is so that the upcoming right-click event on 184 the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs 185 we set the pointer events back to auto for the nav div so it can capture hover and left-click 186 events as usual. 187 */ 188 this.$nav.on('mousedown', function(event) { 189 if (event.which === 3) { 190 self.$nav.css('pointer-events', 'none'); 191 192 self.$lightbox.one('contextmenu', function() { 193 setTimeout(function() { 194 this.$nav.css('pointer-events', 'auto'); 195 }.bind(self), 0); 196 }); 197 } 198 }); 199 200 201 this.$lightbox.find('.lb-loader, .lb-close').on('click', function() { 202 self.end(); 203 return false; 204 }); 205 }; 206 207 // Show overlay and lightbox. If the image is part of a set, add siblings to album array. 208 Lightbox.prototype.start = function($link) { 209 var self = this; 210 var $window = $(window); 211 212 $window.on('resize', function(evt){ 213 $.proxy(this.sizeOverlay, this); 214 }); 215 216 $('select, object, embed').css({ 217 visibility: 'hidden' 218 }); 219 220 this.sizeOverlay(); 221 222 this.album = []; 223 var imageNumber = 0; 224 225 function addToAlbum($link) { 226 self.album.push({ 227 alt: $link.attr('data-alt'), 228 link: $link.attr('href'), 229 title: $link.attr('data-title') || $link.attr('title') 230 }); 231 } 232 233 // Support both data-lightbox attribute and rel attribute implementations 234 var dataLightboxValue = $link.attr('data-lightbox'); 235 var $links; 236 237 if (dataLightboxValue) { 238 $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]'); 239 for (var i = 0; i < $links.length; i = ++i) { 240 addToAlbum($($links[i])); 241 if ($links[i] === $link[0]) { 242 imageNumber = i; 243 } 244 } 245 } else { 246 if ($link.attr('rel') === 'lightbox') { 247 // If image is not part of a set 248 addToAlbum($link); 249 } else { 250 // If image is part of a set 251 $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]'); 252 for (var j = 0; j < $links.length; j = ++j) { 253 addToAlbum($($links[j])); 254 if ($links[j] === $link[0]) { 255 imageNumber = j; 256 } 257 } 258 } 259 } 260 261 // Position Lightbox 262 var top = $window.scrollTop() + this.options.positionFromTop; 263 var left = $window.scrollLeft(); 264 this.$lightbox.css({ 265 top: top + 'px', 266 left: left + 'px' 267 }).fadeIn(this.options.fadeDuration); 268 269 // Disable scrolling of the page while open 270 if (this.options.disableScrolling) { 271 $('html').addClass('lb-disable-scrolling'); 272 } 273 274 this.changeImage(imageNumber); 275 }; 276 277 // Hide most UI elements in preparation for the animated resizing of the lightbox. 278 Lightbox.prototype.changeImage = function(imageNumber) { 279 var self = this; 280 281 this.disableKeyboardNav(); 282 var $image = this.$lightbox.find('.lb-image'); 283 284 this.$overlay.fadeIn(this.options.fadeDuration); 285 286 $('.lb-loader').fadeIn('slow'); 287 this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide(); 288 289 this.$outerContainer.addClass('animating'); 290 291 // When image to show is preloaded, we send the width and height to sizeContainer() 292 var preloader = new Image(); 293 preloader.onload = function() { 294 var $preloader; 295 var imageHeight; 296 var imageWidth; 297 var maxImageHeight; 298 var maxImageWidth; 299 var windowHeight; 300 var windowWidth; 301 302 $image.attr({ 303 'alt': self.album[imageNumber].alt, 304 'src': self.album[imageNumber].link 305 }); 306 307 $preloader = $(preloader); 308 309 $image.width(preloader.width); 310 $image.height(preloader.height); 311 312 if (self.options.fitImagesInViewport) { 313 // Fit image inside the viewport. 314 // Take into account the border around the image and an additional 10px gutter on each side. 315 316 windowWidth = $(window).width(); 317 windowHeight = $(window).height(); 318 maxImageWidth = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20; 319 maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - 120; 320 321 // Check if image size is larger then maxWidth|maxHeight in settings 322 if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) { 323 maxImageWidth = self.options.maxWidth; 324 } 325 if (self.options.maxHeight && self.options.maxHeight < maxImageWidth) { 326 maxImageHeight = self.options.maxHeight; 327 } 328 329 // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight 330 // option than we need to size down while maintaining the aspect ratio. 331 if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) { 332 if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) { 333 imageWidth = maxImageWidth; 334 imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10); 335 $image.width(imageWidth);
336 $image.height(imageHeight); 337 } else { 338 imageHeight = maxImageHeight; 339 imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10); 340 $image.width(imageWidth); 341 $image.height(imageHeight); 342 } 343 } 344 } 345 self.sizeContainer($image.width(), $image.height()); 346 }; 347 348 preloader.src = this.album[imageNumber].link; 349 this.currentImageIndex = imageNumber; 350 }; 351 352 // Stretch overlay to fit the viewport 353 Lightbox.prototype.sizeOverlay = function() { 354 this.$overlay 355 .width($(document).width()) 356 .height($(document).height()); 357 }; 358 359 // Animate the size of the lightbox to fit the image we are showing 360 Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) { 361 var self = this; 362 363 var oldWidth = this.$outerContainer.outerWidth(); 364 var oldHeight = this.$outerContainer.outerHeight(); 365 var newWidth = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right; 366 var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom; 367 368 function postResize() { 369 self.$lightbox.find('.lb-dataContainer').width(newWidth); 370 self.$lightbox.find('.lb-prevLink').height(newHeight); 371 self.$lightbox.find('.lb-nextLink').height(newHeight); 372 self.showImage(); 373 } 374 375 if (oldWidth !== newWidth || oldHeight !== newHeight) { 376 this.$outerContainer.animate({ 377 width: newWidth, 378 height: newHeight 379 }, this.options.resizeDuration, 'swing', function() { 380 postResize(); 381 }); 382 } else { 383 postResize(); 384 } 385 }; 386 387 // Display the image and its details and begin preload neighboring images. 388 Lightbox.prototype.showImage = function() { 389 this.$lightbox.find('.lb-loader').stop(true).hide(); 390 this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration); 391 392 this.updateNav(); 393 this.updateDetails(); 394 this.preloadNeighboringImages(); 395 this.enableKeyboardNav(); 396 }; 397 398 // Display previous and next navigation if appropriate. 399 Lightbox.prototype.updateNav = function() { 400 // Check to see if the browser supports touch events. If so, we take the conservative approach 401 // and assume that mouse hover events are not supported and always show prev/next navigation 402 // arrows in image sets. 403 var alwaysShowNav = false; 404 try { 405 document.createEvent('TouchEvent'); 406 alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false; 407 } catch (e) {} 408 409 this.$lightbox.find('.lb-nav').show(); 410 411 if (this.album.length > 1) { 412 if (this.options.wrapAround) { 413 if (alwaysShowNav) { 414 this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1'); 415 } 416 this.$lightbox.find('.lb-prev, .lb-next').show(); 417 } else { 418 if (this.currentImageIndex > 0) { 419 this.$lightbox.find('.lb-prev').show(); 420 if (alwaysShowNav) { 421 this.$lightbox.find('.lb-prev').css('opacity', '1'); 422 } 423 } 424 if (this.currentImageIndex < this.album.length - 1) { 425 this.$lightbox.find('.lb-next').show(); 426 if (alwaysShowNav) { 427 this.$lightbox.find('.lb-next').css('opacity', '1'); 428 } 429 } 430 } 431 } 432 }; 433 434 // Display caption, image number, and closing button. 435 Lightbox.prototype.updateDetails = function() { 436 var self = this; 437 438 // Enable anchor clicks in the injected caption html. 439 // Thanks Nate Wright for the fix. @https://github.com/NateWr 440 if (typeof this.album[this.currentImageIndex].title !== 'undefined' && 441 this.album[this.currentImageIndex].title !== '') { 442 var $caption = this.$lightbox.find('.lb-caption'); 443 if (this.options.sanitizeTitle) { 444 $caption.text(this.album[this.currentImageIndex].title); 445 } else { 446 $caption.html(this.album[this.currentImageIndex].title); 447 } 448 $caption.fadeIn('fast') 449 .find('a').on('click', function(event) { 450 if ($(this).attr('target') !== undefined) { 451 window.open($(this).attr('href'), $(this).attr('target')); 452 } else { 453 location.href = $(this).attr('href'); 454 } 455 }); 456 } 457 458 if (this.album.length > 1 && this.options.showImageNumberLabel) { 459 var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length); 460 this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast'); 461 } else { 462 this.$lightbox.find('.lb-number').hide(); 463 } 464 465 this.$outerContainer.removeClass('animating'); 466 467 this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() { 468 return self.sizeOverlay(); 469 });
470 }; 471 472 // Preload previous and next images in set. 473 Lightbox.prototype.preloadNeighboringImages = function() { 474 if (this.album.length > this.currentImageIndex + 1) { 475 var preloadNext = new Image(); 476 preloadNext.src = this.album[this.currentImageIndex + 1].link; 477 } 478 if (this.currentImageIndex > 0) { 479 var preloadPrev = new Image(); 480 preloadPrev.src = this.album[this.currentImageIndex - 1].link; 481 } 482 }; 483 484 Lightbox.prototype.enableKeyboardNav = function() { 485 $(document).on('keyup.keyboard', function(evt){ 486 $.proxy(this.keyboardAction, this); 487 }); 488 }; 489 490 Lightbox.prototype.disableKeyboardNav = function() { 491 $(document).off('.keyboard'); 492 }; 493 494 Lightbox.prototype.keyboardAction = function(event) { 495 var KEYCODE_ESC = 27; 496 var KEYCODE_LEFTARROW = 37; 497 var KEYCODE_RIGHTARROW = 39; 498 499 var keycode = event.keyCode; 500 var key = String.fromCharCode(keycode).toLowerCase(); 501 if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) { 502 this.end(); 503 } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) { 504 if (this.currentImageIndex !== 0) { 505 this.changeImage(this.currentImageIndex - 1); 506 } else if (this.options.wrapAround && this.album.length > 1) { 507 this.changeImage(this.album.length - 1); 508 } 509 } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) { 510 if (this.currentImageIndex !== this.album.length - 1) { 511 this.changeImage(this.currentImageIndex + 1); 512 } else if (this.options.wrapAround && this.album.length > 1) { 513 this.changeImage(0); 514 } 515 } 516 }; 517 518 // Closing time. :-( 519 Lightbox.prototype.end = function() { 520 this.disableKeyboardNav(); 521 $(window).off('resize', this.sizeOverlay); 522 this.$lightbox.fadeOut(this.options.fadeDuration); 523 this.$overlay.fadeOut(this.options.fadeDuration); 524 $('select, object, embed').css({ 525 visibility: 'visible' 526 }); 527 if (this.options.disableScrolling) { 528 $('html').removeClass('lb-disable-scrolling'); 529 } 530 }; 531 532 return new Lightbox(); 533}));
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.