PageSourceSearch

https://webfocus.by/wp-content/themes/webfocus/light-box/js/lightbox.js

js webfocus.by collected 2026-10-06 01:33:00 UTC 20,749 bytes, 566 lines download raw bytes

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

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.