PageSourceSearch

https://ilmamaa.ee/javascripts/lightbox.js

js ilmamaa.ee collected 2026-10-02 13:03:16 UTC 15,970 bytes, 598 lines download raw bytes

1// ----------------------------------------
2// Lightbox
3//
4// @author DxF5H
5// @version 0.9.8
6// @url https://github.com/DxF5H/lightbox
7//
8// MIT License
9// ----------------------------------------
10
11// Universal Module Definition
12(function (window, factory) {
13  if (typeof define === "function" && define.amd) {
14    // AMD
15    define(factory);
16  } else if (typeof module === "object" && module.exports) {
17    // CommonJS
18    module.exports = factory();
19  } else {
20    // Browser global
21    window.lightbox = factory();
22  }
23}(typeof window !== "undefined" ? window : this, function () {
24  var lightbox = function (selector, userOptions) {
25    "use strict";
26
27
28    // -----------------
29    // Default options
30    // -----------------
31
32    var options = {
33      // Display captions, if available.
34      captions: true,
35
36      // Set the element where the caption is. Set it to "self" for the a-tag itself
37      captionsSelector: "self",
38
39      // Get the caption from given attribute.
40      captionAttribute: "title",
41
42      // Display navigation buttons. "auto" hides buttons on touch-enabled devices.
43      nav: "auto",
44
45      // Text or HTML for the navigation buttons.
46      navText: ["‹", "›"],
47
48      // Display close button.
49      close: true,
50
51      // Text or HTML for the close button.
52      closeText: "×",
53
54      // Display current image index
55      counter: true,
56
57      // Allow keyboard navigation.
58      keyboard: true,
59
60      // Display zoom icon.
61      zoom: true,
62
63      // Text or HTML for the zoom icon
64      zoomText: "+",
65
66      // Closes the lightbox when clicking outside
67      docClose: false,
68
69      // Swipe up to close lightbox
70      swipeClose: true,
71
72      // Hide scrollbars if lightbox is displayed.
73      scroll: false
74    };
75
76
77    // ------------------
78    // Global variables
79    // ------------------
80
81    // Array with all images
82    var gallery = [];
83
84    // Cache array length
85    var galleryLength = null;
86
87    // Array with all slider elements
88    var sliderElement = [];
89
90    // Current index
91    var currentIndex = 0;
92
93    // Object with touch positions
94    var touch = {};
95
96    // If set to true ignore touch events because animation was already fired
97    var touchFlag = false;
98
99    // The last focused element before opening the overlay
100    var lastFocus = null;
101
102
103    // --------------------------
104    // Create lightbox components
105    // --------------------------
106
107    var overlay = document.createElement("div");
108    overlay.setAttribute("role", "dialog");
109    overlay.setAttribute("aria-hidden", "true");
110    overlay.classList.add("lightbox-overlay");
111    document.getElementsByTagName("body")[0].appendChild(overlay);
112
113
114    var slider = document.createElement("div");
115    slider.classList.add("lightbox-slider");
116    overlay.appendChild(slider);
117
118
119    var prevButton = document.createElement("button");
120    prevButton.setAttribute("type", "button");
121    prevButton.setAttribute("aria-label", "Previous");
122    overlay.appendChild(prevButton);
123
124
125    var nextButton = document.createElement("button");
126    nextButton.setAttribute("type", "button");
127    nextButton.setAttribute("aria-label", "Next");
128    overlay.appendChild(nextButton);
129
130
131    var closeButton = document.createElement("button");
132    closeButton.setAttribute("type", "button");
133    closeButton.setAttribute("aria-label", "Close");
134    overlay.appendChild(closeButton);
135
136
137    var counter = document.createElement("div");
138    counter.classList.add("lightbox-counter");
139    overlay.appendChild(counter);
140
141
142    // ------------------
143    // Global functions
144    // ------------------
145
146    var mergeOptions = function (userOptions, options) {
147      Object.keys(userOptions).forEach(function (key) {
148        options[key] = userOptions[key];
149      });
150    };
151
152
153    var loadImage = function (index, callback) {
154      if (typeof gallery[index] === "undefined" || typeof sliderElement[index] === "undefined") {
155        return;
156
157        // If image is already loaded run callback and return
158      } else if (!sliderElement[index].getElementsByTagName("img")[0].hasAttribute("data-src")) {
159        if (callback) {
160          callback();
161        }
162        return;
163      }
164
165      var figure = sliderElement[index].getElementsByTagName("figure")[0],
166          image = figure.getElementsByTagName("img")[0],
167          figcaption = figure.getElementsByTagName("figcaption")[0];
168
169      image.onload = function () {
170        var loader = figure.querySelector(".lightbox-loader");
171        figure.removeChild(loader);
172        image.style.opacity = "1";
173        if (figcaption) {
174          figcaption.style.opacity = "1";
175        }
176      };
177
178      image.setAttribute("src", image.getAttribute("data-src"));
179      image.removeAttribute("data-src");
180
181      // Run callback
182      if (callback) {
183        callback();
184      }
185    };
186
187
188    var updateOffset = function () {
189      var offset = -currentIndex * 100 + "%";
190
191      if (typeof slider.style.perspective !== "undefined") {
192        slider.style.transform = "translate(" + offset + ", 0)";
193      } else {
194        slider.style.left = offset;
195      }
196    };
197
198
199    var updateCounter = function () {
200      counter.innerHTML = (currentIndex + 1) + "/" + galleryLength;
201    };
202
203
204    var updateFocus = function (direction) {
205      prevButton.disabled = false;
206      nextButton.disabled = false;
207
208      if (currentIndex === galleryLength - 1) {
209        prevButton.disabled = false;
210        nextButton.disabled = true;
211      } else if (currentIndex === 0) {
212        prevButton.disabled = true;
213        nextButton.disabled = false;
214      }
215
216      if (options.nav) {
217        if (!nextButton.disabled && direction !== "left") {
218          nextButton.focus();
219        } else {
220          prevButton.focus();
221        }
222      } else if (options.close) {
223        closeButton.focus();
224      }
225    };
226
227
228    var preloadImage = function (index) {
229      loadImage(index);
230    };
231
232
233    var nextImage = function () {
234      // If next image exists
235      if (currentIndex < galleryLength - 1) {
236        currentIndex++;
237
238        // Updates
239        updateOffset();
240        updateCounter();
241        updateFocus();
242
243        preloadImage(currentIndex + 1);
244      }
245    };
246
247
248    var prevImage = function () {
249      // If previous image exists
250      if (currentIndex > 0) {
251        currentIndex--;
252
253        // Updates
254        updateOffset();
255        updateCounter();
256        updateFocus();
257
258        preloadImage(currentIndex - 1);
259      }
260    };
261
262
263    var createOverlay = function () {
264      var i = 0,
265          x = 0,
266          figure = null,
267          figuresIds = [],
268          figcaption = null,
269          figcaptionsIds = [];
270
271      for (; i < galleryLength; ++i) {
272        sliderElement[i] = document.createElement("div");
273        sliderElement[i].classList.add("lightbox-content");
274        sliderElement[i].id = "lightbox-content-" + i;
275
276        // Create figure
277        figure = document.createElement("figure");
278        figure.innerHTML = "<div class=\"lightbox-loader\"></div>";
279
280        // Create image
281        var image = document.createElement("img");
282        image.style.opacity = "0";
283
284        if (gallery[i].selector.getElementsByTagName("img")[0] && gallery[i].selector.getElementsByTagName("img")[0].alt) {
285          image.alt = gallery[i].selector.getElementsByTagName("img")[0].alt;
286        } else {
287          image.alt = "";
288        }
289        image.setAttribute("src", "");
290        image.setAttribute("data-src", gallery[i].selector.href);
291
292        // Add image to figure
293        figure.appendChild(image);
294
295        // Create figcaption
296        if (options.captions) {
297          figcaption = document.createElement("figcaption");
298          figcaption.style.opacity = "0";
299
300          if (options.captionsSelector == "self" && gallery[i].selector.getAttribute(options.captionAttribute)) {
301            figcaption.innerHTML = gallery[i].selector.getAttribute(options.captionAttribute);
302          } else if (options.captionsSelector == "img" && gallery[i].selector.getElementsByTagName("img")[0].getAttribute(options.captionAttribute)) {
303            figcaption.innerHTML = gallery[i].selector.getElementsByTagName("img")[0].getAttribute(options.captionAttribute);
304          }
305
306          if (figcaption.innerHTML) {
307            figure.id = "lightbox-figure-" + x;
308            figcaption.id = "lightbox-figcaption-" + x;
309            figure.appendChild(figcaption);
310
311            figuresIds.push("lightbox-figure-" + x);
312            figcaptionsIds.push("lightbox-figcaption-" + x);
313            ++x;
314          }
315        }
316
317        // Add figure to slider element
318        sliderElement[i].appendChild(figure);
319
320        // Add slider element to slider
321        slider.appendChild(sliderElement[i]);
322      }
323
324      if (x !== 0) {
325        overlay.setAttribute("aria-labelledby", figuresIds.join(" "));
326        overlay.setAttribute("aria-describedby", figcaptionsIds.join(" "));
327      }
328
329      // Hide buttons if necessary
330      if (!options.nav || galleryLength === 1 || (options.nav === "auto" && "ontouchstart" in window)) {
331        prevButton.style.display = "none";
332        nextButton.style.display = "none";
333      } else {
334        prevButton.innerHTML = options.navText[0];
335        nextButton.innerHTML = options.navText[1];
336      }
337
338      // Hide counter if necessary
339      if (!options.counter || galleryLength === 1) {
340        counter.style.display = "none";
341      }
342
343      // Hide close if necessary
344      if (!options.close) {
345        closeButton.style.display = "none";
346      } else {
347        closeButton.innerHTML = options.closeText;
348      }
349    };
350
351
352    var openOverlay = function (index) {
353      if (overlay.getAttribute("aria-hidden") === "false") {
354        return;
355      }
356
357      if (!options.scroll) {
358        document.documentElement.classList.add("lightbox-no-scroll");
359        document.body.classList.add("lightbox-no-scroll");
360      }
361
362      // Save last focused element
363      lastFocus = document.activeElement;
364
365      // Set current index
366      currentIndex = index;
367
368      // Reset touch positions
369      touch = {
370        count: 0,
371        startX: null,
372        startY: null,
373        moveX: null,
374        moveY: null,
375      };
376
377      // Bind events
378      bindEvents();
379
380      // load image
381      loadImage(currentIndex, function () {
382        preloadImage(currentIndex + 1);
383        preloadImage(currentIndex - 1);
384      });
385
386      updateOffset();
387      updateCounter();
388      overlay.setAttribute("aria-hidden", "false");
389
390      updateFocus();
391    };
392
393
394    var closeOverlay = function () {
395      if (overlay.getAttribute("aria-hidden") === "true") {
396        return;
397      }
398
399      if (!options.scroll) {
400        document.documentElement.classList.remove("lightbox-no-scroll");
401        document.body.classList.remove("lightbox-no-scroll");
402      }
403
404      // Unbind events
405      unbindEvents();
406
407      overlay.setAttribute("aria-hidden", "true");
408
409      // Focus
410      lastFocus.focus();
411    };
412
413
414    // ------------------
415    // Event handler
416    // ------------------
417
418    var clickHandler = function (event) {
419      if (this === prevButton) {
420        prevImage();
421        updateFocus("left");
422      } else if (this === nextButton) {
423        nextImage();
424        updateFocus("right");
425      } else if (this === closeButton || this === overlay && event.target.id.indexOf("lightbox-content") !== -1) {
426        closeOverlay();
427      }
428
429      event.preventDefault();
430    };
431
432
433    var keyupHandler = function (event) {
434      switch (event.keyCode) {
435        // Left arrow
436        case 37:
437          prevImage();
438          updateFocus("left");
439          break;
440
441        // Right arrow
442        case 39:
443          nextImage();
444          updateFocus("right");
445          break;
446
447        // Esc
448        case 27:
449          closeOverlay();
450          break;
451      }
452    };
453
454
455    var touchstartHandler = function (event) {
456      touch.count++;
457      if (touch.count > 1) {
458        touch.multitouch = true;
459      }
460      // Get the touch position
461      touch.startX = event.changedTouches[0].pageX;
462      touch.startY = event.changedTouches[0].pageY;
463    };
464
465    var touchmoveHandler = function (event) {
466      // If action was already triggered or multitouch return
467      if (touchFlag || touch.multitouch) {
468        return;
469      }
470
471      touch.moveX = event.changedTouches[0].pageX;
472      touch.moveY = event.changedTouches[0].pageY;
473
474      // Move > 50 pixels left or right to navigate
475      if (touch.moveX - touch.startX > 50) {
476        touchFlag = true;
477        prevImage();
478      } else if (touch.moveX - touch.startX < -50) {
479        touchFlag = true;
480        nextImage();
481
482        // Move > 100 pixels up to close the overlay
483      } else if (options.swipeClose && touch.startY - touch.moveY > 100) {
484        closeOverlay();
485      }
486
487      event.preventDefault();
488    };
489
490
491    var touchendHandler = function () {
492      touch.count--;
493      if (touch.count <= 0) {
494        touch.multitouch = false;
495      }
496      touchFlag = false;
497    };
498
499
500    var trapFocus = function (event) {
501      if (overlay.getAttribute("aria-hidden") === "false" && !overlay.contains(event.target)) {
502        event.stopPropagation();
503        updateFocus();
504      }
505    };
506
507
508    var bindEvents = function () {
509      if (options.keyboard) {
510        document.addEventListener("keyup", keyupHandler, false);
511      }
512
513      if (options.docClose) {
514        overlay.addEventListener("click", clickHandler, false);
515      }
516
517      prevButton.addEventListener("click", clickHandler, false);
518      nextButton.addEventListener("click", clickHandler, false);
519      closeButton.addEventListener("click", clickHandler, false);
520      overlay.addEventListener("touchstart", touchstartHandler, false);
521      overlay.addEventListener("touchmove", touchmoveHandler, false);
522      overlay.addEventListener("touchend", touchendHandler, false);
523      document.addEventListener("focus", trapFocus, true);
524    };
525
526
527    var unbindEvents = function () {
528      if (options.keyboard) {
529        document.removeEventListener("keyup", keyupHandler, false);
530      }
531
532      if (options.docClose) {
533        overlay.removeEventListener("click", clickHandler, false);
534      }
535
536      prevButton.removeEventListener("click", clickHandler, false);
537      nextButton.removeEventListener("click", clickHandler, false);
538      closeButton.removeEventListener("click", clickHandler, false);
539      overlay.removeEventListener("touchstart", touchstartHandler, false);
540      overlay.removeEventListener("touchmove", touchmoveHandler, false);
541      overlay.removeEventListener("touchend", touchendHandler, false);
542      document.removeEventListener("focus", trapFocus, true);
543    };
544
545
546    // ------
547    // Setup
548    // ------
549
550    var setup = function (selector, userOptions) {
551      // Merge user options into defaults
552      if (userOptions) {
553        mergeOptions(userOptions, options);
554      }
555
556      // Get a list of all elements within the document
557      var elements = document.querySelectorAll(selector);
558
559      if (!elements.length) {
560        console.log("Ups, I can't find the selector \"" + selector + "\".");
561        return;
562      }
563
564      // Execute a few things once per element
565      [].forEach.call(elements, function(element, index) {
566        // Set zoom icon if necessary
567        if (options.zoom && element.getElementsByTagName("img")[0]) {
568          var lightboxZoom = document.createElement("div");
569
570          lightboxZoom.classList.add("lightbox-zoom");
571          lightboxZoom.innerHTML = options.zoomText;
572
573          element.appendChild(lightboxZoom);
574        }
575
576        // Bind event
577        element.addEventListener("click", function (event) {
578          openOverlay(index);
579
580          event.preventDefault();
581        }, true);
582
583        // Add element to gallery
584        gallery.push({
585          selector: element
586        });
587      });
588
589      galleryLength = gallery.length;
590      createOverlay();
591    };
592
593
594    setup(selector, userOptions);
595  };
596
597  return lightbox;
598}));

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.