(function (root, factory) {
if (typeof define === "function" && define.amd) {
define([], factory);
} else if (typeof module === "object" && module.exports) {
module.exports = factory();
} else {
root.SimpleLightbox = factory();
}
})(this, function () {
function assign(target) {
for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i];
if (obj) {
for (var key in obj) {
obj.hasOwnProperty(key) && (target[key] = obj[key]);
}
}
}
return target;
}
function addClass(element, className) {
if (element && className) {
element.className += " " + className;
}
}
function removeClass(element, className) {
if (element && className) {
element.className = element.className
.replace(new RegExp("(\\s|^)" + className + "(\\s|$)"), " ")
.trim();
}
}
function parseHtml(html) {
var div = document.createElement("div");
div.innerHTML = html.trim();
return div.childNodes[0];
}
function matches(el, selector) {
return (el.matches || el.matchesSelector || el.msMatchesSelector).call(
el,
selector
);
}
function getWindowHeight() {
return "innerHeight" in window
? window.innerHeight
: document.documentElement.offsetHeight;
}
function SimpleLightbox(options) {
this.init.apply(this, arguments);
}
SimpleLightbox.defaults = {
// add custom classes to lightbox elements
elementClass: "",
elementLoadingClass: "slbLoading",
htmlClass: "slbActive",
closeBtnClass: "",
nextBtnClass: "",
prevBtnClass: "",
loadingTextClass: "",
// customize / localize controls captions
closeBtnCaption: "Close",
nextBtnCaption: "Next",
prevBtnCaption: "Previous",
loadingCaption: "Loading...",
bindToItems: true, // set click event handler to trigger lightbox on provided $items
closeOnOverlayClick: true,
closeOnEscapeKey: true,
nextOnImageClick: true,
showCaptions: true,
captionAttribute: "title", // choose data source for library to glean image caption from
urlAttribute: "href", // where to expect large image
startAt: 0, // start gallery at custom index
loadingTimeout: 100, // time after loading element will appear
appendTarget: "body", // append elsewhere if needed
beforeSetContent: null, // convenient hooks for extending library behavoiur
beforeClose: null,
afterClose: null,
beforeDestroy: null,
afterDestroy: null,
videoRegex: new RegExp(/youtube.com|youtu.be|vimeo.com/), // regex which tests load url for iframe content
};
assign(SimpleLightbox.prototype, {
init: function (options) {
options = this.options = assign({}, SimpleLightbox.defaults, options);
var self = this;
var elements;
if (options.$items) {
elements = options.$items.get();
}
if (options.elements) {
elements = [].slice.call(
typeof options.elements === "string"
? document.querySelectorAll(options.elements)
: options.elements
);
}
this.eventRegistry = { lightbox: [], thumbnails: [] };
this.items = [];
this.captions = [];
if (elements) {
elements.forEach(function (element, index) {
self.items.push(element.getAttribute(options.urlAttribute));
//look for a caption in various spots
var caption = element.getAttribute(options.captionAttribute);
if (!caption) {
//try to find the title attribute on the underlying image
caption = element
.querySelector("img")
?.getAttribute(options.captionAttribute);
if (!caption) {
//try to find an attached fig caption
caption =
element.parentElement?.querySelector("figcaption")?.textContent;
}
}
self.captions.push(caption);
if (options.bindToItems) {
self.addEvent(
element,
"click",
function (e) {
e.preventDefault();
self.showPosition(index);
},
"thumbnails"
);
}
});
}
if (options.items) {
this.items = options.items;
}
if (options.captions) {
this.captions = options.captions;
}
},
addEvent: function (element, eventName, callback, scope) {
this.eventRegistry[scope || "lightbox"].push({
element: element,
eventName: eventName,
callback: callback,
});
element.addEventListener(eventName, callback);
return this;
},
removeEvents: function (scope) {
this.eventRegistry[scope].forEach(function (item) {
item.element.removeEventListener(item.eventName, item.callback);
});
this.eventRegistry[scope] = [];
return this;
},
next: function () {
return this.showPosition(this.currentPosition + 1);
},
prev: function () {
return this.showPosition(this.currentPosition - 1);
},
normalizePosition: function (position) {
if (position >= this.items.length) {
position = 0;
} else if (position < 0) {
position = this.items.length - 1;
}
return position;
},
showPosition: function (position) {
var newPosition = this.normalizePosition(position);
if (typeof this.currentPosition !== "undefined") {
this.direction = newPosition > this.currentPosition ? "next" : "prev";
}
this.currentPosition = newPosition;
return this.setupLightboxHtml()
.prepareItem(this.currentPosition, this.setContent)
.show();
},
loading: function (on) {
var self = this;
var options = this.options;
if (on) {
this.loadingTimeout = setTimeout(function () {
addClass(self.$el, options.elementLoadingClass);
self.$content.innerHTML =
'<p class="slbLoadingText ' +
options.loadingTextClass +
'">' +
options.loadingCaption +
"</p>";
self.show();
}, options.loadingTimeout);
} else {
removeClass(this.$el, options.elementLoadingClass);
clearTimeout(this.loadingTimeout);
}
},
getVideoURL: function (url) {
var frm = "//_URL_";
var converts = [
{
rx: /^(?:https?:)?\/\/(?:www\.)?vimeo\.com\/([^\?&"]+).*$/g,
tmpl: frm.replace("_URL_", "player.vimeo.com/video/$1"),
},
{
rx: /^.*(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=|embed\/|v\/|user\/.+\/)?([^\?&"]+).*$/g,
tmpl: frm.replace("_URL_", "www.youtube.com/embed/$1"),
},
{
rx: /^.*(?:https?:\/\/)?(?:www\.)?(?:youtube-nocookie\.com)\/(?:watch\?v=|embed\/|v\/|user\/.+\/)?([^\?&"]+).*$/g,
tmpl: frm.replace("_URL_", "www.youtube-nocookie.com/embed/$1"),
},
];
for (var i = 0; i < converts.length; i++) {
if (converts[i].rx.test(url)) {
return url.replace(converts[i].rx, converts[i].tmpl);
}
}
return url;
},
prepareItem: function (position, callback) {
var self = this;
var url = this.items[position];
this.loading(true);
if (this.options.videoRegex.test(url)) {
var videoURL = this.getVideoURL(url);
callback.call(
self,
parseHtml(
'<div class="slbIframeCont"><iframe class="slbIframe" frameborder="0" allowfullscreen src="' +
videoURL +
'"></iframe></div>'
)
);
} else {
var $imageCont = parseHtml(
'<div class="slbImageWrap"><img class="slbImage" src="' +
url +
'" /></div>'
);
this.$currentImage = $imageCont.querySelector(".slbImage");
if (this.options.showCaptions && this.captions[position]) {
$imageCont.appendChild(
parseHtml(
'<div class="slbCaption">' + this.captions[position] + "</div>"
)
);
}
this.loadImage(url, function () {
self.setImageDimensions();
callback.call(self, $imageCont);
self.loadImage(
self.items[self.normalizePosition(self.currentPosition + 1)]
);
});
}
return this;
},
loadImage: function (url, callback) {
if (!this.options.videoRegex.test(url)) {
var image = new Image();
callback && (image.onload = callback);
image.src = url;
}
},
setupLightboxHtml: function () {
var o = this.options;
if (!this.$el) {
this.$el = parseHtml(
'<div class="slbElement ' +
o.elementClass +
'">' +
'<div class="slbOverlay"></div>' +
'<div class="slbWrapOuter">' +
'<div class="slbWrap">' +
'<div class="slbContentOuter">' +
'<div class="slbContent"></div>' +
'<button type="button" title="' +
o.closeBtnCaption +
'" class="slbCloseBtn ' +
o.closeBtnClass +
'">×</button>' +
(this.items.length > 1
? '<div class="slbArrows">' +
'<button type="button" title="' +
o.prevBtnCaption +
'" class="prev slbArrow' +
o.prevBtnClass +
'">' +
o.prevBtnCaption +
"</button>" +
'<button type="button" title="' +
o.nextBtnCaption +
'" class="next slbArrow' +
o.nextBtnClass +
'">' +
o.nextBtnCaption +
"</button>" +
"</div>"
: "") +
"</div>" +
"</div>" +
"</div>" +
"</div>"
);
this.$content = this.$el.querySelector(".slbContent");
}
this.$content.innerHTML = "";
return this;
},
show: function () {
if (!this.modalInDom) {
document.querySelector(this.options.appendTarget).appendChild(this.$el);
addClass(document.documentElement, this.options.htmlClass);
this.setupLightboxEvents();
this.modalInDom = true;
}
return this;
},
setContent: function (content) {
var $content = typeof content === "string" ? parseHtml(content) : content;
this.loading(false);
this.setupLightboxHtml();
removeClass(this.$content, "slbDirectionNext");
removeClass(this.$content, "slbDirectionPrev");
if (this.direction) {
addClass(
this.$content,
this.direction === "next" ? "slbDirectionNext" : "slbDirectionPrev"
);
}
if (this.options.beforeSetContent) {
this.options.beforeSetContent($content, this);
}
this.$content.appendChild($content);
return this;
},
setImageDimensions: function () {
if (this.$currentImage) {
this.$currentImage.style.maxHeight = getWindowHeight() + "px";
}
},
setupLightboxEvents: function () {
var self = this;
if (this.eventRegistry.lightbox.length) {
return this;
}
this.addEvent(this.$el, "click", function (e) {
var $target = e.target;
if (
matches($target, ".slbCloseBtn") ||
(self.options.closeOnOverlayClick && matches($target, ".slbWrap"))
) {
self.close();
} else if (matches($target, ".slbArrow")) {
matches($target, ".next") ? self.next() : self.prev();
} else if (
self.options.nextOnImageClick &&
self.items.length > 1 &&
matches($target, ".slbImage")
) {
self.next();
}
})
.addEvent(document, "keyup", function (e) {
self.options.closeOnEscapeKey && e.keyCode === 27 && self.close();
if (self.items.length > 1) {
(e.keyCode === 39 || e.keyCode === 68) && self.next();
(e.keyCode === 37 || e.keyCode === 65) && self.prev();
}
})
.addEvent(window, "resize", function () {
self.setImageDimensions();
});
return this;
},
close: function () {
if (this.modalInDom) {
this.runHook("beforeClose");
this.removeEvents("lightbox");
this.$el && this.$el.parentNode.removeChild(this.$el);
removeClass(document.documentElement, this.options.htmlClass);
this.modalInDom = false;
this.runHook("afterClose");
}
this.direction = undefined;
this.currentPosition = this.options.startAt;
},
destroy: function () {
this.close();
this.runHook("beforeDestroy");
this.removeEvents("thumbnails");
this.runHook("afterDestroy");
},
runHook: function (name) {
this.options[name] && this.options[name](this);
},
});
SimpleLightbox.open = function (options) {
var instance = new SimpleLightbox(options);
return options.content
? instance.setContent(options.content).show()
: instance.showPosition(instance.options.startAt);
};
SimpleLightbox.registerAsJqueryPlugin = function ($) {
$.fn.simpleLightbox = function (options) {
var lightboxInstance;
var $items = this;
return this.each(function () {
if (!$.data(this, "simpleLightbox")) {
lightboxInstance =
lightboxInstance ||
new SimpleLightbox($.extend({}, options, { $items: $items }));
$.data(this, "simpleLightbox", lightboxInstance);
}
});
};
$.SimpleLightbox = SimpleLightbox;
};
if (typeof window !== "undefined" && window.jQuery) {
SimpleLightbox.registerAsJqueryPlugin(window.jQuery);
}
return SimpleLightbox;
});