(function ($) {
	
    $.fn.slideshow = function (settings) {
    	var opts = $.extend({},$.fn.slideshow.defaults, settings);
    	var current_body = document.body || document.documentElement;
   		var current_body_style = current_body.style;
    	var is_css_transition_supported = current_body_style.WebkitTransition !== undefined || current_body_style.MozTransition !== undefined || current_body_style.OTransition !== undefined || current_body_style.transition !== undefined;
		var has_3D = ('WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix());

    	var reset_css = {
			'-webkit-transform':'none',
			'-moz-transform':'none',
			'-o-transform':'none',
			'transform':'none',
			'-webkit-transition':'none',
			'-moz-transition':'none',
			'-o-transition':'none',
			'transition':'none'
		};
    	var list_items = {};
    	var dimension = 1;
    	var before;
    	var list_items = {};
    	var current_item;
    	var current_id = 0;
    	var width;
    	var height;
    	var startX;
    	var startY;
    	var query = false;
    	var num_items = opts.num_items;
    	var is_sliding = false;
    	var is_dragging = false;
    	var last_drag_time = 0;
    	var drag_tread = 0;
    	var previous_offset = 0;
        
    	var $this = this;
    	
    	var slide = function(id) {
    		if ( is_sliding ) {
    			return;
    		}
    		is_sliding = true;
    		if (typeof list_items[id] != 'undefined') {
    			animate(id);
    		}
    		else if (opts.on_fly_url && ! query ) {
				query = true;
    			jQuery.get(opts.on_fly_url.replace('%id%',id), {}, function(data, status, request) {
    				display(id, data, status, request, true);
    				query = false;
    			}, 'html');
    		}
    	};

    	var animate = function(id) {
    		var current_start_css = jQuery.extend({},reset_css);
    		var current_end_css = jQuery.extend({},reset_css);
    		var next_start_css = jQuery.extend({},reset_css);
    		var next_end_css = jQuery.extend({},reset_css);
    		switch ( opts.direction ) {
    			case 'left':
    			case 'right':
    			case 'top':
    			case 'bottom':
    				current_start_css[opts.direction] = 0;
    				next_end_css[opts.direction] = 0;
    				if (current_id > id) {
    					current_end_css[opts.direction] = dimension;
    					next_start_css[opts.direction] = -dimension;
    				}
    				else {
    					current_end_css[opts.direction] = -dimension;
    					next_start_css[opts.direction] = dimension;
    				}
    				break;
    			case 'opacity':
    				current_start_css[opts.direction] = 1;
    				next_end_css[opts.direction] = 1;
    				current_end_css[opts.direction] = 0;
    				next_start_css[opts.direction] = 0;
    				break;
    		}
    		var list_item = list_items[id];
    		if ( current_item ) {
    			$this.children('.item').not(current_item).not(list_item).hide();
    			current_item.css(current_start_css).show();
    			list_item.css(next_start_css).show();
    			if ( is_css_transition_supported ) {
    				var transition = {
						'-webkit-transition':opts.direction+' '+opts.duration+'ms',
						'-moz-transition':opts.direction+' '+opts.duration+'ms',
						'-o-transition':opts.direction+' '+opts.duration+'ms',
						'transition':opts.direction+' '+opts.duration+'ms'
					};
    				current_end_css = jQuery.extend(current_end_css, transition );
    				next_end_css = jQuery.extend(next_end_css, transition );
    				setTimeout(function() {
	    				current_item.css(current_end_css);
	    				list_item.css(next_end_css);
    				},5);
    			}
    			else {
    				current_item.animate(current_end_css, opts.duration, opts.easing);
    				list_item.animate(next_end_css, opts.duration, opts.easing);
    			}
    			setTimeout(function() {
    				current_item.hide().css(reset_css);
    				current_item = list_item;
    				current_id = id;
    				current_item.show().css(reset_css);
    				if ( ! opts.loop ) {
    					refreshNavigation();
    				}
    	    		is_sliding = false;
    			}, opts.duration + 10);
    		} else {
    			current_item = list_item;
    			current_item.show();
    			current_id = id;
    			if ( ! opts.loop ) {
    				refreshNavigation();
    			}
        		is_sliding = false;
    		}
    	};
    	
    	var refreshNavigation = function() {
    		if ( current_id == 1 ) {
    			$this.children('.navigation-previous').hide();
    			$this.children('.navigation-next').show();
    		}
    		else if ( current_id == num_items ) {
    			$this.children('.navigation-previous').show();
    			$this.children('.navigation-next').hide();
    		}
    		else {			
    			$this.children('.navigation-previous').show();
    			$this.children('.navigation-next').show();
    		}
    	};

    	var display = function(id, data, status, request, do_animate) {
    		var list_item = jQuery(data);
    		list_item.css({
    			width:width+'px',
    			height:height+'px'
    		});
    		list_item.addClass('item');
    		if ( before ) {
    			before.before(list_item);
    		}
    		else {
    			$this.append(list_item);
    		}
			bindEvents(list_item);
    		list_items[id] = list_item;
    		if ( do_animate ) {
    			animate(id);
    		}
    	};

    	var bindEvents = function(list_item) {
    		//jQuery('a', list_item).bind('click', clickAnchor);
    		jQuery('.slideshow-hover', list_item).addClass('slideshow-hover-collapsed').hover(function() {
				jQuery(this).removeClass('slideshow-hover-collapsed').addClass('slideshow-hover-expanded');
			},function() {
				jQuery(this).removeClass('slideshow-hover-expanded').addClass('slideshow-hover-collapsed');
			});
    	};

    	var clickAnchor = function() {
    		var href = this.href;
    		var id = parseInt(href.substring(href.indexOf('#') + 1));
    		if ( current_id != id ) {
    			slide(id);
    		}
    		return false;
    	};
    	
    	var getOffset = function(e) {
    		var offset = 0;
    		var pageX = 0;
    		var pageY = 0;
    		if ( ( typeof e.originalEvent.changedTouches != 'undefined' ) && e.originalEvent.changedTouches.length ) {
    			var t = e.originalEvent.changedTouches[0];
        		pageX = t.pageX;
        		pageY = t.pageY;
    		}
    		else {
        		pageX = e.pageX;
        		pageY = e.pageY;
    		}
    		switch( opts.direction ) {
	        	case 'left':
	        		offset = startX - pageX;
	        		break;
	        	case 'right':
	        		offset = pageX - startX;
	        		break;
	        	case 'top':
	        		offset = startY - pageY;
	        		break;
	        	case 'bottom':
	        		offset = pageY - startY;
	        		break;
    		}
    		return offset;
    	};
    	
    	var startDrag = function(e) {
    		if ( is_sliding ) {
    			return;
    		}
    		if ( is_dragging ) {
    			return;
    		}
    		if ( e.target.nodeName.toLowerCase() == 'a' ) {
    			return;
    		}
    		is_dragging = true;
    		if ( ( typeof e.originalEvent.changedTouches != 'undefined' ) && e.originalEvent.changedTouches.length ) {
    			var t = e.originalEvent.changedTouches[0];
        		startX = t.pageX;
        		startY = t.pageY;
    		}
    		else {
        		startX = e.pageX;
        		startY = e.pageY;
    		}
    		if ( jQuery.browserTouchSupport.touches ) {
    			$this.bind('touchmove',drag);
    			$this.bind('touchend',endDrag);
    			$this.bind('touchcancel',endDrag);
    		}
    		else {
    			$this.bind('mousemove',drag);
    			$this.bind('mouseleave',endDrag);
    			$this.bind('mouseup',endDrag);
    		}
    	};

    	var drag = function(e) {
    		var offset = getOffset(e);
    		var final_css = $.extend({}, reset_css);
    		final_css[opts.direction] = 0;
    		if (
    			opts.loop || (
	    			( current_id > 1 || offset > 0 ) &&
	    			( current_id < num_items || offset < 0 )
	    		)
    		) {
    			var id = 0;
    			if ( offset > 0 ) {
    				id  = ( current_id % num_items ) + 1 ;
    			}
    			else {
    				id  = ( ( current_id - 2 + num_items ) % num_items ) + 1 ;
    			}
    			var current_css = {};
    			var next_css = {};
    			var transform;
    			if ( is_css_transition_supported ) {
    				if ( has_3D ) {
    					switch( opts.direction ) {
	    					case 'left':
	    						transform = 'translate3d('+(-offset)+'px,0,0)';
	    						break;
	    					case 'right':
	    						transform = 'translate3d('+(offset)+'px,0,0)';
	    						break;
	    					case 'top':
	    						transform = 'translate3d(0,'+(-offset)+'px,0)';
	    						break;
	    					case 'bottom':
	    						transform = 'translate3d(0,'+(offset)+'px,0)';
	    						break;
    					}
    				}
    				else {
    					switch( opts.direction ) {
	    					case 'left':
	    						transform = 'translate('+(-offset)+'px,0)';
	    						break;
	    					case 'right':
	    						transform = 'translate('+(offset)+'px,0)';
	    						break;
	    					case 'top':
	    						transform = 'translate(0,'+(-offset)+'px)';
	    						break;
	    					case 'bottom':
	    						transform = 'translate(0,'+(offset)+'px)';
	    						break;
    					}
    				}
    				current_css[opts.direction] = 0;
    				current_css['-webkit-transform'] = transform;
    				current_css['-moz-transform'] = transform;
    				current_css['-o-transform'] = transform;
    				current_css['transform'] = transform;
    				next_css[opts.direction] = ( offset > 0 ) ? dimension : -dimension;
    				next_css['-webkit-transform'] = transform;
    				next_css['-moz-transform'] = transform;
    				next_css['-o-transform'] = transform;
    				next_css['transform'] = transform;
    			}
    			else {
    				current_css[opts.direction] = -offset;
    				next_css[opts.direction] = -offset+( ( offset > 0 ) ? dimension : -dimension );
    			}
    			if ( typeof list_items[id] != 'undefined' ) {
    				var list_item = list_items[id];
    				if ( ! jQuery.browserTouchSupport.touches ) {
    					current_item.css(current_css);
    					list_item.show().css(next_css);    					
    				}
    			}
    			else if ( opts.on_fly_url && ! query ) {
    				query = true;
        			jQuery.get(opts.on_fly_url.replace('%id%',id), {}, function(data, status, request) {
        				display(id, data, status, request, false);
        				query = false;
        			}, 'html');
        		}
    		}
    		else {
    			current_item.css(final_css);
    			$this.children('.item').not(current_item).hide();
    		}
    	};
    	
    	var endDrag = function(e) {
    		if ( jQuery.browserTouchSupport.touches ) {
	    		$this.unbind('touchmove',drag);
	    		$this.unbind('touchend',endDrag);
	    		$this.unbind('touchcancel',endDrag);
    		}
    		else {    			
    			$this.unbind('mousemove',drag);
    			$this.unbind('mouseleave',endDrag);
    			$this.unbind('mouseup',endDrag);
    		}
    		var offset = getOffset(e);
    		if (
    			opts.loop || (
	    			( current_id > 1 || offset > 0 ) &&
	    			( current_id < num_items || offset < 0 )
	    		)
    		) {
	    		var next_css = $.extend({}, reset_css);
	    		var current_css = $.extend({}, reset_css);
	    		if ( offset > 0 ) {
					id  = ( current_id % num_items ) + 1 ;
				}
				else {
					id  = ( ( current_id - 2 + num_items ) % num_items ) + 1 ;
				}
	    		var virtual_offset = offset;
	    		if ( jQuery.browserTouchSupport.touches ) {
	    			virtual_offset = 0;
	    		}
				var list_item = list_items[id];
				current_css[opts.direction] = - virtual_offset;
				next_css[opts.direction] = - virtual_offset + ( ( offset > 0 ) ? dimension : -dimension );
				current_item.css(current_css).show();
				list_item.css(next_css).show();
				var previous_item = current_item;
				var next_item = list_item;
				var next_id = id;
				current_css = {};
				next_css = {};
				current_css[opts.direction] = ( offset > 0 ) ? - dimension : dimension;
				next_css[opts.direction] = 0;
	    		if ( Math.abs( 3 * offset ) < dimension ) {
	    			current_css[opts.direction] = - current_css[opts.direction];
	    			previous_item = list_item;
	    			next_item = current_item;
	    			next_id = current_id;
	    		}
	    		var rest_duration = Math.round( opts.duration * ( dimension - Math.abs(virtual_offset) ) / dimension );
    			if ( is_css_transition_supported ) {
    				var transition = {
						'-webkit-transition':opts.direction+' '+rest_duration+'ms',
						'-moz-transition':opts.direction+' '+rest_duration+'ms',
						'-o-transition':opts.direction+' '+rest_duration+'ms',
						'transition':opts.direction+' '+rest_duration+'ms'
					};
    				current_css = jQuery.extend(current_css, transition );
    				next_css = jQuery.extend(next_css, transition );
    				setTimeout(function() {
    					previous_item.css(current_css);
    					next_item.css(next_css);
    				},5);
    			}
    			else {
				previous_item.animate(current_css, rest_duration, opts.easing);
    				next_item.animate(next_css, rest_duration, opts.easing);
    			}
    			setTimeout(function() {
    				previous_item.hide().css(reset_css);
    				current_item = next_item;
    				current_id = next_id;
    				next_item.show().css(reset_css);
    				if ( ! opts.loop ) {
    					refreshNavigation();
    				}
    				is_dragging = false;
    			}, opts.duration + 10);
    		}
    		else {
    			$this.children('.item').css(reset_css);
    			is_dragging = false;
    		}
    	};
        $this.addClass('slideshow-'+opts.direction);
        
        width = $this.width();
    	height = $this.height();
    	
    	switch ( opts.direction ) {
    		case 'left':
    		case 'right':
    			dimension = width;
    			reset_css[opts.direction] = 0;
    			break;
    		case 'top':
    		case 'bottom':
    			dimension = height;
    			reset_css[opts.direction] = 0;
    			break;
    		case 'opacity':
    			reset_css[opts.direction] = 1;
    			break;
    	}
    	var i = 1;
    	$this.children('.item').css({
			width:width+'px',
			height:height+'px'
		}).each(function() {
    		list_items[i] = jQuery(this);
    		bindEvents(list_items[i]);
    		i++;
    	});
    	if ( ! opts.on_fly_url ) {
    		num_items = $this.children().size();
    	}
    	if ( ! before && $this.children(':not(.item):first').size() ) {
    		before = $this.children(':not(.item):first');
    	}

    	if ( opts.pagination ) {
    		var html = [];
    		if ( typeof opts.pagination == 'object' ) {
    			if ( opts.pagination.length ) {
    				for ( var i = 0 ; i < opts.pagination.length ; i++ ) {
    					var menu = [];
        				for ( var key in opts.pagination[i] ) {
        					menu.push('<a href="#'+key+'">'+opts.pagination[i][key]+'</a>');
        				}
        				html.push('<li class="menu menu-'+i+' '+( opts.hide_pagination?'menu-collapsed':'menu-expanded')+'">'+menu.join('')+'</li>');
    				}
    			}
    			else {
            		var menu = [];	
    				for ( var key in opts.pagination ) {
    					menu.push('<a href="#'+key+'">'+opts.pagination[key]+'</a>');
    				}
    				html.push('<li class="menu '+( opts.hide_pagination?'menu-collapsed':'menu-expanded')+'">'+menu.join('')+'</li>');
    			}
    		}
    		else {
        		var menu = [];
    			for ( var i = 1 ; i <= num_items ; i++ ) {
    				menu.push('<a href="#'+i+'">'+i+'</a>');
        		}
        		html.push('<li class="menu '+( opts.hide_pagination?'menu-collapsed':'menu-expanded')+'">'+menu.join('')+'</li>');
    		}
    		var menu = jQuery(html.join(''));
    		$this.append(menu);
    		//jQuery('a',menu).bind('click', clickAnchor);
    		if ( ! before ) {
    			before = jQuery( menu[0] );
    		}
    		if ( opts.hide_pagination ) {
    			menu.each(function() {
    				jQuery(this).hover(function() {
	    				jQuery(this).removeClass('menu-collapsed').addClass('menu-expanded');
	    			},function() {
	    				jQuery(this).removeClass('menu-expanded').addClass('menu-collapsed');
	    			});
    			});
    		}
    	}
    	
        if ( opts.navigation ) {
    		var navigation = jQuery('<li class="navigation navigation-previous"><a class="previous">'+opts.previous_label+'</a></li><li class="navigation navigation-next"><a class="next">'+opts.next_label+'</a></li>');
    		$this.append(navigation);
    		jQuery('a',navigation).bind('click', function() {
    			if ( jQuery(this).hasClass('next') ) {
    				slide((current_id)%num_items+1);
    			}
    			else {
    				slide((current_id-2+num_items)%num_items+1);
    			}
    		});
    		if ( ! before ) {
    			before = jQuery( navigation[0] );
    		}
    	}
    	
    	slide(1);
    	
    	if ( opts.auto ) {
    		setInterval(function() { slide((current_id)%num_items+1); }, opts.auto );
    	}
    	
    	if ( opts.drag ) {
        	switch( opts.direction ) {
	        	case 'left':
	        	case 'right':
	        	case 'top':
	        	case 'bottom':
	        		if ( jQuery.browserTouchSupport.touches ) {
	        			$this.bind('touchstart',startDrag);
	        		}
	        		else {	        			
	        			$this.bind('mousedown',startDrag);
	        		}
	        		break;
        	}
    	}
        
        $.fn.extend({
        	slide: function(id) {
        		slide(id);
    		},
    		start: function() {
    		},
    		stop: function() {
    		}
        });
    	
    	return $this;
    };
    
	$.fn.slideshow.defaults = {
        displayed_items: 1,
        num_items: 1,
        navigation: true,
        previous_label: 'Previous',
        next_label: 'Next',
        pagination: true,
        loop: false,
        drag: true,
        auto: false,
        effect: 'slide',
        duration: 1000,
        direction: 'left',
		easing: 'swing',
		hide_pagination: false,
		hide_navigation: false
    };
	
})(jQuery);
