/*! jquery.yalb - v0.3.3 - 2015-06-24
* https://github.com/Fuzzyma/jquery.yalb
* Copyright (c) 2015 Ulrich-Matthias Schäfer; Licensed MIT */
/* jshint -W083 */
(function($){
var instance = null;
$.yalb = function(list, options){
// We create an instance of this plugin
if(!(this instanceof $.yalb)){
// close last instance
if(instance){ instance.yalb.trigger('close'); }
return new $.yalb(list, options);
}else{
instance = this;
}
var settings = $.extend({}, $.yalb.defaults, options),
images = [],
current = settings.current,
$container = $('
'),
$wrapper,
createSpan,
hideLoader,
hideShowButtons,
showLoader,
showError,
showImg,
getSrc,
loadImg,
changeImg,
resizeWindow,
onerror,
onload,
prev,
next,
show,
open,
close;
// Helper, gives a span with added class and click-event
createSpan = function(name){
return $('
',{ 'class': name, click: function(){ $container.trigger(name); } });
};
// hides the loader-icon
hideLoader = function(){
$container.children('span.loader').stop().fadeOut();
$container.children('strong.error').stop().fadeOut();
};
// hides or shows next and prev-button
hideShowButtons = function(){
if(settings.loop && list.length > 1){ return; }
if(current === list.length - 1){ $container.children('.next').hide(); }
else{ $container.children('.next').show();}
if(current === 0){ $container.children('.prev').hide(); }
else{ $container.children('.prev').show(); }
};
// displays the loader-icon
showLoader = function(){
$container.children('span.loader').stop().fadeIn();
};
// shows the error-msg
showError = function(){
$container.children('img.image').fadeOut(function(){ $(this).remove(); });
$container.children('span.loader').stop().fadeOut();
$container.children('strong.error').fadeIn();
loadImg([current-1, current+1]);
};
// shows the image
showImg = function(){
$container.append($(images[current].img).addClass('image').fadeIn());
$container.trigger('change', {index:current});
loadImg([current-1, current+1]);
};
// returns the src-path of an image
getSrc = function(obj){
if(typeof obj === 'string'){ return obj; }
// check if a data-attribute was specified
if(obj instanceof Node && settings.src.indexOf('data-') === 0){
return obj.getAttribute(settings.src);
}
var split = settings.src.split('.');
// extract path from object in case `src`-path is nested
for(var i = 0, len = split.length; i < len; ++i){
obj = obj[split[i]];
}
// return path string
return obj;
};
// Loads one or more images
loadImg = function(arr){
arr = arr || [current];
for(var i = arr.length; i--;){
// When looping is active, make sure we also load images out of range
if(settings.loop && arr[i] < 0){
arr[i] += images.length;
}
// check if image is loaded / error / pending or index is out of range
if(arr[i] >= images.length || arr[i] < 0 || images[arr[i]].loaded || images[arr[i]].pending || images[arr[i]].error){ continue; }
// start image-loading by setting its path, state is now "pending"
images[arr[i]].img.src = getSrc(list[arr[i]]);
images[arr[i]].pending = true;
}
};
// changes the image
changeImg = function(){
hideShowButtons();
if(images[current].loaded){
// when image is already loaded, start the resizing process
resizeWindow();
}else if(images[current].error){
// if there was an error loading the image, display the error ( no need to resize here )
showError();
}else{
// the image has to be loaded first. Display loader and start loading the Image
showLoader();
loadImg();
}
};
// resizes the window to fit the new image
resizeWindow = function(){
hideLoader();
// make sure that new image is not the same as current, fade Out the old Image
if($container.children('img.image').attr('src') === $(images[current].img).attr('src')){ return; }
if($container.children('img.image').length){ $container.children('img.image').fadeOut(function(){ $(this).remove(); }); }
var ratio = images[current].img.naturalWidth / images[current].img.naturalHeight,
height = images[current].img.naturalHeight,
width = images[current].img.naturalWidth,
maxHeight = settings.height || $(window).height() - 40,
maxWidth = settings.width || $(window).width() - 40;
// scale down image if to big
if(height > maxHeight){
height = maxHeight;
width = ratio * maxHeight;
}
if(width > maxWidth){
width = maxWidth;
height = maxWidth / ratio;
}
// when image-dimension didn't change to much ( < 1px) don't animate
if(Math.abs($container.width() - width) > 1 || Math.abs($container.height() - height) > 1){
$container.stop();
$container.animate({
width: width,
height: height,
bottom: -($(window).height() - height) / 2
}, showImg);
}else{
showImg();
}
};
// returns function which is called when one image could not be loaded
onerror = function(i){
return function(){
images[i].error = true;
if(i === current){ changeImg(); }
};
};
// returns function which is called when one image is loaded
onload = function(i){
return function(){
images[i].loaded = true;
// when the just loaded image is the current one we are waiting for: change to it ( we could directly call resizeWindow here)
if(i === current){ changeImg(); }
};
};
// change to previous image
prev = function(){
if(current > 0){
--current;
changeImg();
}else if(settings.loop){
current = images.length -1;
changeImg();
}
};
// change to next image
next = function(){
if(current < list.length - 1){
++current;
changeImg();
}else if(settings.loop){
current = 0;
changeImg();
}
};
// changes to a specific image
show = function(e, data){
if(data.index !== null){
current = data.index % images.length;
changeImg();
}
};
// close yalb
close = function(){
$container.parent().fadeOut(function(){ $(this).remove(); });
$container.unbind();
};
// open yalb if not already open
open = function(){
$wrapper.appendTo('body').hide().fadeIn();
changeImg();
};
// add prev/next-links, loader and error-msg
$container.addClass(settings['class'])
.append(createSpan('prev'))
.append(createSpan('next'))
.append(createSpan('close'))
.append($('').addClass('loader').hide())
.append($('').addClass('error').hide());
// wrapper containing yalb
$wrapper = $('').addClass(settings['class'] + '_wrapper').click(function(e){
if(e.target === this){ $container.trigger('close'); }
}).append($container);
// Bind events to yalb
$container.on({
prev:prev,
next:next,
close:close,
show:show,
open:open
});
this.yalb = $container;
// loop through all images and create an image-object and a state-machine for every one
for(var i = 0, len = list.length; i < len; ++i){
images[i] = {
img: new Image(),
loaded: false,
pending: false,
error: false
};
images[i].img.onload = onload(i);
images[i].img.onerror = onerror(i);
}
// Open yalb
if(settings.open){
open();
changeImg();
}
return this;
};
$.yalb.prev = function(){
if(!instance){ return $.yalb; }
instance.yalb.trigger('prev');
return $.yalb;
};
$.yalb.next = function(){
if(!instance){ return $.yalb; }
instance.yalb.trigger('next');
return $.yalb;
};
$.yalb.close = function(){
if(!instance){ return $.yalb; }
instance.yalb.trigger('close');
return $.yalb;
};
$.yalb.show = function(index){
if(!instance){ return $.yalb; }
instance.yalb.trigger('show', {index:index});
return $.yalb;
};
$.yalb.open = function(){
if(!instance){ return $.yalb; }
instance.yalb.trigger('open');
return $.yalb;
};
$.yalb.on = function(){
if(!instance){ return $.yalb; }
instance.yalb.on.apply(instance.yalb, Array.prototype.slice.call(arguments, 0));
return $.yalb;
};
$.yalb.off = function(){
if(!instance){ return $.yalb; }
instance.yalb.off.apply(instance.yalb, Array.prototype.slice.call(arguments, 0));
return $.yalb;
};
$.yalb.get = function(){
if(!instance){ return $.yalb; }
return instance.yalb;
};
$.yalb.defaults = {
src: 'src',
current: 0,
'class': 'yalb',
loop: true,
open: true,
width: 0,
height: 0
};
$.fn.yalb = function(options){
$.yalb(this, options);
return this;
};
})(jQuery);