/* ==|== Carousel Widget ==================================== 
	This bad boy is a portable carousel plugin for jquery.
	See the FMG Wiki for reference & documentation
   =======================================================|== */

(function($) {
	
  $.widget("meetcha.carousel", {
	
	// Defaults! Don't change the defaults here as it may break
	// expected behaviour, and previous installations
    options: { 
		transition: "slide",
		transitionDuration: 400,
		autoRotate: false,
		autoRotateTime: 20000,
		prevNext: true,
		showNav: false,
		verbose: false
	},
	
	_create: function() {
		
		// Instance variables
		this.random = null;
		this.viewport = null;
		this.itemContainer = null;
		this.itemWidth = 0;
		this.slides = new Array();
		this.curSlide = 0;
		this.circlingNext = false;
		this.circlingPrev = false;
		this.animating = false;
		this.rotateTimer = null;
		this.mouseOver = false;
		
		
		// Init & DOM injection methods
		this._initViewport();
		this._buildSlides();
		this._calcSizes();
		
		
		// Init methods, if the user has enabled them
    	if (this.options['showNav']) this._buildNav();
		if (this.options['prevNext']) this._buildPrevNext();
		if (this.options['autoRotate']) this._autoRotate();


		// Go to the first slide
		this.gotoSlide(this.curSlide, false);
		
	},
	
	_initViewport: function() {
	// Start up the viewport, do viewport init.
		
		this.viewport = this.element; // the viewport is actually the invoked element
		
		var itemContainer = $('<div class="carousel_items"></div>');
		this.itemContainer = itemContainer;
		this.itemContainer.css("position", "relative"); 
		
		// Give the viewport an ID if necessary
		if (!this.viewport.attr("id")) { 
			var ts = Math.round((new Date()).getTime() / 1000);
			this.viewport.attr("id", "carousel_" + ts);
		}

		// Make the viewport have non-static position
		if (!this.viewport.css("position") || this.viewport.css("position") == "static") {
			this.viewport.css("position", "relative");
		}

		if (this.options['verbose']) this._log("Creating a carousel for " + this.element.attr("id"));
		
		this.viewport.addClass("carousel_viewport");
		this.viewport.append(this.itemContainer);
		this.viewport.hover(this._carouselHoverIn, this._carouselHoverOut);
			
		$("body").bind('keydown', { target: this.element.attr("id") }, this._keyPress);
	},
	
	_buildSlides: function() {
	// Go through all the viewport's top level children
	// add them to the container, and class them as slides
	
		if (this.options['verbose']) this._log("Scraping out the slides for " + this.element.attr("id"));
		
		var _this = this;
		_this.element.children().each(function(i, e) {

			if ( !$(this).is(".carousel_items") ) {
				$(this).addClass("slide");

				// Give each slide a unique id if they don't have one
				if(!$(this).attr("id")) {
					$(this).attr("id", _this.viewport.attr('id') + "_slide_" + i);
				}

				var node = $(this).detach();
				_this.itemContainer.append(node);
				_this.slides.push(node);
			}
		
        });
		
		if (this.slides.length == 0) {
			this._error("Didn't find any slides on " + this.element.attr("id"));
		} else if (this.options['verbose']) {
			this._log("Found " + this.slides.length + " slides for " + this.element.attr("id"));
		}

		this.curSlide = 1;
		this.itemContainer.append($('<br clear="both" />'));
		
	},
	
	_calcSizes: function() {
	// Determine the size of the slides, and therefore
	// the size of the slide container
	
		var itemWidth = this.viewport.find(".slide").outerWidth(true);
		var containerWidth = ((this.slides.length + 1) * itemWidth);
		
		this.itemContainer.css("width", containerWidth + "px");	
		this.itemWidth = itemWidth;
		
	},
	
	_buildNav: function() {
	// Go through all the slides, and build a corresponding
	// list of navigation items. Add some listeners to them.

		navbar = $('<ol class="carousel_nav"></ol>')
		this.viewport.append(navbar);

		for (var i = 1; i <= this.slides.length; i++) {

			if (i == this.curSlide) { 
				var cssclass = "active"; 
			} else { 
				var cssclass = "";
			}

			var navItem = $('<li id="nav_' + i + '" class="carouselNavItem ' + cssclass + '"><a href="#">' + i +'</a></li>');
			navItem.data("slideId", i);
			navItem.click(this._navClick);
			$(navbar).append(navItem);


		}
	},
	_navClick: function(e) {
		var slideId = $(this).data("slideId");
		_this = $(this).parent().parent().data("carousel");
		_this.gotoSlide(slideId);
		e.preventDefault();
	},
	
	_buildPrevNext: function() {
		
		var prev = $('<a href="#" class="carousel_prev">&lt;</a>');
		this.viewport.append(prev);
		prev.click(this._prevClick);
		prev.css("lineHeight", this.element.outerHeight() + "px");
		prev.hide();

		var next = $('<a href="#" class="carousel_next">&gt;</a>');		
		this.viewport.append(next);
		next.click(this._nextClick);
		next.css("lineHeight", this.element.outerHeight() + "px");
		next.hide();
		
	},	
	_prevClick: function(e) {
		_this = $(this).parent().data("carousel");
		_this.prevSlide();
		e.preventDefault();
	},
	_nextClick: function(e) {
		_this = $(this).parent().data("carousel");
		_this.nextSlide();
		e.preventDefault();
	},
	
	
	
	prevSlide: function() {
		
		if (!this.animating) {
			
			// Are we at the first slide?
			if (this.curSlide == 1) {

				// Duplicate the last slide for animation
				// And append it before this one
				lastSlide = this.element.find(".slide:last").clone();
				this.itemContainer.prepend(lastSlide);

				// Move over to see our original slide
				this.itemContainer.css("left", (this.itemWidth * -1)+"px");
				this.curSlide = 2;

				// Flag that we're circling around (backwards)
				this.circlingPrev = true;

			} 

			this.curSlide--;
			this._animateSlides();
		
		}
				
	},
	
	nextSlide: function() {
		
		if (!this.animating) {
			
			// Are we at the last slide?
			if (this.curSlide == this.slides.length) {
				
				// Duplicate the first slide for the animation
				// and append at the end
				var firstSlide = this.element.find(".slide:first").clone();
				var br = this.element.find("br").detach(); // Stupid clearfix
				this.itemContainer.append(firstSlide);
				this.itemContainer.append(br);

				// Flag that we're circling around (forwards)
				this.circlingNext = true;
			} 
			
			// Go to the next slide
			this.curSlide++;
			this._animateSlides();
		
		}
				
	},
	
	gotoSlide: function(slide, animate) {
		if (typeof animate === "undefined")	animate = true; // Animate by default
		
		this.curSlide = slide;
		
		if (animate === true) {
			this._animateSlides();
		} else {
			this._animateSlides(0);
		}
	},

	_animateSlides: function(duration) {
		
		if (typeof duration === "undefined") {
			var animDuration = this.options['transitionDuration'];
		} else {
			var animDuration = duration;
		}
		
		this.animating = true;

		// Update the carousel nav items
		_this = this;
		this.viewport.find(".carouselNavItem").each(function(e){
			if ($(this).data("slideId") != _this.curSlide) {
				$(this).removeClass("active");
			} else {
				$(this).addClass("active");
			}
		});

		var targetLeft = ((this.itemWidth * this.curSlide) - this.itemWidth);
		var curLeft = this.itemContainer.css("left");
		curLeft = curLeft.substring(0, curLeft.length - 2);
		targetLeft *= -1;	
		
		if (animDuration > 0) {
			this.itemContainer.animate({ left: targetLeft + "px"}, animDuration, this._slideAnimationComplete);
		} else if (animDuration === 0) {
			this.itemContainer.css("left", "0");
			this.animating = false;
		}
	},

	_slideAnimationComplete: function() {
	// The slide animation is done
	// Handle all post-animation duties
	
	_this = $(this).parent().data("carousel");
	
	
		// If the user is circling around from moving forwards
		if (_this.circlingNext) {
			// Set current slide number and position properly
			_this.curSlide = 1; 	
			_this.itemContainer.css("left", "0px");

			// Get rid of the animation slide
			_this.viewport.find(".slide:last").remove();
			_this.circlingNext = false;
		}


		// If the user is circling around from moving backwards
		if (_this.circlingPrev) {

			// Set current slide number and position properly
			_this.curSlide = _this.slides.length;

			targetLeft = ((_this.slides.length * _this.itemWidth) - _this.itemWidth) * -1;
			_this.itemContainer.css("left", targetLeft + "px");

			_this.viewport.find(".slide:first").remove();
			_this.circlingPrev = false;
		}
		
		_this.animating = false;

	},
	
	stopAutoRotate: function() {
		clearInterval(this.rotateTimer);
	},
	
	resumeAutoRotate: function() {
		_this = $(this).data("carousel");
		this._autoRotate();
	},

	_autoRotate: function() {
		clearInterval(this.rotateTimer);
		this.rotateTimer = setInterval("jQuery('#" + this.viewport.attr('id') + "').carousel('nextSlide');", this.options['autoRotateTime']);	
	},

    _carouselHoverIn: function(e) {	
		_this = $(this).data("carousel");
		if (_this.options['prevNext']) {
			_this.viewport.find(".carousel_prev").fadeIn();
			_this.viewport.find(".carousel_next").fadeIn();
		}

		if (_this.options['showNav'] && e.target.nodeName.toLowerCase() != "video") {
			_this.viewport.find(".carousel_nav").fadeIn();
		}
		
		_this.mouseOver = true;
    },

	_carouselHoverOut: function(e) {
		_this = $(this).data("carousel");
		if (_this.options['prevNext']) {
			_this.viewport.find(".carousel_prev").fadeOut();
			_this.viewport.find(".carousel_next").fadeOut();
		}

		if (_this.options['showNav']) {
			_this.viewport.find(".carousel_nav").fadeOut();
		}

		_this.mouseOver = false;
	},
	
	_keyPress: function(e) {
	// keyboard listener. careful! this is bound to Window
	// so any changes may produce unexpected behaviour

		// traverse all carousels
		var carousels = $(".carousel_viewport").each(function() {
			
			var _this = $(this).data("carousel");
			
			// if this carousel has the mouse, respond to arrow presses
			if (_this.mouseOver) {
				if (e.keyCode === 37) _this.prevSlide();
				if (e.keyCode === 39) _this.nextSlide();
			}
			
		});
		
	},
	
	_log: function(message) {
		if (console) {
			if (console.log) console.log(message);
		}	
	},	
	_warn: function(message) {
		if (console) {
			if (console.warn) console.warn(message);
		}
	},
	_error: function(message) {
		if (console) {
			if (console.error) console.error(message);
		}
	},
	_trace: function(message) {
		if (console) {
			if (console.trace) console.trace(message);
		}
	},
	_dir: function(message) {
		if (console) {
			if (console.dir) console.dir(message);
		}
	},

    _setOption: function(key, value) {
      switch(key) {
        case "":
          break;
      }
      $.Widget.prototype._setOption.apply(this,arguments)
    },
    destroy: function() {
      $.Widget.prototype.destroy.call(this);
    }
  });
})(jQuery);

