PageSourceSearch

https://massasoit.edu/_resources/js/lightbox.js

js massasoit.edu collected 2026-10-02 11:00:14 UTC 20,312 bytes, 559 lines download raw bytes

vendor: 5,639 bytes, lines 1-142
1/*!
2 * Lightbox v2.11.1
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 propogation of the Esc keypress when
110    // Lightbox is open. This prevents it from intefering 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" aria-label="Previous image" href="" ></a><a class="lb-next" aria-label="Next image" 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'));
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', function() {
203      self.end();
204      return false;
205    });
206  };
207
208  // Show overlay and lightbox. If the image is part of a set, add siblings to album array.
209  Lightbox.prototype.start = function($link) {
210    var self    = this;
211    var $window = $(window);
212
213    $window.on('resize', $.proxy(this.sizeOverlay, this));
214
215    this.sizeOverlay();
216
217    this.album = [];
218    var imageNumber = 0;
219
220    function addToAlbum($link) {
221      self.album.push({
222        alt: $link.attr('data-alt'),
223        link: $link.attr('href'),
224        title: $link.attr('data-title') || $link.attr('title')
225      });
226    }
227
228    // Support both data-lightbox attribute and rel attribute implementations
229    var dataLightboxValue = $link.attr('data-lightbox');
230    var $links;
231
232    if (dataLightboxValue) {
233      $links = $($link.prop('tagName') + '[data-lightbox="' + dataLightboxValue + '"]');
234      for (var i = 0; i < $links.length; i = ++i) {
235        addToAlbum($($links[i]));
236        if ($links[i] === $link[0]) {
237          imageNumber = i;
238        }
239      }
240    } else {
241      if ($link.attr('rel') === 'lightbox') {
242        // If image is not part of a set
243        addToAlbum($link);
244      } else {
245        // If image is part of a set
246        $links = $($link.prop('tagName') + '[rel="' + $link.attr('rel') + '"]');
247        for (var j = 0; j < $links.length; j = ++j) {
248          addToAlbum($($links[j]));
249          if ($links[j] === $link[0]) {
250            imageNumber = j;
251          }
252        }
253      }
254    }
255
256    // Position Lightbox
257    var top  = $window.scrollTop() + this.options.positionFromTop;
258    var left = $window.scrollLeft();
259    this.$lightbox.css({
260      top: top + 'px',
261      left: left + 'px'
262    }).fadeIn(this.options.fadeDuration);
263
264    // Disable scrolling of the page while open
265    if (this.options.disableScrolling) {
266      $('body').addClass('lb-disable-scrolling');
267    }
268
269    this.changeImage(imageNumber);
270  };
271
272  // Hide most UI elements in preparation for the animated resizing of the lightbox.
273  Lightbox.prototype.changeImage = function(imageNumber) {
274    var self = this;
275    var filename = this.album[imageNumber].link;
276    var filetype = filename.split('.').slice(-1)[0];
277    var $image = this.$lightbox.find('.lb-image');
278
279    // Disable keyboard nav during transitions
280    this.disableKeyboardNav();
281
282    // Show loading state
283    this.$overlay.fadeIn(this.options.fadeDuration);
284    $('.lb-loader').fadeIn('slow');
285    this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide();
286    this.$outerContainer.addClass('animating');
287
288    // When image to show is preloaded, we send the width and height to sizeContainer()
289    var preloader = new Image();
290    preloader.onload = function() {
291      var $preloader;
292      var imageHeight;
293      var imageWidth;
294      var maxImageHeight;
295      var maxImageWidth;
296      var windowHeight;
297      var windowWidth;
298
299      $image.attr({
300        'alt': self.album[imageNumber].alt,
301        'src': filename
302      });
303
304      $preloader = $(preloader);
305
306      $image.width(preloader.width);
307      $image.height(preloader.height);
308      windowWidth = $(window).width();
309      windowHeight = $(window).height();
310
311      // Calculate the max image dimensions for the current viewport.
312      // Take into account the border around the image and an additional 10px gutter on each side.
313      maxImageWidth  = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20;
314      maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - self.options.positionFromTop - 70;
315
316      /*
317      SVGs that don't have width and height attributes specified are reporting width and height
318      values of 0 in Firefox 47 and IE11 on Windows. To fix, we set the width and height to the max
319      dimensions for the viewport rather than 0 x 0.
320
321      https://github.com/lokesh/lightbox2/issues/552
322      */
323
324      if (filetype === 'svg') {
325        if ((preloader.width === 0) || preloader.height === 0) {
326          $image.width(maxImageWidth);
327          $image.height(maxImageHeight);
328        }
329      }
330
331      // Fit image inside the viewport.
332      if (self.options.fitImagesInViewport) {
333
334        // Check if image size is larger then maxWidth|maxHeight in settings
335        if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) {
336          maxImageWidth = self.options.maxWidth;
337        }
338        if (self.options.maxHeight && self.options.maxHeight < maxImageHeight) {
339          maxImageHeight = self.options.maxHeight;
340        }
341
342      } else {
343        maxImageWidth = self.options.maxWidth || preloader.width || maxImageWidth;
344        maxImageHeight = self.options.maxHeight || preloader.height || maxImageHeight;
345      }
346
347      // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight
348      // option than we need to size down while maintaining the aspect ratio.
349      if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) {
350        if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) {
351          imageWidth  = maxImageWidth;
352          imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10);
353          $image.width(imageWidth);
354          $image.height(imageHeight);
355        } else {
356          imageHeight = maxImageHeight;
357          imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10);
358          $image.width(imageWidth);
359          $image.height(imageHeight);
360        }
361      }
362      self.sizeContainer($image.width(), $image.height());
363    };
364
365    // Preload image before showing
366    preloader.src = this.album[imageNumber].link;
367    this.currentImageIndex = imageNumber;
368  };
369
370  // Stretch overlay to fit the viewport
371  Lightbox.prototype.sizeOverlay = function() {
372    var self = this;
373    /*
374    We use a setTimeout 0 to pause JS execution and let the rendering catch-up.
375    Why do this? If the `disableScrolling` option is set to true, a class is added to the body
376    tag that disables scrolling and hides the scrollbar. We want to make sure the scrollbar is
377    hidden before we measure the document width, as the presence of the scrollbar will affect the
378    number.
379    */
380    setTimeout(function() {
381      self.$overlay
382        .width($(document).width())
383        .height($(document).height());
384
385    }, 0);
386  };
387
388  // Animate the size of the lightbox to fit the image we are showing
389  // This method also shows the the image.
390  Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) {
391    var self = this;
392
393    var oldWidth  = this.$outerContainer.outerWidth();
394    var oldHeight = this.$outerContainer.outerHeight();
395    var newWidth  = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right;
396    var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
397
398    function postResize() {
399      self.$lightbox.find('.lb-dataContainer').width(newWidth);
400      self.$lightbox.find('.lb-prevLink').height(newHeight);
401      self.$lightbox.find('.lb-nextLink').height(newHeight);
402
403      // Set focus on one of the two root nodes so keyboard events are captured.
404      self.$overlay.focus();
405
406      self.showImage();
407    }
408
409    if (oldWidth !== newWidth || oldHeight !== newHeight) {
410      this.$outerContainer.animate({
411        width: newWidth,
412        height: newHeight
413      }, this.options.resizeDuration, 'swing', function() {
414        postResize();
415      });
416    } else {
417      postResize();
418    }
419  };
420
421  // Display the image and its details and begin preload neighboring images.
422  Lightbox.prototype.showImage = function() {
423    this.$lightbox.find('.lb-loader').stop(true).hide();
424    this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration);
425
426    this.updateNav();
427    this.updateDetails();
428    this.preloadNeighboringImages();
429    this.enableKeyboardNav();
430  };
431
432  // Display previous and next navigation if appropriate.
433  Lightbox.prototype.updateNav = function() {
434    // Check to see if the browser supports touch events. If so, we take the conservative approach
435    // and assume that mouse hover events are not supported and always show prev/next navigation
436    // arrows in image sets.
437    var alwaysShowNav = false;
438    try {
439      document.createEvent('TouchEvent');
440      alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false;
441    } catch (e) {}
442
443    this.$lightbox.find('.lb-nav').show();
444
445    if (this.album.length > 1) {
446      if (this.options.wrapAround) {
447        if (alwaysShowNav) {
448          this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1');
449        }
450        this.$lightbox.find('.lb-prev, .lb-next').show();
451      } else {
452        if (this.currentImageIndex > 0) {
453          this.$lightbox.find('.lb-prev').show();
454          if (alwaysShowNav) {
455            this.$lightbox.find('.lb-prev').css('opacity', '1');
456          }
457        }
458        if (this.currentImageIndex < this.album.length - 1) {
459          this.$lightbox.find('.lb-next').show();
460          if (alwaysShowNav) {
461            this.$lightbox.find('.lb-next').css('opacity', '1');
462          }
463        }
464      }
465    }
466  };
467
468  // Display caption, image number, and closing button.
469  Lightbox.prototype.updateDetails = function() {
470    var self = this;
471
472    // Enable anchor clicks in the injected caption html.
473    // Thanks Nate Wright for the fix. @https://github.com/NateWr
474    if (typeof this.album[this.currentImageIndex].title !== 'undefined' &&
475      this.album[this.currentImageIndex].title !== '') {
476      var $caption = this.$lightbox.find('.lb-caption');
477      if (this.options.sanitizeTitle) {
478        $caption.text(this.album[this.currentImageIndex].title);
479      } else {
480        $caption.html(this.album[this.currentImageIndex].title);
481      }
482      $caption.fadeIn('fast');
483    }
484
485    if (this.album.length > 1 && this.options.showImageNumberLabel) {
486      var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length);
487      this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast');
488    } else {
489      this.$lightbox.find('.lb-number').hide();
490    }
491
492    this.$outerContainer.removeClass('animating');
493
494    this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() {
495      return self.sizeOverlay();
496    });
497  };
498
499  // Preload previous and next images in set.
500  Lightbox.prototype.preloadNeighboringImages = function() {
501    if (this.album.length > this.currentImageIndex + 1) {
502      var preloadNext = new Image();
503      preloadNext.src = this.album[this.currentImageIndex + 1].link;
504    }
505    if (this.currentImageIndex > 0) {
506      var preloadPrev = new Image();
507      preloadPrev.src = this.album[this.currentImageIndex - 1].link;
508    }
509  };
510
511  Lightbox.prototype.enableKeyboardNav = function() {
512    this.$lightbox.on('keyup.keyboard', $.proxy(this.keyboardAction, this));
513    this.$overlay.on('keyup.keyboard', $.proxy(this.keyboardAction, this));
514  };
515
516  Lightbox.prototype.disableKeyboardNav = function() {
517    this.$lightbox.off('.keyboard');
518    this.$overlay.off('.keyboard');
519  };
520
521  Lightbox.prototype.keyboardAction = function(event) {
522    var KEYCODE_ESC        = 27;
523    var KEYCODE_LEFTARROW  = 37;
524    var KEYCODE_RIGHTARROW = 39;
525
526    var keycode = event.keyCode;
527    if (keycode === KEYCODE_ESC) {
528      // Prevent bubbling so as to not affect other components on the page.
529      event.stopPropagation();
530      this.end();
531    } else if (keycode === KEYCODE_LEFTARROW) {
532      if (this.currentImageIndex !== 0) {
533        this.changeImage(this.currentImageIndex - 1);
534      } else if (this.options.wrapAround && this.album.length > 1) {
535        this.changeImage(this.album.length - 1);
536      }
537    } else if (keycode === KEYCODE_RIGHTARROW) {
538      if (this.currentImageIndex !== this.album.length - 1) {
539        this.changeImage(this.currentImageIndex + 1);
540      } else if (this.options.wrapAround && this.album.length > 1) {
541        this.changeImage(0);
542      }
543    }
544  };
545
546  // Closing time. :-(
547  Lightbox.prototype.end = function() {
548    this.disableKeyboardNav();
549    $(window).off('resize', this.sizeOverlay);
550    this.$lightbox.fadeOut(this.options.fadeDuration);
551    this.$overlay.fadeOut(this.options.fadeDuration);
552
553    if (this.options.disableScrolling) {
554      $('body').removeClass('lb-disable-scrolling');
555    }
556  };
557
558  return new Lightbox();
559}));

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.