lightgallery
Version:
lightGallery is a feature-rich, modular JavaScript gallery plugin for building beautiful image and video galleries for the web and the mobile
1,248 lines (1,238 loc) • 114 kB
JavaScript
/*!
* lightgallery | 2.7.1 | January 11th 2023
* http://www.lightgalleryjs.com/
* Copyright (c) 2020 Sachin Neravath;
* @license GPLv3
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __spreadArrays() {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
}
/**
* List of lightGallery events
* All events should be documented here
* Below interfaces are used to build the website documentations
* */
var lGEvents = {
afterAppendSlide: 'lgAfterAppendSlide',
init: 'lgInit',
hasVideo: 'lgHasVideo',
containerResize: 'lgContainerResize',
updateSlides: 'lgUpdateSlides',
afterAppendSubHtml: 'lgAfterAppendSubHtml',
beforeOpen: 'lgBeforeOpen',
afterOpen: 'lgAfterOpen',
slideItemLoad: 'lgSlideItemLoad',
beforeSlide: 'lgBeforeSlide',
afterSlide: 'lgAfterSlide',
posterClick: 'lgPosterClick',
dragStart: 'lgDragStart',
dragMove: 'lgDragMove',
dragEnd: 'lgDragEnd',
beforeNextSlide: 'lgBeforeNextSlide',
beforePrevSlide: 'lgBeforePrevSlide',
beforeClose: 'lgBeforeClose',
afterClose: 'lgAfterClose',
rotateLeft: 'lgRotateLeft',
rotateRight: 'lgRotateRight',
flipHorizontal: 'lgFlipHorizontal',
flipVertical: 'lgFlipVertical',
autoplay: 'lgAutoplay',
autoplayStart: 'lgAutoplayStart',
autoplayStop: 'lgAutoplayStop',
};
var lightGalleryCoreSettings = {
mode: 'lg-slide',
easing: 'ease',
speed: 400,
licenseKey: '0000-0000-000-0000',
height: '100%',
width: '100%',
addClass: '',
startClass: 'lg-start-zoom',
backdropDuration: 300,
container: '',
startAnimationDuration: 400,
zoomFromOrigin: true,
hideBarsDelay: 0,
showBarsAfter: 10000,
slideDelay: 0,
supportLegacyBrowser: true,
allowMediaOverlap: false,
videoMaxSize: '1280-720',
loadYouTubePoster: true,
defaultCaptionHeight: 0,
ariaLabelledby: '',
ariaDescribedby: '',
resetScrollPosition: true,
hideScrollbar: false,
closable: true,
swipeToClose: true,
closeOnTap: true,
showCloseIcon: true,
showMaximizeIcon: false,
loop: true,
escKey: true,
keyPress: true,
trapFocus: true,
controls: true,
slideEndAnimation: true,
hideControlOnEnd: false,
mousewheel: false,
getCaptionFromTitleOrAlt: true,
appendSubHtmlTo: '.lg-sub-html',
subHtmlSelectorRelative: false,
preload: 2,
numberOfSlideItemsInDom: 10,
selector: '',
selectWithin: '',
nextHtml: '',
prevHtml: '',
index: 0,
iframeWidth: '100%',
iframeHeight: '100%',
iframeMaxWidth: '100%',
iframeMaxHeight: '100%',
download: true,
counter: true,
appendCounterTo: '.lg-toolbar',
swipeThreshold: 50,
enableSwipe: true,
enableDrag: true,
dynamic: false,
dynamicEl: [],
extraProps: [],
exThumbImage: '',
isMobile: undefined,
mobileSettings: {
controls: false,
showCloseIcon: false,
download: false,
},
plugins: [],
strings: {
closeGallery: 'Close gallery',
toggleMaximize: 'Toggle maximize',
previousSlide: 'Previous slide',
nextSlide: 'Next slide',
download: 'Download',
playVideo: 'Play video',
},
};
function initLgPolyfills() {
(function () {
if (typeof window.CustomEvent === 'function')
return false;
function CustomEvent(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: null,
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
window.CustomEvent = CustomEvent;
})();
(function () {
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
})();
}
var lgQuery = /** @class */ (function () {
function lgQuery(selector) {
this.cssVenderPrefixes = [
'TransitionDuration',
'TransitionTimingFunction',
'Transform',
'Transition',
];
this.selector = this._getSelector(selector);
this.firstElement = this._getFirstEl();
return this;
}
lgQuery.generateUUID = function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
};
lgQuery.prototype._getSelector = function (selector, context) {
if (context === void 0) { context = document; }
if (typeof selector !== 'string') {
return selector;
}
context = context || document;
var fl = selector.substring(0, 1);
if (fl === '#') {
return context.querySelector(selector);
}
else {
return context.querySelectorAll(selector);
}
};
lgQuery.prototype._each = function (func) {
if (!this.selector) {
return this;
}
if (this.selector.length !== undefined) {
[].forEach.call(this.selector, func);
}
else {
func(this.selector, 0);
}
return this;
};
lgQuery.prototype._setCssVendorPrefix = function (el, cssProperty, value) {
// prettier-ignore
var property = cssProperty.replace(/-([a-z])/gi, function (s, group1) {
return group1.toUpperCase();
});
if (this.cssVenderPrefixes.indexOf(property) !== -1) {
el.style[property.charAt(0).toLowerCase() + property.slice(1)] = value;
el.style['webkit' + property] = value;
el.style['moz' + property] = value;
el.style['ms' + property] = value;
el.style['o' + property] = value;
}
else {
el.style[property] = value;
}
};
lgQuery.prototype._getFirstEl = function () {
if (this.selector && this.selector.length !== undefined) {
return this.selector[0];
}
else {
return this.selector;
}
};
lgQuery.prototype.isEventMatched = function (event, eventName) {
var eventNamespace = eventName.split('.');
return event
.split('.')
.filter(function (e) { return e; })
.every(function (e) {
return eventNamespace.indexOf(e) !== -1;
});
};
lgQuery.prototype.attr = function (attr, value) {
if (value === undefined) {
if (!this.firstElement) {
return '';
}
return this.firstElement.getAttribute(attr);
}
this._each(function (el) {
el.setAttribute(attr, value);
});
return this;
};
lgQuery.prototype.find = function (selector) {
return $LG(this._getSelector(selector, this.selector));
};
lgQuery.prototype.first = function () {
if (this.selector && this.selector.length !== undefined) {
return $LG(this.selector[0]);
}
else {
return $LG(this.selector);
}
};
lgQuery.prototype.eq = function (index) {
return $LG(this.selector[index]);
};
lgQuery.prototype.parent = function () {
return $LG(this.selector.parentElement);
};
lgQuery.prototype.get = function () {
return this._getFirstEl();
};
lgQuery.prototype.removeAttr = function (attributes) {
var attrs = attributes.split(' ');
this._each(function (el) {
attrs.forEach(function (attr) { return el.removeAttribute(attr); });
});
return this;
};
lgQuery.prototype.wrap = function (className) {
if (!this.firstElement) {
return this;
}
var wrapper = document.createElement('div');
wrapper.className = className;
this.firstElement.parentNode.insertBefore(wrapper, this.firstElement);
this.firstElement.parentNode.removeChild(this.firstElement);
wrapper.appendChild(this.firstElement);
return this;
};
lgQuery.prototype.addClass = function (classNames) {
if (classNames === void 0) { classNames = ''; }
this._each(function (el) {
// IE doesn't support multiple arguments
classNames.split(' ').forEach(function (className) {
if (className) {
el.classList.add(className);
}
});
});
return this;
};
lgQuery.prototype.removeClass = function (classNames) {
this._each(function (el) {
// IE doesn't support multiple arguments
classNames.split(' ').forEach(function (className) {
if (className) {
el.classList.remove(className);
}
});
});
return this;
};
lgQuery.prototype.hasClass = function (className) {
if (!this.firstElement) {
return false;
}
return this.firstElement.classList.contains(className);
};
lgQuery.prototype.hasAttribute = function (attribute) {
if (!this.firstElement) {
return false;
}
return this.firstElement.hasAttribute(attribute);
};
lgQuery.prototype.toggleClass = function (className) {
if (!this.firstElement) {
return this;
}
if (this.hasClass(className)) {
this.removeClass(className);
}
else {
this.addClass(className);
}
return this;
};
lgQuery.prototype.css = function (property, value) {
var _this = this;
this._each(function (el) {
_this._setCssVendorPrefix(el, property, value);
});
return this;
};
// Need to pass separate namespaces for separate elements
lgQuery.prototype.on = function (events, listener) {
var _this = this;
if (!this.selector) {
return this;
}
events.split(' ').forEach(function (event) {
if (!Array.isArray(lgQuery.eventListeners[event])) {
lgQuery.eventListeners[event] = [];
}
lgQuery.eventListeners[event].push(listener);
_this.selector.addEventListener(event.split('.')[0], listener);
});
return this;
};
// @todo - test this
lgQuery.prototype.once = function (event, listener) {
var _this = this;
this.on(event, function () {
_this.off(event);
listener(event);
});
return this;
};
lgQuery.prototype.off = function (event) {
var _this = this;
if (!this.selector) {
return this;
}
Object.keys(lgQuery.eventListeners).forEach(function (eventName) {
if (_this.isEventMatched(event, eventName)) {
lgQuery.eventListeners[eventName].forEach(function (listener) {
_this.selector.removeEventListener(eventName.split('.')[0], listener);
});
lgQuery.eventListeners[eventName] = [];
}
});
return this;
};
lgQuery.prototype.trigger = function (event, detail) {
if (!this.firstElement) {
return this;
}
var customEvent = new CustomEvent(event.split('.')[0], {
detail: detail || null,
});
this.firstElement.dispatchEvent(customEvent);
return this;
};
// Does not support IE
lgQuery.prototype.load = function (url) {
var _this = this;
fetch(url)
.then(function (res) { return res.text(); })
.then(function (html) {
_this.selector.innerHTML = html;
});
return this;
};
lgQuery.prototype.html = function (html) {
if (html === undefined) {
if (!this.firstElement) {
return '';
}
return this.firstElement.innerHTML;
}
this._each(function (el) {
el.innerHTML = html;
});
return this;
};
lgQuery.prototype.append = function (html) {
this._each(function (el) {
if (typeof html === 'string') {
el.insertAdjacentHTML('beforeend', html);
}
else {
el.appendChild(html);
}
});
return this;
};
lgQuery.prototype.prepend = function (html) {
this._each(function (el) {
el.insertAdjacentHTML('afterbegin', html);
});
return this;
};
lgQuery.prototype.remove = function () {
this._each(function (el) {
el.parentNode.removeChild(el);
});
return this;
};
lgQuery.prototype.empty = function () {
this._each(function (el) {
el.innerHTML = '';
});
return this;
};
lgQuery.prototype.scrollTop = function (scrollTop) {
if (scrollTop !== undefined) {
document.body.scrollTop = scrollTop;
document.documentElement.scrollTop = scrollTop;
return this;
}
else {
return (window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop ||
0);
}
};
lgQuery.prototype.scrollLeft = function (scrollLeft) {
if (scrollLeft !== undefined) {
document.body.scrollLeft = scrollLeft;
document.documentElement.scrollLeft = scrollLeft;
return this;
}
else {
return (window.pageXOffset ||
document.documentElement.scrollLeft ||
document.body.scrollLeft ||
0);
}
};
lgQuery.prototype.offset = function () {
if (!this.firstElement) {
return {
left: 0,
top: 0,
};
}
var rect = this.firstElement.getBoundingClientRect();
var bodyMarginLeft = $LG('body').style().marginLeft;
// Minus body margin - https://stackoverflow.com/questions/30711548/is-getboundingclientrect-left-returning-a-wrong-value
return {
left: rect.left - parseFloat(bodyMarginLeft) + this.scrollLeft(),
top: rect.top + this.scrollTop(),
};
};
lgQuery.prototype.style = function () {
if (!this.firstElement) {
return {};
}
return (this.firstElement.currentStyle ||
window.getComputedStyle(this.firstElement));
};
// Width without padding and border even if box-sizing is used.
lgQuery.prototype.width = function () {
var style = this.style();
return (this.firstElement.clientWidth -
parseFloat(style.paddingLeft) -
parseFloat(style.paddingRight));
};
// Height without padding and border even if box-sizing is used.
lgQuery.prototype.height = function () {
var style = this.style();
return (this.firstElement.clientHeight -
parseFloat(style.paddingTop) -
parseFloat(style.paddingBottom));
};
lgQuery.eventListeners = {};
return lgQuery;
}());
function $LG(selector) {
initLgPolyfills();
return new lgQuery(selector);
}
var defaultDynamicOptions = [
'src',
'sources',
'subHtml',
'subHtmlUrl',
'html',
'video',
'poster',
'slideName',
'responsive',
'srcset',
'sizes',
'iframe',
'downloadUrl',
'download',
'width',
'facebookShareUrl',
'tweetText',
'iframeTitle',
'twitterShareUrl',
'pinterestShareUrl',
'pinterestText',
'fbHtml',
'disqusIdentifier',
'disqusUrl',
];
// Convert html data-attribute to camalcase
function convertToData(attr) {
// FInd a way for lgsize
if (attr === 'href') {
return 'src';
}
attr = attr.replace('data-', '');
attr = attr.charAt(0).toLowerCase() + attr.slice(1);
attr = attr.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
return attr;
}
var utils = {
/**
* get possible width and height from the lgSize attribute. Used for ZoomFromOrigin option
*/
getSize: function (el, container, spacing, defaultLgSize) {
if (spacing === void 0) { spacing = 0; }
var LGel = $LG(el);
var lgSize = LGel.attr('data-lg-size') || defaultLgSize;
if (!lgSize) {
return;
}
var isResponsiveSizes = lgSize.split(',');
// if at-least two viewport sizes are available
if (isResponsiveSizes[1]) {
var wWidth = window.innerWidth;
for (var i = 0; i < isResponsiveSizes.length; i++) {
var size_1 = isResponsiveSizes[i];
var responsiveWidth = parseInt(size_1.split('-')[2], 10);
if (responsiveWidth > wWidth) {
lgSize = size_1;
break;
}
// take last item as last option
if (i === isResponsiveSizes.length - 1) {
lgSize = size_1;
}
}
}
var size = lgSize.split('-');
var width = parseInt(size[0], 10);
var height = parseInt(size[1], 10);
var cWidth = container.width();
var cHeight = container.height() - spacing;
var maxWidth = Math.min(cWidth, width);
var maxHeight = Math.min(cHeight, height);
var ratio = Math.min(maxWidth / width, maxHeight / height);
return { width: width * ratio, height: height * ratio };
},
/**
* @desc Get transform value based on the imageSize. Used for ZoomFromOrigin option
* @param {jQuery Element}
* @returns {String} Transform CSS string
*/
getTransform: function (el, container, top, bottom, imageSize) {
if (!imageSize) {
return;
}
var LGel = $LG(el).find('img').first();
if (!LGel.get()) {
return;
}
var containerRect = container.get().getBoundingClientRect();
var wWidth = containerRect.width;
// using innerWidth to include mobile safari bottom bar
var wHeight = container.height() - (top + bottom);
var elWidth = LGel.width();
var elHeight = LGel.height();
var elStyle = LGel.style();
var x = (wWidth - elWidth) / 2 -
LGel.offset().left +
(parseFloat(elStyle.paddingLeft) || 0) +
(parseFloat(elStyle.borderLeft) || 0) +
$LG(window).scrollLeft() +
containerRect.left;
var y = (wHeight - elHeight) / 2 -
LGel.offset().top +
(parseFloat(elStyle.paddingTop) || 0) +
(parseFloat(elStyle.borderTop) || 0) +
$LG(window).scrollTop() +
top;
var scX = elWidth / imageSize.width;
var scY = elHeight / imageSize.height;
var transform = 'translate3d(' +
(x *= -1) +
'px, ' +
(y *= -1) +
'px, 0) scale3d(' +
scX +
', ' +
scY +
', 1)';
return transform;
},
getIframeMarkup: function (iframeWidth, iframeHeight, iframeMaxWidth, iframeMaxHeight, src, iframeTitle) {
var title = iframeTitle ? 'title="' + iframeTitle + '"' : '';
return "<div class=\"lg-video-cont lg-has-iframe\" style=\"width:" + iframeWidth + "; max-width:" + iframeMaxWidth + "; height: " + iframeHeight + "; max-height:" + iframeMaxHeight + "\">\n <iframe class=\"lg-object\" frameborder=\"0\" " + title + " src=\"" + src + "\" allowfullscreen=\"true\"></iframe>\n </div>";
},
getImgMarkup: function (index, src, altAttr, srcset, sizes, sources) {
var srcsetAttr = srcset ? "srcset=\"" + srcset + "\"" : '';
var sizesAttr = sizes ? "sizes=\"" + sizes + "\"" : '';
var imgMarkup = "<img " + altAttr + " " + srcsetAttr + " " + sizesAttr + " class=\"lg-object lg-image\" data-index=\"" + index + "\" src=\"" + src + "\" />";
var sourceTag = '';
if (sources) {
var sourceObj = typeof sources === 'string' ? JSON.parse(sources) : sources;
sourceTag = sourceObj.map(function (source) {
var attrs = '';
Object.keys(source).forEach(function (key) {
// Do not remove the first space as it is required to separate the attributes
attrs += " " + key + "=\"" + source[key] + "\"";
});
return "<source " + attrs + "></source>";
});
}
return "" + sourceTag + imgMarkup;
},
// Get src from responsive src
getResponsiveSrc: function (srcItms) {
var rsWidth = [];
var rsSrc = [];
var src = '';
for (var i = 0; i < srcItms.length; i++) {
var _src = srcItms[i].split(' ');
// Manage empty space
if (_src[0] === '') {
_src.splice(0, 1);
}
rsSrc.push(_src[0]);
rsWidth.push(_src[1]);
}
var wWidth = window.innerWidth;
for (var j = 0; j < rsWidth.length; j++) {
if (parseInt(rsWidth[j], 10) > wWidth) {
src = rsSrc[j];
break;
}
}
return src;
},
isImageLoaded: function (img) {
if (!img)
return false;
// During the onload event, IE correctly identifies any images that
// weren’t downloaded as not complete. Others should too. Gecko-based
// browsers act like NS4 in that they report this incorrectly.
if (!img.complete) {
return false;
}
// However, they do have two very useful properties: naturalWidth and
// naturalHeight. These give the true size of the image. If it failed
// to load, either of these should be zero.
if (img.naturalWidth === 0) {
return false;
}
// No other way of checking: assume it’s ok.
return true;
},
getVideoPosterMarkup: function (_poster, dummyImg, videoContStyle, playVideoString, _isVideo) {
var videoClass = '';
if (_isVideo && _isVideo.youtube) {
videoClass = 'lg-has-youtube';
}
else if (_isVideo && _isVideo.vimeo) {
videoClass = 'lg-has-vimeo';
}
else {
videoClass = 'lg-has-html5';
}
return "<div class=\"lg-video-cont " + videoClass + "\" style=\"" + videoContStyle + "\">\n <div class=\"lg-video-play-button\">\n <svg\n viewBox=\"0 0 20 20\"\n preserveAspectRatio=\"xMidYMid\"\n focusable=\"false\"\n aria-labelledby=\"" + playVideoString + "\"\n role=\"img\"\n class=\"lg-video-play-icon\"\n >\n <title>" + playVideoString + "</title>\n <polygon class=\"lg-video-play-icon-inner\" points=\"1,0 20,10 1,20\"></polygon>\n </svg>\n <svg class=\"lg-video-play-icon-bg\" viewBox=\"0 0 50 50\" focusable=\"false\">\n <circle cx=\"50%\" cy=\"50%\" r=\"20\"></circle></svg>\n <svg class=\"lg-video-play-icon-circle\" viewBox=\"0 0 50 50\" focusable=\"false\">\n <circle cx=\"50%\" cy=\"50%\" r=\"20\"></circle>\n </svg>\n </div>\n " + (dummyImg || '') + "\n <img class=\"lg-object lg-video-poster\" src=\"" + _poster + "\" />\n </div>";
},
getFocusableElements: function (container) {
var elements = container.querySelectorAll('a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])');
var visibleElements = [].filter.call(elements, function (element) {
var style = window.getComputedStyle(element);
return style.display !== 'none' && style.visibility !== 'hidden';
});
return visibleElements;
},
/**
* @desc Create dynamic elements array from gallery items when dynamic option is false
* It helps to avoid frequent DOM interaction
* and avoid multiple checks for dynamic elments
*
* @returns {Array} dynamicEl
*/
getDynamicOptions: function (items, extraProps, getCaptionFromTitleOrAlt, exThumbImage) {
var dynamicElements = [];
var availableDynamicOptions = __spreadArrays(defaultDynamicOptions, extraProps);
[].forEach.call(items, function (item) {
var dynamicEl = {};
for (var i = 0; i < item.attributes.length; i++) {
var attr = item.attributes[i];
if (attr.specified) {
var dynamicAttr = convertToData(attr.name);
var label = '';
if (availableDynamicOptions.indexOf(dynamicAttr) > -1) {
label = dynamicAttr;
}
if (label) {
dynamicEl[label] = attr.value;
}
}
}
var currentItem = $LG(item);
var alt = currentItem.find('img').first().attr('alt');
var title = currentItem.attr('title');
var thumb = exThumbImage
? currentItem.attr(exThumbImage)
: currentItem.find('img').first().attr('src');
dynamicEl.thumb = thumb;
if (getCaptionFromTitleOrAlt && !dynamicEl.subHtml) {
dynamicEl.subHtml = title || alt || '';
}
dynamicEl.alt = alt || title || '';
dynamicElements.push(dynamicEl);
});
return dynamicElements;
},
isMobile: function () {
return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
},
/**
* @desc Check the given src is video
* @param {String} src
* @return {Object} video type
* Ex:{ youtube : ["//www.youtube.com/watch?v=c0asJgSyxcY", "c0asJgSyxcY"] }
*
* @todo - this information can be moved to dynamicEl to avoid frequent calls
*/
isVideo: function (src, isHTML5VIdeo, index) {
if (!src) {
if (isHTML5VIdeo) {
return {
html5: true,
};
}
else {
console.error('lightGallery :- data-src is not provided on slide item ' +
(index + 1) +
'. Please make sure the selector property is properly configured. More info - https://www.lightgalleryjs.com/demos/html-markup/');
return;
}
}
var youtube = src.match(/\/\/(?:www\.)?youtu(?:\.be|be\.com|be-nocookie\.com)\/(?:watch\?v=|embed\/)?([a-z0-9\-\_\%]+)([\&|?][\S]*)*/i);
var vimeo = src.match(/\/\/(?:www\.)?(?:player\.)?vimeo.com\/(?:video\/)?([0-9a-z\-_]+)(.*)?/i);
var wistia = src.match(/https?:\/\/(.+)?(wistia\.com|wi\.st)\/(medias|embed)\/([0-9a-z\-_]+)(.*)/);
if (youtube) {
return {
youtube: youtube,
};
}
else if (vimeo) {
return {
vimeo: vimeo,
};
}
else if (wistia) {
return {
wistia: wistia,
};
}
},
};
// @ref - https://stackoverflow.com/questions/3971841/how-to-resize-images-proportionally-keeping-the-aspect-ratio
// @ref - https://2ality.com/2017/04/setting-up-multi-platform-packages.html
// Unique id for each gallery
var lgId = 0;
var LightGallery = /** @class */ (function () {
function LightGallery(element, options) {
this.lgOpened = false;
this.index = 0;
// lightGallery modules
this.plugins = [];
// false when lightGallery load first slide content;
this.lGalleryOn = false;
// True when a slide animation is in progress
this.lgBusy = false;
this.currentItemsInDom = [];
// Scroll top value before lightGallery is opened
this.prevScrollTop = 0;
this.bodyPaddingRight = 0;
this.isDummyImageRemoved = false;
this.dragOrSwipeEnabled = false;
this.mediaContainerPosition = {
top: 0,
bottom: 0,
};
if (!element) {
return this;
}
lgId++;
this.lgId = lgId;
this.el = element;
this.LGel = $LG(element);
this.generateSettings(options);
this.buildModules();
// When using dynamic mode, ensure dynamicEl is an array
if (this.settings.dynamic &&
this.settings.dynamicEl !== undefined &&
!Array.isArray(this.settings.dynamicEl)) {
throw 'When using dynamic mode, you must also define dynamicEl as an Array.';
}
this.galleryItems = this.getItems();
this.normalizeSettings();
// Gallery items
this.init();
this.validateLicense();
return this;
}
LightGallery.prototype.generateSettings = function (options) {
// lightGallery settings
this.settings = __assign(__assign({}, lightGalleryCoreSettings), options);
if (this.settings.isMobile &&
typeof this.settings.isMobile === 'function'
? this.settings.isMobile()
: utils.isMobile()) {
var mobileSettings = __assign(__assign({}, this.settings.mobileSettings), this.settings.mobileSettings);
this.settings = __assign(__assign({}, this.settings), mobileSettings);
}
};
LightGallery.prototype.normalizeSettings = function () {
if (this.settings.slideEndAnimation) {
this.settings.hideControlOnEnd = false;
}
if (!this.settings.closable) {
this.settings.swipeToClose = false;
}
// And reset it on close to get the correct value next time
this.zoomFromOrigin = this.settings.zoomFromOrigin;
// At the moment, Zoom from image doesn't support dynamic options
// @todo add zoomFromOrigin support for dynamic images
if (this.settings.dynamic) {
this.zoomFromOrigin = false;
}
if (!this.settings.container) {
this.settings.container = document.body;
}
// settings.preload should not be grater than $item.length
this.settings.preload = Math.min(this.settings.preload, this.galleryItems.length);
};
LightGallery.prototype.init = function () {
var _this = this;
this.addSlideVideoInfo(this.galleryItems);
this.buildStructure();
this.LGel.trigger(lGEvents.init, {
instance: this,
});
if (this.settings.keyPress) {
this.keyPress();
}
setTimeout(function () {
_this.enableDrag();
_this.enableSwipe();
_this.triggerPosterClick();
}, 50);
this.arrow();
if (this.settings.mousewheel) {
this.mousewheel();
}
if (!this.settings.dynamic) {
this.openGalleryOnItemClick();
}
};
LightGallery.prototype.openGalleryOnItemClick = function () {
var _this = this;
var _loop_1 = function (index) {
var element = this_1.items[index];
var $element = $LG(element);
// Using different namespace for click because click event should not unbind if selector is same object('this')
// @todo manage all event listners - should have namespace that represent element
var uuid = lgQuery.generateUUID();
$element
.attr('data-lg-id', uuid)
.on("click.lgcustom-item-" + uuid, function (e) {
e.preventDefault();
var currentItemIndex = _this.settings.index || index;
_this.openGallery(currentItemIndex, element);
});
};
var this_1 = this;
// Using for loop instead of using bubbling as the items can be any html element.
for (var index = 0; index < this.items.length; index++) {
_loop_1(index);
}
};
/**
* Module constructor
* Modules are build incrementally.
* Gallery should be opened only once all the modules are initialized.
* use moduleBuildTimeout to make sure this
*/
LightGallery.prototype.buildModules = function () {
var _this = this;
this.settings.plugins.forEach(function (plugin) {
_this.plugins.push(new plugin(_this, $LG));
});
};
LightGallery.prototype.validateLicense = function () {
if (!this.settings.licenseKey) {
console.error('Please provide a valid license key');
}
else if (this.settings.licenseKey === '0000-0000-000-0000') {
console.warn("lightGallery: " + this.settings.licenseKey + " license key is not valid for production use");
}
};
LightGallery.prototype.getSlideItem = function (index) {
return $LG(this.getSlideItemId(index));
};
LightGallery.prototype.getSlideItemId = function (index) {
return "#lg-item-" + this.lgId + "-" + index;
};
LightGallery.prototype.getIdName = function (id) {
return id + "-" + this.lgId;
};
LightGallery.prototype.getElementById = function (id) {
return $LG("#" + this.getIdName(id));
};
LightGallery.prototype.manageSingleSlideClassName = function () {
if (this.galleryItems.length < 2) {
this.outer.addClass('lg-single-item');
}
else {
this.outer.removeClass('lg-single-item');
}
};
LightGallery.prototype.buildStructure = function () {
var _this = this;
var container = this.$container && this.$container.get();
if (container) {
return;
}
var controls = '';
var subHtmlCont = '';
// Create controls
if (this.settings.controls) {
controls = "<button type=\"button\" id=\"" + this.getIdName('lg-prev') + "\" aria-label=\"" + this.settings.strings['previousSlide'] + "\" class=\"lg-prev lg-icon\"> " + this.settings.prevHtml + " </button>\n <button type=\"button\" id=\"" + this.getIdName('lg-next') + "\" aria-label=\"" + this.settings.strings['nextSlide'] + "\" class=\"lg-next lg-icon\"> " + this.settings.nextHtml + " </button>";
}
if (this.settings.appendSubHtmlTo !== '.lg-item') {
subHtmlCont =
'<div class="lg-sub-html" role="status" aria-live="polite"></div>';
}
var addClasses = '';
if (this.settings.allowMediaOverlap) {
// Do not remove space before last single quote
addClasses += 'lg-media-overlap ';
}
var ariaLabelledby = this.settings.ariaLabelledby
? 'aria-labelledby="' + this.settings.ariaLabelledby + '"'
: '';
var ariaDescribedby = this.settings.ariaDescribedby
? 'aria-describedby="' + this.settings.ariaDescribedby + '"'
: '';
var containerClassName = "lg-container " + this.settings.addClass + " " + (document.body !== this.settings.container ? 'lg-inline' : '');
var closeIcon = this.settings.closable && this.settings.showCloseIcon
? "<button type=\"button\" aria-label=\"" + this.settings.strings['closeGallery'] + "\" id=\"" + this.getIdName('lg-close') + "\" class=\"lg-close lg-icon\"></button>"
: '';
var maximizeIcon = this.settings.showMaximizeIcon
? "<button type=\"button\" aria-label=\"" + this.settings.strings['toggleMaximize'] + "\" id=\"" + this.getIdName('lg-maximize') + "\" class=\"lg-maximize lg-icon\"></button>"
: '';
var template = "\n <div class=\"" + containerClassName + "\" id=\"" + this.getIdName('lg-container') + "\" tabindex=\"-1\" aria-modal=\"true\" " + ariaLabelledby + " " + ariaDescribedby + " role=\"dialog\"\n >\n <div id=\"" + this.getIdName('lg-backdrop') + "\" class=\"lg-backdrop\"></div>\n\n <div id=\"" + this.getIdName('lg-outer') + "\" class=\"lg-outer lg-use-css3 lg-css3 lg-hide-items " + addClasses + " \">\n\n <div id=\"" + this.getIdName('lg-content') + "\" class=\"lg-content\">\n <div id=\"" + this.getIdName('lg-inner') + "\" class=\"lg-inner\">\n </div>\n " + controls + "\n </div>\n <div id=\"" + this.getIdName('lg-toolbar') + "\" class=\"lg-toolbar lg-group\">\n " + maximizeIcon + "\n " + closeIcon + "\n </div>\n " + (this.settings.appendSubHtmlTo === '.lg-outer'
? subHtmlCont
: '') + "\n <div id=\"" + this.getIdName('lg-components') + "\" class=\"lg-components\">\n " + (this.settings.appendSubHtmlTo === '.lg-sub-html'
? subHtmlCont
: '') + "\n </div>\n </div>\n </div>\n ";
$LG(this.settings.container).append(template);
if (document.body !== this.settings.container) {
$LG(this.settings.container).css('position', 'relative');
}
this.outer = this.getElementById('lg-outer');
this.$lgComponents = this.getElementById('lg-components');
this.$backdrop = this.getElementById('lg-backdrop');
this.$container = this.getElementById('lg-container');
this.$inner = this.getElementById('lg-inner');
this.$content = this.getElementById('lg-content');
this.$toolbar = this.getElementById('lg-toolbar');
this.$backdrop.css('transition-duration', this.settings.backdropDuration + 'ms');
var outerClassNames = this.settings.mode + " ";
this.manageSingleSlideClassName();
if (this.settings.enableDrag) {
outerClassNames += 'lg-grab ';
}
this.outer.addClass(outerClassNames);
this.$inner.css('transition-timing-function', this.settings.easing);
this.$inner.css('transition-duration', this.settings.speed + 'ms');
if (this.settings.download) {
this.$toolbar.append("<a id=\"" + this.getIdName('lg-download') + "\" target=\"_blank\" rel=\"noopener\" aria-label=\"" + this.settings.strings['download'] + "\" download class=\"lg-download lg-icon\"></a>");
}
this.counter();
$LG(window).on("resize.lg.global" + this.lgId + " orientationchange.lg.global" + this.lgId, function () {
_this.refreshOnResize();
});
this.hideBars();
this.manageCloseGallery();
this.toggleMaximize();
this.initModules();
};
LightGallery.prototype.refreshOnResize = function () {
if (this.lgOpened) {
var currentGalleryItem = this.galleryItems[this.index];
var __slideVideoInfo = currentGalleryItem.__slideVideoInfo;
this.mediaContainerPosition = this.getMediaContainerPosition();
var _a = this.mediaContainerPosition, top_1 = _a.top, bottom = _a.bottom;
this.currentImageSize = utils.getSize(this.items[this.index], this.outer, top_1 + bottom, __slideVideoInfo && this.settings.videoMaxSize);
if (__slideVideoInfo) {
this.resizeVideoSlide(this.index, this.currentImageSize);
}
if (this.zoomFromOrigin && !this.isDummyImageRemoved) {
var imgStyle = this.getDummyImgStyles(this.currentImageSize);
this.outer
.find('.lg-current .lg-dummy-img')
.first()
.attr('style', imgStyle);
}
this.LGel.trigger(lGEvents.containerResize);
}
};
LightGallery.prototype.resizeVideoSlide = function (index, imageSize) {
var lgVideoStyle = this.getVideoContStyle(imageSize);
var currentSlide = this.getSlideItem(index);
currentSlide.find('.lg-video-cont').attr('style', lgVideoStyle);
};
/**
* Update slides dynamically.
* Add, edit or delete slides dynamically when lightGallery is opened.
* Modify the current gallery items and pass it via updateSlides method
* @note
* - Do not mutate existing lightGallery items directly.
* - Always pass new list of gallery items
* - You need to take care of thumbnails outside the gallery if any
* - user this method only if you want to update slides when the gallery is opened. Otherwise, use `refresh()` method.
* @param items Gallery items
* @param index After the update operation, which slide gallery should navigate to
* @category lGPublicMethods
* @example
* const plugin = lightGallery();
*
* // Adding slides dynamically
* let galleryItems = [
* // Access existing lightGallery items
* // galleryItems are automatically generated internally from the gallery HTML markup
* // or directly from galleryItems when dynamic gallery is used
* ...plugin.galleryItems,
* ...[
* {
* src: 'img/img-1.png',
* thumb: 'img/thumb1.png',
* },
* ],
* ];
* plugin.updateSlides(
* galleryItems,
* plugin.index,
* );
*
*
* // Remove slides dynamically
* galleryItems = JSON.parse(
* JSON.stringify(updateSlideInstance.galleryItems),
* );
* galleryItems.shift();
* updateSlideInstance.updateSlides(galleryItems, 1);
* @see <a href="/demos/update-slides/">Demo</a>
*/
LightGallery.prototype.updateSlides = function (items, index) {
if (this.index > items.length - 1) {
this.index = items.length - 1;
}
if (items.length === 1) {
this.index = 0;
}
if (!items.length) {
this.closeGallery();
return;
}
var currentSrc = this.galleryItems[index].src;
this.galleryItems = items;
this.updateControls();
this.$inner.empty();
this.currentItemsInDom = [];
var _index = 0;
// Find the current index based on source value of the slide
this.galleryItems.some(function (galleryItem, itemIndex) {
if (galleryItem.src === currentSrc) {
_index = itemIndex;
return true;
}
return false;
});
this.currentItemsInDom = this.organizeSlideItems(_index, -1);
this.loadContent(_index, true);
this.getSlideItem(_index).addClass('lg-current');
this.index = _index;
this.updateCurrentCounter(_index);
this.LGel.trigger(lGEvents.updateSlides);
};
// Get gallery items based on multiple conditions
LightGallery.prototype.getItems = function () {
// Gallery items
this.items = [];
if (!this.settings.dynamic) {
if (this.settings.selector === 'this') {
this.items.push(this.el);
}
else if (this.settings.selector) {
if (typeof this.settings.selector === 'string') {
if (this.settings.selectWithin) {
var selectWithin = $LG(this.settings.selectWithin);
this.items = selectWithin
.find(this.settings.selector)
.get();
}
else {
this.items = this.el.querySelectorAll(this.settings.selector);
}
}
else {
this.items = this.settings.selector;
}
}
else {
this.items = this.el.children;
}
return utils.getDynamicOptions(this.items, this.settings.extraProps, this.settings.getCaptionFromTitleOrAlt, this.settings.exThumbImage);
}
else {
return this.settings.dynamicEl || [];
}
};
LightGallery.prototype.shouldHideScrollbar = function () {
return (this.settings.hideScrollbar &&
document.body === this.settings.container);
};
LightGallery.prototype.hideScrollbar = function () {
if (!this.shouldHideScrollbar()) {
return;
}
this.bodyPaddingRight = parseFloat($LG('body').style().paddingRight);
var bodyRect = document.documentElement.getBoundingClientRect();
var scrollbarWidth = window.innerWidth - bodyRect.width;
$LG(document.body).css('padding-right', scrollbarWidth + this.bodyPaddingRight + 'px');
$LG(document.body).addClass('lg-overlay-open');
};
LightGallery.prototype.resetScrollBar = function () {
if (!this.shouldHideScrollbar()) {
return;
}
$LG(document.body).css('padding-right', this.bodyPaddingRight + 'px');
$LG(document.body).removeClass('lg-overlay-open');
};
/**
* Open lightGallery.
* Open gallery with specific slide by passing index of the slide as parameter.
* @category lGPublicMethods
* @param {Number} index - index of the slide
* @param {HTMLElement} element - Which image lightGallery should zoom from
*
* @example
* const $dynamicGallery = document.getElementById('dynamic-gallery-demo');
* const dynamicGallery = lightGallery($dynamicGallery, {
* dynamic: true,
* dynamicEl: [
* {
* src: 'img/1.jpg',
* thumb: 'img/thumb-1.jpg',
* subHtml: '<h4>Image 1 title</h4><p>Image 1 descriptions.</p>',
* },
* ...
* ],
* });
* $dynamicGallery.addEventListener('click', function () {
* // Starts with third item.(Optional).
* // This is useful if you want use dynamic mode with
* // custom thumbnails (thumbnails outside gallery),
* dynamicGallery.openGallery(2);
* });
*
*/
LightGallery.prototype.openGallery = function (index, element) {
var _this = this;
if (index === void 0) { index = this.settings.index; }
// prevent accidental double execution
if (this.lgOpened)
return;
this.lgOpened = true;
this.outer.removeClass('lg-hide-items');
this.hideScrollbar();
// Add display block, but still has opacity 0
this.$container.addClass('lg-show');
var itemsToBeInsertedToDom = this.getItemsToBeInsertedToDom(index, index);
this.currentItemsInDom = itemsToBeInsertedToDom;
var items = '';
itemsToBeInsertedToDom.for