PageSourceSearch

https://nra.org.uk/wp-content/themes/nra-theme/js/lightbox.js

js nra.org.uk collected 2026-09-24 09:35:41 UTC 18,250 bytes, 511 lines download raw bytes

vendor: 4,910 bytes, lines 1-126
1/*!
2 * Lightbox v2.9.0
3 * by Lokesh Dhakar
4 *
5 * More info:
6 * http://lokeshdhakar.com/projects/lightbox2/
7 *
8 * Copyright 2007, 2015 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    var self = this;
98    $('<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'));
99
100    // Cache jQuery objects
101    this.$lightbox       = $('#lightbox');
102    this.$overlay        = $('#lightboxOverlay');
103    this.$outerContainer = this.$lightbox.find('.lb-outerContainer');
104    this.$container      = this.$lightbox.find('.lb-container');
105    this.$image          = this.$lightbox.find('.lb-image');
106    this.$nav            = this.$lightbox.find('.lb-nav');
107
108    // Store css values for future lookup
109    this.containerPadding = {
110      top: parseInt(this.$container.css('padding-top'), 10),
111      right: parseInt(this.$container.css('padding-right'), 10),
112      bottom: parseInt(this.$container.css('padding-bottom'), 10),
113      left: parseInt(this.$container.css('padding-left'), 10)
114    };
115
116    this.imageBorderWidth = {
117      top: parseInt(this.$image.css('border-top-width'), 10),
118      right: parseInt(this.$image.css('border-right-width'), 10),
119      bottom: parseInt(this.$image.css('border-bottom-width'), 10),
120      left: parseInt(this.$image.css('border-left-width'), 10)
121    };
122
123    // Attach event handlers to the newly minted DOM elements
124    this.$overlay.hide().on('click', function() {
125      self.end();
126      return false;
127    });
128
129    this.$lightbox.hide().on('click', function(event) {
130      if ($(event.target).attr('id') === 'lightbox') {
131        self.end();
132      }
133      return false;
134    });
135
136    this.$outerContainer.on('click', function(event) {
137      if ($(event.target).attr('id') === 'lightbox') {
138        self.end();
139      }
140      return false;
141    });
142
143    this.$lightbox.find('.lb-prev').on('click', function() {
144      if (self.currentImageIndex === 0) {
145        self.changeImage(self.album.length - 1);
146      } else {
147        self.changeImage(self.currentImageIndex - 1);
148      }
149      return false;
150    });
151
152    this.$lightbox.find('.lb-next').on('click', function() {
153      if (self.currentImageIndex === self.album.length - 1) {
154        self.changeImage(0);
155      } else {
156        self.changeImage(self.currentImageIndex + 1);
157      }
158      return false;
159    });
160
161    /*
162      Show context menu for image on right-click
163
164      There is a div containing the navigation that spans the entire image and lives above of it. If
165      you right-click, you are right clicking this div and not the image. This prevents users from
166      saving the image or using other context menu actions with the image.
167
168      To fix this, when we detect the right mouse button is pressed down, but not yet clicked, we
169      set pointer-events to none on the nav div. This is so that the upcoming right-click event on
170      the next mouseup will bubble down to the image. Once the right-click/contextmenu event occurs
171      we set the pointer events back to auto for the nav div so it can capture hover and left-click
172      events as usual.
173     */
174    this.$nav.on('mousedown', function(event) {
175      if (event.which === 3) {
176        self.$nav.css('pointer-events', 'none');
177
178        self.$lightbox.one('contextmenu', function() {
179          setTimeout(function() {
180              this.$nav.css('pointer-events', 'auto');
181          }.bind(self), 0);
182        });
183      }
184    });
185
186
187    this.$lightbox.find('.lb-loader, .lb-close').on('click', function() {
188      self.end();
189      return false;
190    });
191  };
192
193  // Show overlay and lightbox. If the image is part of a set, add siblings to album array.
194  Lightbox.prototype.start = function($link) {
195    var self    = this;
196    var $window = $(window);
197
198    $window.on('resize', $.proxy(this.sizeOverlay, this));
199
200    $('select, object, embed').css({
201      visibility: 'hidden'
202    });
203
204    this.sizeOverlay();
205
206    this.album = [];
207    var imageNumber = 0;
208
209    function addToAlbum($link) {
210      self.album.push({
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
264    this.disableKeyboardNav();
265    var $image = this.$lightbox.find('.lb-image');
266
267    this.$overlay.fadeIn(this.options.fadeDuration);
268
269    $('.lb-loader').fadeIn('slow');
270    this.$lightbox.find('.lb-image, .lb-nav, .lb-prev, .lb-next, .lb-dataContainer, .lb-numbers, .lb-caption').hide();
271
272    this.$outerContainer.addClass('animating');
273
274    // When image to show is preloaded, we send the width and height to sizeContainer()
275    var preloader = new Image();
276    preloader.onload = function() {
277      var $preloader;
278      var imageHeight;
279      var imageWidth;
280      var maxImageHeight;
281      var maxImageWidth;
282      var windowHeight;
283      var windowWidth;
284
285      $image.attr('src', self.album[imageNumber].link);
286
287      $preloader = $(preloader);
288
289      $image.width(preloader.width);
290      $image.height(preloader.height);
291
292      if (self.options.fitImagesInViewport) {
293        // Fit image inside the viewport.
294        // Take into account the border around the image and an additional 10px gutter on each side.
295
296        windowWidth    = $(window).width();
297        windowHeight   = $(window).height();
298        maxImageWidth  = windowWidth - self.containerPadding.left - self.containerPadding.right - self.imageBorderWidth.left - self.imageBorderWidth.right - 20;
299        maxImageHeight = windowHeight - self.containerPadding.top - self.containerPadding.bottom - self.imageBorderWidth.top - self.imageBorderWidth.bottom - 120;
300
301        // Check if image size is larger then maxWidth|maxHeight in settings
302        if (self.options.maxWidth && self.options.maxWidth < maxImageWidth) {
303          maxImageWidth = self.options.maxWidth;
304        }
305        if (self.options.maxHeight && self.options.maxHeight < maxImageWidth) {
306          maxImageHeight = self.options.maxHeight;
307        }
308
309        // Is the current image's width or height is greater than the maxImageWidth or maxImageHeight
310        // option than we need to size down while maintaining the aspect ratio.
311        if ((preloader.width > maxImageWidth) || (preloader.height > maxImageHeight)) {
312          if ((preloader.width / maxImageWidth) > (preloader.height / maxImageHeight)) {
313            imageWidth  = maxImageWidth;
314            imageHeight = parseInt(preloader.height / (preloader.width / imageWidth), 10);
315            $image.width(imageWidth);
316            $image.height(imageHeight);
317          } else {
318            imageHeight = maxImageHeight;
319            imageWidth = parseInt(preloader.width / (preloader.height / imageHeight), 10);
320            $image.width(imageWidth);
321            $image.height(imageHeight);
322          }
323        }
324      }
325      self.sizeContainer($image.width(), $image.height());
326    };
327
328    preloader.src          = this.album[imageNumber].link;
329    this.currentImageIndex = imageNumber;
330  };
331
332  // Stretch overlay to fit the viewport
333  Lightbox.prototype.sizeOverlay = function() {
334    this.$overlay
335      .width($(document).width())
336      .height($(document).height());
337  };
338
339  // Animate the size of the lightbox to fit the image we are showing
340  Lightbox.prototype.sizeContainer = function(imageWidth, imageHeight) {
341    var self = this;
342
343    var oldWidth  = this.$outerContainer.outerWidth();
344    var oldHeight = this.$outerContainer.outerHeight();
345    var newWidth  = imageWidth + this.containerPadding.left + this.containerPadding.right + this.imageBorderWidth.left + this.imageBorderWidth.right;
346    var newHeight = imageHeight + this.containerPadding.top + this.containerPadding.bottom + this.imageBorderWidth.top + this.imageBorderWidth.bottom;
347
348    function postResize() {
349      self.$lightbox.find('.lb-dataContainer').width(newWidth);
350      self.$lightbox.find('.lb-prevLink').height(newHeight);
351      self.$lightbox.find('.lb-nextLink').height(newHeight);
352      self.showImage();
353    }
354
355    if (oldWidth !== newWidth || oldHeight !== newHeight) {
356      this.$outerContainer.animate({
357        width: newWidth,
358        height: newHeight
359      }, this.options.resizeDuration, 'swing', function() {
360        postResize();
361      });
362    } else {
363      postResize();
364    }
365  };
366
367  // Display the image and its details and begin preload neighboring images.
368  Lightbox.prototype.showImage = function() {
369    this.$lightbox.find('.lb-loader').stop(true).hide();
370    this.$lightbox.find('.lb-image').fadeIn(this.options.imageFadeDuration);
371
372    this.updateNav();
373    this.updateDetails();
374    this.preloadNeighboringImages();
375    this.enableKeyboardNav();
376  };
377
378  // Display previous and next navigation if appropriate.
379  Lightbox.prototype.updateNav = function() {
380    // Check to see if the browser supports touch events. If so, we take the conservative approach
381    // and assume that mouse hover events are not supported and always show prev/next navigation
382    // arrows in image sets.
383    var alwaysShowNav = false;
384    try {
385      document.createEvent('TouchEvent');
386      alwaysShowNav = (this.options.alwaysShowNavOnTouchDevices) ? true : false;
387    } catch (e) {}
388
389    this.$lightbox.find('.lb-nav').show();
390
391    if (this.album.length > 1) {
392      if (this.options.wrapAround) {
393        if (alwaysShowNav) {
394          this.$lightbox.find('.lb-prev, .lb-next').css('opacity', '1');
395        }
396        this.$lightbox.find('.lb-prev, .lb-next').show();
397      } else {
398        if (this.currentImageIndex > 0) {
399          this.$lightbox.find('.lb-prev').show();
400          if (alwaysShowNav) {
401            this.$lightbox.find('.lb-prev').css('opacity', '1');
402          }
403        }
404        if (this.currentImageIndex < this.album.length - 1) {
405          this.$lightbox.find('.lb-next').show();
406          if (alwaysShowNav) {
407            this.$lightbox.find('.lb-next').css('opacity', '1');
408          }
409        }
410      }
411    }
412  };
413
414  // Display caption, image number, and closing button.
415  Lightbox.prototype.updateDetails = function() {
416    var self = this;
417
418    // Enable anchor clicks in the injected caption html.
419    // Thanks Nate Wright for the fix. @https://github.com/NateWr
420    if (typeof this.album[this.currentImageIndex].title !== 'undefined' &&
421      this.album[this.currentImageIndex].title !== '') {
422      var $caption = this.$lightbox.find('.lb-caption');
423      if (this.options.sanitizeTitle) {
424        $caption.text(this.album[this.currentImageIndex].title);
425      } else {
426        $caption.html(this.album[this.currentImageIndex].title);
427      }
428      $caption.fadeIn('fast')
429        .find('a').on('click', function(event) {
430          if ($(this).attr('target') !== undefined) {
431            window.open($(this).attr('href'), $(this).attr('target'));
432          } else {
433            location.href = $(this).attr('href');
434          }
435        });
436    }
437
438    if (this.album.length > 1 && this.options.showImageNumberLabel) {
439      var labelText = this.imageCountLabel(this.currentImageIndex + 1, this.album.length);
440      this.$lightbox.find('.lb-number').text(labelText).fadeIn('fast');
441    } else {
442      this.$lightbox.find('.lb-number').hide();
443    }
444
445    this.$outerContainer.removeClass('animating');
446
447    this.$lightbox.find('.lb-dataContainer').fadeIn(this.options.resizeDuration, function() {
448      return self.sizeOverlay();
449    });
450  };
451
452  // Preload previous and next images in set.
453  Lightbox.prototype.preloadNeighboringImages = function() {
454    if (this.album.length > this.currentImageIndex + 1) {
455      var preloadNext = new Image();
456      preloadNext.src = this.album[this.currentImageIndex + 1].link;
457    }
458    if (this.currentImageIndex > 0) {
459      var preloadPrev = new Image();
460      preloadPrev.src = this.album[this.currentImageIndex - 1].link;
461    }
462  };
463
464  Lightbox.prototype.enableKeyboardNav = function() {
465    $(document).on('keyup.keyboard', $.proxy(this.keyboardAction, this));
466  };
467
468  Lightbox.prototype.disableKeyboardNav = function() {
469    $(document).off('.keyboard');
470  };
471
472  Lightbox.prototype.keyboardAction = function(event) {
473    var KEYCODE_ESC        = 27;
474    var KEYCODE_LEFTARROW  = 37;
475    var KEYCODE_RIGHTARROW = 39;
476
477    var keycode = event.keyCode;
478    var key     = String.fromCharCode(keycode).toLowerCase();
479    if (keycode === KEYCODE_ESC || key.match(/x|o|c/)) {
480      this.end();
481    } else if (key === 'p' || keycode === KEYCODE_LEFTARROW) {
482      if (this.currentImageIndex !== 0) {
483        this.changeImage(this.currentImageIndex - 1);
484      } else if (this.options.wrapAround && this.album.length > 1) {
485        this.changeImage(this.album.length - 1);
486      }
487    } else if (key === 'n' || keycode === KEYCODE_RIGHTARROW) {
488      if (this.currentImageIndex !== this.album.length - 1) {
489        this.changeImage(this.currentImageIndex + 1);
490      } else if (this.options.wrapAround && this.album.length > 1) {
491        this.changeImage(0);
492      }
493    }
494  };
495
496  // Closing time. :-(
497  Lightbox.prototype.end = function() {
498    this.disableKeyboardNav();
499    $(window).off('resize', this.sizeOverlay);
500    this.$lightbox.fadeOut(this.options.fadeDuration);
501    this.$overlay.fadeOut(this.options.fadeDuration);
502    $('select, object, embed').css({
503      visibility: 'visible'
504    });
505    if (this.options.disableScrolling) {
506      $('body').removeClass('lb-disable-scrolling');
507    }
508  };
509
510  return new Lightbox();
511}));

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.