PageSourceSearch

https://www.cjstandalproductions.com/classes/components/Gallery/resources/lightbox2/js/lightbox.js

js cjstandalproductions.com collected 2026-09-27 04:03:13 UTC 20,822 bytes, 571 lines download raw bytes

vendor: 5,748 bytes, lines 1-142
1/*!
2 * Lightbox v2.11.5
3 * by Lokesh Dhakar
4 *
5 * More info:
6 * http://lokeshdhakar.com/projects/lightbox2/
7 *
8 * Copyright 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    if ($('#lightbox').length > 0) {
98        return;
99    }
100
101    var self = this;
102
103    // The two root notes generated, #lightboxOverlay and #lightbox are given
104    // tabindex attrs so they are focusable. We attach our keyboard event
105    // listeners to these two elements, and not the document. Clicking anywhere
106    // while Lightbox is opened will keep the focus on or inside one of these
107    // two elements.
108    //
109    // We do this so we can prevent propagation of the Esc keypress when
110    // Lightbox is open. This prevents it from interfering with other components
111    // on the page below.
112    //
113    // Github issue: https://github.com/lokesh/lightbox2/issues/663
114    $('<div id="lightboxOverlay" tabindex="-1" class="lightboxOverlay"></div><div id="lightbox" tabindex="-1" class="lightbox"><div class="lb-outerContainer"><div class="lb-container"><img class="lb-image" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" alt=""/><div class="lb-nav"><a class="lb-prev" role="button" tabindex="0" aria-label="Previous image" href="" ></a><a class="lb-next" role="button" tabindex="0" aria-label="Next image" href="" ></a></div><div class="lb-loader"><a class="lb-cancel" role="button" tabindex="0"></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" role="button" tabindex="0"></a></div></div></div></div>').appendTo($('body'));
115
116    // Cache jQuery objects
117    this.$lightbox       = $('#lightbox');
118    this.$overlay        = $('#lightboxOverlay');
119    this.$outerContainer = this.$lightbox.find('.lb-outerContainer');
120    this.$container      = this.$lightbox.find('.lb-container');
121    this.$image          = this.$lightbox.find('.lb-image');
122    this.$nav            = this.$lightbox.find('.lb-nav');
123
124    // Store css values for future lookup
125    this.containerPadding = {
126      top: parseInt(this.$container.css('padding-top'), 10),
127      right: parseInt(this.$container.css('padding-right'), 10),
128      bottom: parseInt(this.$container.css('padding-bottom'), 10),
129      left: parseInt(this.$container.css('padding-left'), 10)
130    };
131
132    this.imageBorderWidth = {
133      top: parseInt(this.$image.css('border-top-width'), 10),
134      right: parseInt(this.$image.css('border-right-width'), 10),
135      bottom: parseInt(this.$image.css('border-bottom-width'), 10),
136      left: parseInt(this.$image.css('border-left-width'), 10)
137    };
138
139    // Attach event handlers to the newly minted DOM elements
140    this.$overlay.hide().on('click', function() {
141      self.end();
142      return false;
143    });
144
145    this.$lightbox.hide().on('click', function(event) {
146      if ($(event.target).attr('id') === 'lightbox') {
147        self.end();
148      }
149    });
150
151    this.$outerContainer.on('click', function(event) {
152      if ($(event.target).attr('id') === 'lightbox') {
153        self.end();
154      }
155      return false;
156    });
157
158    this.$lightbox.find('.lb-prev').on('click', function() {
159      if (self.currentImageIndex === 0) {
160        self.changeImage(self.album.length - 1);
161      } else {
162        self.changeImage(self.currentImageIndex - 1);
163      }
164      return false;
165    });
166
167    this.$lightbox.find('.lb-next').on('click', function() {
168      if (self.currentImageIndex === self.album.length - 1) {
169        self.changeImage(0);
170      } else {
171        self.changeImage(self.currentImageIndex + 1);
172      }
173      return false;
174    });
175
176    /*
177      Show context menu for image on right-click
178
179      There is a div containing the navigation that spans the entire image and lives above of it. If
180      you right-click, you are right clicking this div and not the image. This prevents users from
181      saving the image or using other context menu actions with the image.
182
183      To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we
184      set pointer-events to none on the nav div. This is so that the upcoming right-click event on
185      the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs
186      we set the pointer events back to auto for the nav div so it can capture hover and left-click
187      events as usual.
188     */
189    this.$nav.on('mousedown', function(event) {
190      if (event.which === 3) {
191        self.$nav.css('pointer-events', 'none');
192
193        self.$lightbox.one('contextmenu', function() {
194          setTimeout(function() {
195              this.$nav.css('pointer-events', 'auto');
196          }.bind(self), 0);
197        });
198      }
199    });
200
201
202    this.$lightbox.find('.lb-loader, .lb-close').on('click keyup', function(e) {
203      // If mouse click OR 'enter' or 'space' keypress, close LB
204      if (
205        e.type === 'click' || (e.type === 'keyup' && (e.which === 13 || e.which === 32))) {
206        self.end();
207        return false;
208      }
209    });
210  };
211
212  // Show overlay and lightbox. If the image is part of a set, add siblings to album array.
213  Lightbox.prototype.start = function($link) {
214    var self    = this;
215    var $window = $(window);
216
217    $window.on('resize', $.proxy(this.sizeOverlay, this));
218
219    this.sizeOverlay();
220
221    this.album = [];
222    var imageNumber = 0;
223
224    function addToAlbum($link) {
225      self.album.push({
226        alt: $link.attr('data-alt'),
227        link: $link.attr('href'),
228        title: $link.attr('data-title') || $link.attr('title')
229      });
230    }
231
232    // Support both data-lightbox attribute and rel attribute implementations
233    var dataLightboxValue = $link.attr('data-lightbox');
234    var $links;
235
236    if (dataLightboxValue) {
237      $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]');
238      for (var i = 0; i < $links.length; i = ++i) {
239        addToAlbum($($links[i]));
240        if ($links[i] === $link[0]) {
241          imageNumber = i;
242        }
243      }
244    } else {
245      if ($link.attr('rel') === 'lightbox') {
246        // If image is not part of a set
247        addToAlbum($link);
248      } else {
249        // If image is part of a set
250        $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]');
251        for (var j = 0; j < $links.length; j = ++j) {
252          addToAlbum($($links[j]));
253          if ($links[j] === $link[0]) {
254            imageNumber = j;
255          }
256        }
257      }
258    }
259
260    // Position Lightbox
261    var top  = $window.scrollTop() + this.options.positionFromTop;
262    var left = $window.scrollLeft();
263    this.$lightbox.css({
264      top: top + 'px',
265      left: left + 'px'
266    }).fadeIn(this.options.fadeDuration);
267
268    // Disable scrolling of the page while open
269    if (this.options.disableScrolling) {
270      $('body').addClass('lb-disable-scrolling');
271    }
272
273    this.changeImage(imageNumber);
274  };
275
276  // Hide most UI elements in preparation for the animated resizing of the lightbox.
277  Lightbox.prototype.changeImage = function(imageNumber) {
278    var self = this;
279    var filename = this.album[imageNumber].link;
280    var filetype = filename.split('.').slice(-1)[0];
281    var $image = this.$lightbox.find('.lb-image');
282
283    // Disable keyboard nav during transitions
284    this.disableKeyboardNav();
285
286    // Show loading state
287    this.$overlay.fadeIn(this.options.fadeDuration);
288    $('.lb-loader').fadeIn('slow');
289    this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide();
290    this.$outerContainer.addClass('animating');
291
292    // When image to show is preloaded, we send the width and height to sizeContainer()
293    var preloader = new Image();
294    preloader.onload = function() {
295      var $preloader;
296      var imageHeight;
297      var imageWidth;
298      var maxImageHeight;
299      var maxImageWidth;
300      var windowHeight;
301      var windowWidth;
302
303      $image.attr({
304        'alt': self.album[imageNumber].alt,
305        'src': filename
306      });
307
308      $preloader = $(preloader);
309
310      $image.width(preloader.width);
311      $image.height(preloader.height);
312
313      var aspectRatio = preloader.width / preloader.height;
314
315      windowWidth = $(window).width();
316      windowHeight = $(window).height();
317
318      // Calculate the max image dimensions for the current viewport.
319      // Take into account the border around the image and an additional 10px gutter on each side.
320      maxImageWidth  = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20;
321      maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - self.options.positionFromTop - 70;
322
323      /*
324      Since many SVGs have small intrinsic dimensions, but they support scaling
325      up without quality loss because of their vector format, max out their
326      size inside the viewport.
327      */
328      if (filetype === 'svg') {
329        if (aspectRatio >= 1) {
330          imageWidth = maxImageWidth;
331          imageHeight = parseInt(maxImageWidth / aspectRatio, 10);
332        } else {
333          imageWidth = parseInt(maxImageHeight / aspectRatio, 10);
334          imageHeight = maxImageHeight;
335        }
336        $image.width(imageWidth);
337        $image.height(imageHeight);
338
339      } else {
340
341        // Fit image inside the viewport.
342        if (self.options.fitImagesInViewport) {
343
344          // Check if image size is larger then maxWidth|maxHeight in settings
345          if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) {
346            maxImageWidth = self.options.maxWidth;
347          }
348          if (self.options.maxHeight && self.options.maxHeight < maxImageHeight) {
349            maxImageHeight = self.options.maxHeight;
350          }
351
352        } else {
353          maxImageWidth = self.options.maxWidth || preloader.width || maxImageWidth;
354          maxImageHeight = self.options.maxHeight || preloader.height || maxImageHeight;
355        }
356
357        // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight
358        // option than we need to size down while maintaining the aspect ratio.
359        if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) {
360          if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) {
361            imageWidth  = maxImageWidth;
362            imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10);
363            $image.width(imageWidth);
364            $image.height(imageHeight);
365          } else {
366            imageHeight = maxImageHeight;
367            imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10);
368            $image.width(imageWidth);
369            $image.height(imageHeight);
370          }
371        }
372      }
373
374      self.sizeContainer($image.width(), $image.height());
375    };
376
377    // Preload image before showing
378    preloader.src = this.album[imageNumber].link;
379    this.currentImageIndex = imageNumber;
380  };
381
382  // Stretch overlay to fit the viewport
383  Lightbox.prototype.sizeOverlay = function() {
384    var self = this;
385    /*
386    We use a setTimeout 0 to pause JS execution and let the rendering catch-up.
387    Why do this? If the `disableScrolling` option is set to true, a class is added to the body
388    tag that disables scrolling and hides the scrollbar. We want to make sure the scrollbar is
389    hidden before we measure the document width, as the presence of the scrollbar will affect the
390    number.
391    */
392    setTimeout(function() {
393      self.$overlay
394        .width($(document).width())
395        .height($(document).height());
396
397    }, 0);
398  };
399
400  // Animate the size of the lightbox to fit the image we are showing
401  // This method also shows the the image.
402  Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) {
403    var self = this;
404
405    var oldWidth  = this.$outerContainer.outerWidth();
406    var oldHeight = this.$outerContainer.outerHeight();
407    var newWidth  = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right;
408    var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
409
410    function postResize() {
411      self.$lightbox.find('.lb-dataContainer').width(newWidth);
412      self.$lightbox.find('.lb-prevLink').height(newHeight);
413      self.$lightbox.find('.lb-nextLink').height(newHeight);
414
415      // Set focus on one of the two root nodes so keyboard events are captured.
416      self.$overlay.trigger('focus');
417
418      self.showImage();
419    }
420
421    if (oldWidth !== newWidth || oldHeight !== newHeight) {
422      this.$outerContainer.animate({
423        width: newWidth,
424        height: newHeight
425      }, this.options.resizeDuration, 'swing', function() {
426        postResize();
427      });
428    } else {
429      postResize();
430    }
431  };
432
433  // Display the image and its details and begin preload neighboring images.
434  Lightbox.prototype.showImage = function() {
435    this.$lightbox.find('.lb-loader').stop(true).hide();
436    this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration);
437
438    this.updateNav();
439    this.updateDetails();
440    this.preloadNeighboringImages();
441    this.enableKeyboardNav();
442  };
443
444  // Display previous and next navigation if appropriate.
445  Lightbox.prototype.updateNav = function() {
446    // Check to see if the browser supports touch events. If so, we take the conservative approach
447    // and assume that mouse hover events are not supported and always show prev/next navigation
448    // arrows in image sets.
449    var alwaysShowNav = false;
450    try {
451      document.createEvent('TouchEvent');
452      alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false;
453    } catch (e) {}
454
455    this.$lightbox.find('.lb-nav').show();
456
457    if (this.album.length > 1) {
458      if (this.options.wrapAround) {
459        if (alwaysShowNav) {
460          this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1');
461        }
462        this.$lightbox.find('.lb-prev, .lb-next').show();
463      } else {
464        if (this.currentImageIndex > 0) {
465          this.$lightbox.find('.lb-prev').show();
466          if (alwaysShowNav) {
467            this.$lightbox.find('.lb-prev').css('opacity', '1');
468          }
469        }
470        if (this.currentImageIndex < this.album.length - 1) {
471          this.$lightbox.find('.lb-next').show();
472          if (alwaysShowNav) {
473            this.$lightbox.find('.lb-next').css('opacity', '1');
474          }
475        }
476      }
477    }
478  };
479
480  // Display caption, image number, and closing button.
481  Lightbox.prototype.updateDetails = function() {
482    var self = this;
483
484    // Enable anchor clicks in the injected caption html.
485    // Thanks Nate Wright for the fix. @https://github.com/NateWr
486    if (typeof this.album[this.currentImageIndex].title !== 'undefined' &&
487      this.album[this.currentImageIndex].title !== '') {
488      var $caption = this.$lightbox.find('.lb-caption');
489      if (this.options.sanitizeTitle) {
490        $caption.text(this.album[this.currentImageIndex].title);
491      } else {
492        $caption.html(this.album[this.currentImageIndex].title);
493      }
494      $caption.fadeIn('fast');
495    }
496
497    if (this.album.length > 1 && this.options.showImageNumberLabel) {
498      var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length);
499      this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast');
500    } else {
501      this.$lightbox.find('.lb-number').hide();
502    }
503
504    this.$outerContainer.removeClass('animating');
505
506    this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() {
507      return self.sizeOverlay();
508    });
509  };
510
511  // Preload previous and next images in set.
512  Lightbox.prototype.preloadNeighboringImages = function() {
513    if (this.album.length > this.currentImageIndex + 1) {
514      var preloadNext = new Image();
515      preloadNext.src = this.album[this.currentImageIndex + 1].link;
516    }
517    if (this.currentImageIndex > 0) {
518      var preloadPrev = new Image();
519      preloadPrev.src = this.album[this.currentImageIndex - 1].link;
520    }
521  };
522
523  Lightbox.prototype.enableKeyboardNav = function() {
524    this.$lightbox.on('keyup.keyboard', $.proxy(this.keyboardAction, this));
525    this.$overlay.on('keyup.keyboard', $.proxy(this.keyboardAction, this));
526  };
527
528  Lightbox.prototype.disableKeyboardNav = function() {
529    this.$lightbox.off('.keyboard');
530    this.$overlay.off('.keyboard');
531  };
532
533  Lightbox.prototype.keyboardAction = function(event) {
534    var KEYCODE_ESC        = 27;
535    var KEYCODE_LEFTARROW  = 37;
536    var KEYCODE_RIGHTARROW = 39;
537
538    var keycode = event.keyCode;
539    if (keycode === KEYCODE_ESC) {
540      // Prevent bubbling so as to not affect other components on the page.
541      event.stopPropagation();
542      this.end();
543    } else if (keycode === KEYCODE_LEFTARROW) {
544      if (this.currentImageIndex !== 0) {
545        this.changeImage(this.currentImageIndex - 1);
546      } else if (this.options.wrapAround && this.album.length > 1) {
547        this.changeImage(this.album.length - 1);
548      }
549    } else if (keycode === KEYCODE_RIGHTARROW) {
550      if (this.currentImageIndex !== this.album.length - 1) {
551        this.changeImage(this.currentImageIndex + 1);
552      } else if (this.options.wrapAround && this.album.length > 1) {
553        this.changeImage(0);
554      }
555    }
556  };
557
558  // Closing time. :-(
559  Lightbox.prototype.end = function() {
560    this.disableKeyboardNav();
561    $(window).off('resize', this.sizeOverlay);
562    this.$lightbox.fadeOut(this.options.fadeDuration);
563    this.$overlay.fadeOut(this.options.fadeDuration);
564
565    if (this.options.disableScrolling) {
566      $('body').removeClass('lb-disable-scrolling');
567    }
568  };
569
570  return new Lightbox();
571}));

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.