/**
 * Easy Dropdown
 *
 * @author		Luke Robinson <luke@rejuvenateproductions.com>
 * @copyright	Luke Robinson 2010
 * @version		0.1.3
 *
 * @changelog
 *		0.1.2 	-	Added 'init' callback
 *		0.1.3	-	Added return value to 'timeout' callback
 */
;(function($){
	
	var plugin = "easyDropdown";
	
	/**
	 * Class Constructor
	 */
    $[plugin] = function(ele, options){
		// Attach a few quick access properties
		this.ele = ele;		
		this.$ele = $(ele);
		
		// Extend the default options
		this.options = $.extend(true, {}, $[plugin].defaults, options);
		
		// Attach hover function
		var connector = this.options.hoverConnector || this.$ele;
		connector.hover($.proxy(this.hoverOver, this), $.proxy(this.hoverOut, this));
		
		// Run init callback
		this.options.callbacks.init(this);
    };
	
	/**
	 * Toggle
	 */	
	$[plugin].prototype.toggle = function(){
		( ! this.timer) ? this.open() : this.close();
	};	 
	 
	/**
	 * Open
	 */
	$[plugin].prototype.open = function(){
		// Run first callback
		this.options.callbacks.open(this);
		// Set timer
		this.setTimer();
		// Run animation
		this.$ele[this.options.animation[0]]({
			duration	: this.options.speed,
			easing		: this.options.easing[0],
			complete	: $.proxy(function(){
				// Run complete callback
				this.options.callbacks.openComplete(this);
			}, this)		
		});		
	};
	
	/**
	 * Close
	 */
	$[plugin].prototype.close = function(){
		// Run first callback
		this.options.callbacks.close(this);
		// Reset timer
		this.resetTimer();
		// Run animation	
		this.$ele[this.options.animation[1]]({
			duration	: this.options.speed,
			easing		: this.options.easing[1],
			complete	: $.proxy(function(){
				// Run complete callback
				this.options.callbacks.closeComplete(this);
			}, this)		
		});
	};
	
	/**
	 * Set Timer
	 */
	$[plugin].prototype.setTimer = function(timeout){
		// Reset timer before creating a new one
		this.resetTimer();
		
		// Use $.proxy to pass function context
		this.timer = setTimeout($.proxy(function() {
			
			// Run timeout callback
			var timeoutResponse = this.options.callbacks.timeout(this);
			
			// If hovering set the timer again, otherwise call close()
			(this.hoverActive || timeoutResponse) 
				? this.setTimer() : this.close();
				
		}, this), timeout || this.options.timeout);
	};
	
	/**
	 * Reset Timer
	 */
	$[plugin].prototype.resetTimer = function(){
		clearTimeout(this.timer);
		this.timer = false;
	};
	
	/**
	 * Default Properties
	 */
	$[plugin].prototype.timer = false;

	/**
	 * Hover Over
	 */
	$[plugin].prototype.hoverOver = function() {
		this.hoverActive = true;
		this.options.callbacks.hoverOver(this);
	};
	
	/**
	 * Hover Out
	 */
	$[plugin].prototype.hoverOut = function() {
		this.hoverActive = false;
		this.setTimer(this.options.hoverTimeout);
		this.options.callbacks.hoverOut(this);		
	};
	 
	/**
	 * Class Method(s) & Properties
	 */
	$[plugin].defaults = {
		'hoverConnector':	false,						// The element to be used as the hover connector
		'speed'			:	'fast',						// Animation Speed
		'timeout'		:	3500,						// Timeout before auto close
		'hoverTimeout'	:	500,						// Timeout before close after hover
		'animation'		:	['slideDown', 'slideUp'],	// Open and Close jQuery methods used
		'easing'		:	['swing', 'swing'],			// Open and Close easing effects on animation
		'callbacks'		:	{
			'init'			:	$.noop,					// Callback on first init
			'hoverOver'		:	$.noop,					// Callback on hover over
			'hoverOut'		:	$.noop,					// Callback on hover out
			'open'			:	$.noop,					// Callback on open
			'openComplete'	:	$.noop,					// Callback on open complete
			'close'			:	$.noop,					// Callback on close
			'closeComplete'	:	$.noop,					// Callback on close complete
			'timeout'		:	$.noop					// Callback on timeout (before close). Return TRUE to halt timeout.
		}
	};	
	
	/**
	 * jQuery Plugin Prototype
	 *
	 * Creates a new instance of the class and stores it in the element data.
	 *
	 * Once initialised, first param becomes the method you wish to call. An unlimited 
	 * number of params after this are also sent to the same method.
	 */
    $.fn[plugin] = function(options, params){

		var params = [].slice.call(arguments, 1);

        return this.each(function(){

			var instance = $(this).data(plugin);

			(instance)
				? (typeof(options) === "string") ? instance[options].apply(instance, params) : false
				: $(this).data(plugin, new $[plugin](this, options));
        });
    };

})(jQuery);
