|
|
@@ -0,0 +1,532 @@ |
|
|
|
(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|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)); |
|
|
|
self.captions.push(element.getAttribute(options.captionAttribute)); |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
prepareItem: function(position, callback) { |
|
|
|
|
|
|
|
var self = this; |
|
|
|
var url = this.items[position]; |
|
|
|
|
|
|
|
this.loading(true); |
|
|
|
|
|
|
|
if (this.options.videoRegex.test(url)) { |
|
|
|
|
|
|
|
callback.call(self, parseHtml( |
|
|
|
'<div class="slbIframeCont"><iframe class="slbIframe" frameborder="0" allowfullscreen src="' + url + '"></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; |
|
|
|
|
|
|
|
})); |