(function (factory) {
if(typeof module === "object" && typeof module.exports === "object") {
module.exports = factory(require("jquery"), window, document);
} else {
factory(jQuery, window, document);
}
}(function($, window, document, undefined) {
var calls = 0;
function Chocolat(element, settings) {
var that = this;
this.settings = settings;
this.elems = {};
this.element = element;
this._cssClasses = [
'chocolat-open',
'chocolat-in-container',
'chocolat-cover',
'chocolat-zoomable',
'chocolat-zoomed'
];
if (!this.settings.setTitle && element.data('chocolat-title')) {
this.settings.setTitle = element.data('chocolat-title');
}
this.element.find(this.settings.imageSelector).each(function () {
that.settings.images.push({
title : $(this).attr('title'),
src : $(this).attr(that.settings.imageSource),
height : false,
width : false
});
});
this.element.find(this.settings.imageSelector).each(function (i) {
$(this).off('click.chocolat').on('click.chocolat', function(e){
that.init(i);
e.preventDefault();
});
});
return this;
}
$.extend(Chocolat.prototype, {
init : function(i) {
if (!this.settings.initialized) {
this.setDomContainer();
this.markup();
this.events();
this.settings.lastImage = this.settings.images.length - 1;
this.settings.initialized = true;
}
this.settings.afterInitialize.call(this);
return this.load(i);
},
preload : function(i) {
var def = $.Deferred();
if (typeof this.settings.images[i] === 'undefined') {
return;
}
var imgLoader = new Image();
imgLoader.onload = function() { def.resolve(imgLoader); };
imgLoader.src = this.settings.images[i].src;
return def;
},
load : function(i) {
var that = this;
if (this.settings.fullScreen) {
this.openFullScreen();
}
if (this.settings.currentImage === i) {
return;
}
this.elems.overlay.fadeIn(this.settings.duration);
this.elems.wrapper.fadeIn(this.settings.duration);
this.elems.domContainer.addClass('chocolat-open');
this.settings.timer = setTimeout(function(){
if (typeof that.elems != 'undefined') {
$.proxy(that.elems.loader.fadeIn(), that);
}
}, this.settings.duration);
var deferred = this.preload(i)
.then(function (imgLoader) {
return that.place(i, imgLoader);
})
.then(function (imgLoader) {
return that.appear(i);
})
.then(function (imgLoader) {
that.zoomable();
that.settings.afterImageLoad.call(that);
});
var nextIndex = i + 1;
if (typeof this.settings.images[nextIndex] != 'undefined') {
this.preload(nextIndex);
}
return deferred;
},
place : function(i, imgLoader) {
var that = this;
var fitting;
this.settings.currentImage = i;
this.description();
this.pagination();
this.arrows();
this.storeImgSize(imgLoader, i);
fitting = this.fit(i, that.elems.wrapper);
return this.center(
fitting.width,
fitting.height,
fitting.left,
fitting.top,
0
);
},
center : function(width, height, left, top, duration) {
return this.elems.content
.css('overflow', 'visible')
.animate({
'width' :width,
'height' :height,
'left' :left,
'top' :top
}, duration)
.promise();
},
appear : function(i) {
var that = this;
clearTimeout(this.settings.timer);
this.elems.loader.stop().fadeOut(300, function() {
that.elems.img
.attr('src', that.settings.images[i].src);
});
},
fit : function(i, container) {
var height;
var width;
var imgHeight = this.settings.images[i].height;
var imgWidth = this.settings.images[i].width;
var holderHeight = $(container).height();
var holderWidth = $(container).width();
var holderOutMarginH = this.getOutMarginH();
var holderOutMarginW = this.getOutMarginW();
var holderGlobalWidth = holderWidth-holderOutMarginW;
var holderGlobalHeight = holderHeight-holderOutMarginH;
var holderGlobalRatio = (holderGlobalHeight / holderGlobalWidth);
var holderRatio = (holderHeight / holderWidth);
var imgRatio = (imgHeight / imgWidth);
if (this.settings.imageSize == 'cover') {
if (imgRatio < holderRatio) {
height = holderHeight;
width = height / imgRatio;
}
else {
width = holderWidth;
height = width * imgRatio;
}
}
else if (this.settings.imageSize == 'native') {
height = imgHeight;
width = imgWidth;
}
else {
if (imgRatio > holderGlobalRatio) {
height = holderGlobalHeight;
width = height / imgRatio;
}
else {
width = holderGlobalWidth;
height = width * imgRatio;
}
if (this.settings.imageSize === 'default' && (width >= imgWidth || height >= imgHeight)) {
width = imgWidth;
height = imgHeight;
}
}
return {
'height' : height,
'width' : width,
'top' : (holderHeight - height)/2,
'left' : (holderWidth - width)/2
};
},
change : function(signe) {
this.zoomOut(0);
this.zoomable();
var requestedImage = this.settings.currentImage + parseInt(signe);
if (requestedImage > this.settings.lastImage) {
if (this.settings.loop) {
return this.load(0);
}
}
else if (requestedImage < 0) {
if (this.settings.loop) {
return this.load(this.settings.lastImage);
}
}
else {
return this.load(requestedImage);
}
},
arrows: function() {
if (this.settings.loop) {
$([this.elems.left[0],this.elems.right[0]])
.addClass('active');
}
else if (this.settings.linkImages) {
// right
if (this.settings.currentImage == this.settings.lastImage) {
this.elems.right.removeClass('active');
}
else {
this.elems.right.addClass('active');
}
// left
if (this.settings.currentImage === 0) {
this.elems.left.removeClass('active');
}
else {
this.elems.left.addClass('active');
}
}
else {
$([this.elems.left[0],this.elems.right[0]])
.removeClass('active');
}
},
description : function() {
var that = this;
this.elems.description
.html(that.settings.images[that.settings.currentImage].title);
},
pagination : function() {
var that = this;
var last = this.settings.lastImage + 1;
var position = this.settings.currentImage + 1;
this.elems.pagination
.html(position + ' ' + that.settings.separator2 + last);
},
storeImgSize : function(img, i) {
if (typeof img === 'undefined') {
return;
}
if (!this.settings.images[i].height || !this.settings.images[i].width) {
this.settings.images[i].height = img.height;
this.settings.images[i].width = img.width;
}
},
close : function() {
if (this.settings.fullscreenOpen) {
this.exitFullScreen();
return;
}
var els = [
this.elems.overlay[0],
this.elems.loader[0],
this.elems.wrapper[0]
];
var that = this;
var def = $.when($(els).fadeOut(200)).done(function () {
that.elems.domContainer.removeClass('chocolat-open');
});
this.settings.currentImage = false;
return def;
},
destroy : function() {
this.element.removeData();
this.element.find(this.settings.imageSelector).off('click.chocolat');
if (!this.settings.initialized) {
return;
}
if (this.settings.fullscreenOpen) {
this.exitFullScreen();
}
this.settings.currentImage = false;
this.settings.initialized = false;
this.elems.domContainer.removeClass(this._cssClasses.join(' '));
this.elems.wrapper.remove();
},
getOutMarginW : function() {
var left = this.elems.left.outerWidth(true);
var right = this.elems.right.outerWidth(true);
return left + right;
},
getOutMarginH : function() {
return this.elems.top.outerHeight(true) + this.elems.bottom.outerHeight(true);
},
markup : function() {
this.elems.domContainer.addClass('chocolat-open ' + this.settings.className);
if (this.settings.imageSize == 'cover') {
this.elems.domContainer.addClass('chocolat-cover');
}
if (this.settings.container !== window) {
this.elems.domContainer.addClass('chocolat-in-container');
}
this.elems.wrapper = $('
', {
'class' : 'chocolat-wrapper',
'id' : 'chocolat-content-' + this.settings.setIndex
}).appendTo(this.elems.domContainer);
this.elems.overlay = $('', {
'class' : 'chocolat-overlay'
}).appendTo(this.elems.wrapper);
this.elems.loader = $('', {
'class' : 'chocolat-loader'
}).appendTo(this.elems.wrapper);
this.elems.content = $('', {
'class' : 'chocolat-content',
}).appendTo(this.elems.wrapper);
this.elems.img = $('
', {
'class' : 'chocolat-img',
'src' : ''
}).appendTo(this.elems.content);
this.elems.top = $('', {
'class' : 'chocolat-top'
}).appendTo(this.elems.wrapper);
this.elems.left = $('', {
'class' : 'chocolat-left'
}).appendTo(this.elems.wrapper);
this.elems.right = $('', {
'class' : 'chocolat-right'
}).appendTo(this.elems.wrapper);
this.elems.bottom = $('', {
'class' : 'chocolat-bottom'
}).appendTo(this.elems.wrapper);
this.elems.close = $('', {
'class' : 'chocolat-close'
}).appendTo(this.elems.top);
this.elems.fullscreen = $('', {
'class' : 'chocolat-fullscreen'
}).appendTo(this.elems.bottom);
this.elems.description = $('', {
'class' : 'chocolat-description'
}).appendTo(this.elems.bottom);
this.elems.pagination = $('', {
'class' : 'chocolat-pagination'
}).appendTo(this.elems.bottom);
this.elems.setTitle = $('', {
'class' : 'chocolat-set-title',
'html' : this.settings.setTitle
}).appendTo(this.elems.bottom);
this.settings.afterMarkup.call(this);
},
openFullScreen : function() {
var wrapper = this.elems.wrapper[0];
if (wrapper.requestFullscreen) {
this.settings.fullscreenOpen = true;
wrapper.requestFullscreen();
}
else if (wrapper.mozRequestFullScreen) {
this.settings.fullscreenOpen = true;
wrapper.mozRequestFullScreen();
}
else if (wrapper.webkitRequestFullscreen) {
this.settings.fullscreenOpen = true;
wrapper.webkitRequestFullscreen();
}
else if (wrapper.msRequestFullscreen) {
wrapper.msRequestFullscreen();
this.settings.fullscreenOpen = true;
}
else {
this.settings.fullscreenOpen = false;
}
},
exitFullScreen : function() {
if (document.exitFullscreen) {
document.exitFullscreen();
this.settings.fullscreenOpen = false;
}
else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
this.settings.fullscreenOpen = false;
}
else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
this.settings.fullscreenOpen = false;
}
else if (document.msExitFullscreen) {
document.msExitFullscreen();
this.settings.fullscreenOpen = false;
}
else {
this.settings.fullscreenOpen = true;
}
},
events : function() {
var that = this;
$(document).off('keydown.chocolat').on('keydown.chocolat', function(e) {
if (that.settings.initialized) {
if (e.keyCode == 37) {
that.change(-1);
}
else if (e.keyCode == 39) {
that.change(1);
}
else if (e.keyCode == 27) {
that.close();
}
}
});
// this.elems.wrapper.find('.chocolat-img')
// .off('click.chocolat')
// .on('click.chocolat', function(e) {
// var currentImage = that.settings.images[that.settings.currentImage];
// if(currentImage.width > $(that.elems.wrapper).width() || currentImage.height > $(that.elems.wrapper).height() ){
// that.toggleZoom(e);
// }
// });
this.elems.wrapper.find('.chocolat-right')
.off('click.chocolat')
.on('click.chocolat', function() {
that.change(+1);
});
this.elems.wrapper.find('.chocolat-left')
.off('click.chocolat')
.on('click.chocolat', function() {
return that.change(-1);
});
$([this.elems.overlay[0], this.elems.close[0]])
.off('click.chocolat')
.on('click.chocolat', function() {
return that.close();
});
this.elems.fullscreen
.off('click.chocolat')
.on('click.chocolat', function() {
if (that.settings.fullscreenOpen) {
that.exitFullScreen();
return;
}
that.openFullScreen();
});
if (that.settings.backgroundClose) {
this.elems.overlay
.off('click.chocolat')
.on('click.chocolat', function() {
return that.close();
});
}
this.elems.wrapper
.off('click.chocolat')
.on('click.chocolat', function(e) {
return that.zoomOut(e);
});
this.elems.wrapper.find('.chocolat-img')
.off('click.chocolat')
.on('click.chocolat', function(e) {
if (that.settings.initialZoomState === null && that.elems.domContainer.hasClass('chocolat-zoomable')) {
e.stopPropagation();
return that.zoomIn(e);
}
});
this.elems.wrapper.mousemove(function( e ) {
if (that.settings.initialZoomState === null) {
return;
}
if (that.elems.img.is(':animated')) {
return;
}
var pos = $(this).offset();
var height = $(this).height();
var width = $(this).width();
var currentImage = that.settings.images[that.settings.currentImage];
var imgWidth = currentImage.width;
var imgHeight = currentImage.height;
var coord = [e.pageX - width/2 - pos.left, e.pageY - height/2 - pos.top];
var mvtX = 0;
if (imgWidth > width) {
var paddingX = that.settings.zoomedPaddingX(imgWidth, width);
mvtX = coord[0] / (width / 2);
mvtX = ((imgWidth - width)/ 2 + paddingX) * mvtX;
}
var mvtY = 0;
if (imgHeight > height) {
var paddingY = that.settings.zoomedPaddingY(imgHeight, height);
mvtY = coord[1] / (height / 2);
mvtY = ((imgHeight - height) / 2 + paddingY) * mvtY;
}
var animation = {
'margin-left': - mvtX + 'px',
'margin-top': - mvtY + 'px'
};
if (typeof e.duration !== 'undefined') {
$(that.elems.img).stop(false, true).animate(animation, e.duration);
}
else {
$(that.elems.img).stop(false, true).css(animation);
}
});
$(window).on('resize', function() {
if (!that.settings.initialized || that.settings.currentImage === false) {
return;
}
that.debounce(50, function() {
var fitting = that.fit(that.settings.currentImage, that.elems.wrapper);
that.center(fitting.width, fitting.height, fitting.left, fitting.top, 0);
that.zoomable();
});
});
},
zoomable : function () {
var currentImage = this.settings.images[this.settings.currentImage];
var wrapperWidth = this.elems.wrapper.width();
var wrapperHeight = this.elems.wrapper.height();
var isImageZoomable = ( this.settings.enableZoom && ( currentImage.width > wrapperWidth || currentImage.height > wrapperHeight)) ? true : false;
var isImageStretched = this.elems.img.width() > currentImage.width || this.elems.img.height() > currentImage.height;
if (isImageZoomable && !isImageStretched) {
this.elems.domContainer.addClass('chocolat-zoomable');
}
else {
this.elems.domContainer.removeClass('chocolat-zoomable');
}
},
zoomIn : function (e) {
this.settings.initialZoomState = this.settings.imageSize;
this.settings.imageSize = 'native';
var event = $.Event('mousemove');
event.pageX = e.pageX;
event.pageY = e.pageY;
event.duration = this.settings.duration;
this.elems.wrapper.trigger(event);
this.elems.domContainer.addClass('chocolat-zoomed');
var fitting = this.fit(this.settings.currentImage, this.elems.wrapper);
return this.center(fitting.width, fitting.height, fitting.left, fitting.top, this.settings.duration);
},
zoomOut : function (e, duration) {
if (this.settings.initialZoomState === null || this.settings.currentImage === false) {
return;
}
duration = duration || this.settings.duration;
this.settings.imageSize = this.settings.initialZoomState;
this.settings.initialZoomState = null;
this.elems.img.animate({'margin': 0}, duration);
this.elems.domContainer.removeClass('chocolat-zoomed');
var fitting = this.fit(this.settings.currentImage, this.elems.wrapper);
return this.center(fitting.width, fitting.height, fitting.left, fitting.top, duration);
},
setDomContainer : function() {
// if container == window
// domContainer = body
if ( this.settings.container === window) {
this.elems.domContainer = $('body');
}
else {
this.elems.domContainer = $(this.settings.container);
}
},
debounce: function(duration, callback) {
clearTimeout(this.settings.timerDebounce);
this.settings.timerDebounce = setTimeout(function() {
callback();
}, duration);
},
api: function() {
var that = this;
return {
open : function(i){
i = parseInt(i) || 0;
return that.init(i);
},
close : function(){
return that.close();
},
next : function(){
return that.change(1);
},
prev : function(){
return that.change(-1);
},
goto : function(i){ // open alias
return that.open(i);
},
current : function(){
return that.settings.currentImage;
},
place : function(){
return that.place(that.settings.currentImage, that.settings.duration);
},
destroy : function(){
return that.destroy();
},
set : function(property, value){
that.settings[property] = value;
return value;
},
get : function(property){
return that.settings[property];
},
getElem : function(name){
return that.elems[name];
},
};
}
});
var defaults = {
container : window, // window or jquery object or jquery selector, or element
imageSelector : '.chocolat-image',
className : '',
imageSize : 'default', // 'default', 'contain', 'cover' or 'native'
initialZoomState : null,
fullScreen : false,
loop : false,
linkImages : true,
duration : 300,
setTitle : '',
separator2 : '/',
setIndex : 0,
firstImage : 0,
lastImage : false,
currentImage : false,
initialized : false,
timer : false,
timerDebounce : false,
images : [],
enableZoom : true,
imageSource : "href",
afterInitialize : function () {},
afterMarkup : function () {},
afterImageLoad : function () {},
zoomedPaddingX : function (canvasWidth, imgWidth) { return 0; },
zoomedPaddingY : function (canvasHeight, imgHeight) { return 0; },
};
$.fn.Chocolat = function (options) {
return this.each(function() {
calls++;
var settings = $.extend(true, {}, defaults, options, {setIndex:calls} );
if (!$.data(this, 'chocolat')) {
$.data(this, 'chocolat',
new Chocolat($(this), settings)
);
}
});
};
}));