PageSourceSearch

https://www.7vinesvineyard.com/assets/lib/lightbox2/js/lightbox.js

js 7vinesvineyard.com collected 2026-09-29 13:17:04 UTC 19,197 bytes, 535 lines download raw bytes

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

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.