;(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)) } }) } return $.fn.Chocolat })