/* -------------------------------------------------------------------------- */
/** 
 *    @fileoverview
 *       toggle opened/closed the panels
 *
 *    @version 2.1.20100228
 *    @requires jquery.js
 *    @requires jquery.easing.js
 *    @requires bajl.js
 */
/* -------------------------------------------------------------------------- */
(function($) {



/* -------------------- Settings for BAJL.TogglePanel -------------------- */
/** 
 * settings for {@link BAJL.TogglePanel}
 * @namespace settings for {@link BAJL.TogglePanel}
 * @fieldOf BAJL.settings
 * @property {Boolean} autoSetup.enabled    autosetup is enabled or not.
 * @property {String}  className.opened     className for when the panel block is expanded ; this className is applied to both buttons and a panel.
 * @property {String}  className.closed     className for when the panel block is collapsed; this className is applied to both buttons and a panel.
 * @property {Number}  effect.duration      default time for animation duration.
 */
BAJL.settings.TogglePanel = {
	  'autoSetup' : {
		  'enabled' : true
	}
	, 'className' : {
		  'base'   : 'bajl-togglepanel'
		, 'opened' : 'pseudo-opened'
		, 'closed' : 'pseudo-closed'
	}
	, 'effect'    : {
		  'duration' : 250
	}
}



/* -------------------- jQuery.fn : BAJL_TogglePanel -------------------- */
/**
 * BAJL.TogglePanel as jQuery plugin
 * @param {Element|jQuery|String} buttons           element node(s) as toggle buttons
 * @param {Number}                [duration=250]    animation duration time (ms)
 * @returns jQuery
 * @type jQuery
 */
jQuery.fn.BAJL_TogglePanel = function (buttons, duration) {
	return this.each(function(){ new BAJL.TogglePanel(this, buttons) });
}



/* -------------------- Class : BAJL.TogglePanel -------------------- */
/**
 * provides 
 * @class toggle panel
 * @extends BAJL.Observable
 * @param {Element|jQuery|String} panel             an element node as a panel block
 * @param {Element|jQuery|String} buttons           element node(s) as toggle buttons
 * @param {Number}                [duration=250]    animation duration time (ms)
 */
BAJL.TogglePanel = function(panel, buttons, duration) {
	/** an element node as a panel block
	    @type Element
	    @private */
	this.node     = panel;
	/** element node(s) as toggle buttons
	    @type Element[]
	    @private */
	this.buttons  = buttons;
	/** animation duration time (ms)
	    @type Number
	    @private
	    @constant */
	this.duration = (typeof duration == 'number') ? Math.max(0, duration) : BAJL.settings.TogglePanel.effect.duration;
	/** true when the panel is opened.
	    type Boolean
	    @private */
	this.opened   = false;
	/** true during the opening/closing animation is ongoing.
	    type Boolean
	    @private */
	this.locked   = false;
	
	if (BAJL.env.isDOMReady) {
		this.init();
	}
}

BAJL.TogglePanel.prototype = new BAJL.Observable;

/* ---------- class methods/props ---------- */

/**
 * an array of instances of this class.
 * @type BAJL.TogglePanel[]
 */
BAJL.TogglePanel.instances = [];

/**
 * store an instance created from this class
 * @param {BAJL.TogglePanel} instance    an instance object to store
 * @return an instance object stored
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.storeInstance = function(instance) {
	if (!instance || !(instance instanceof BAJL.TogglePanel)) {
		throw new TypeError('BAJL.TogglePanel.storeInstance: first argument must be an instance of BAJL.TogglePanel');
	} else {
		$(instance.node).data('BAJL.TogglePanel.InstanceID', this.instances.push(instance) - 1);
	}
}

/**
 * get an instance created from this class
 * @param {Number|Element|jQuery|String} arg    instance-ID number, or element node which was applied BAJL.TogglePanel
 * @return BAJL.TogglePanel instance
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.getInstance = function(arg) {
	if (typeof arg == 'number') {
		return this.instances[arg];
	} else if (arg && (arg.nodeType == Node.ELEMENT_NODE || typeof arg.jquery == 'string' || typeof arg == 'string')) {
		return this.instances[$(arg).data('BAJL.TogglePanel.InstanceID')];
	} else {
		throw new TypeError('BAJL.TogglePanel.getInstance: first argument must be an ID number, element node, jQuery object, or jQuery selector text.');
	}
}

/**
 * dipose an instance created from this class
 * @param {BAJL.TogglePanel} instance    an instance object to delete
 * @return an instance object stored
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.disposeInstance = function(instance) {
	if (!instance || !(instance instanceof BAJL.TogglePanel)) {
		throw new TypeError('BAJL.TogglePanel.disposeInstance: first argument must be an instance of BAJL.TogglePanel');
	} else if (instance.node) {
		BAJL.TogglePanel.instances.splice($(instance.node).data('BAJL.TogglePanel.InstanceID'), 1, undefined);
		instance.dispose(true);
	}
}

/* ---------- instance methods ---------- */

/**
 * initialize
 * @private
 */
BAJL.TogglePanel.prototype.init = function() {
	var $node    = $(this.node);
	var $buttons = $(this.buttons);

	if (!$node.BAJL_HasElement()) {
		throw new ReferenceError('BAJL.TogglePanel#init: a panel block element is not found.');
	} else if (!BAJL.TogglePanel.getInstance($node)) {
		var closedCName = BAJL.settings.TogglePanel.className.closed;

		this.node    = $node.addClass(BAJL.settings.TogglePanel.className.base).get(0);
		this.buttons = $buttons.get();
		this.opened  = (   !$node   .hasClass(closedCName)
                        && !$buttons.hasClass(closedCName)
                        &&  $node   .css('display') != 'none');
		if (this.opened) this.removeClass(closedCName);
		            else this.addClass   (closedCName);

		$buttons.click(BAJL.Delegate(function(e) {
			e.preventDefault();
			this.toggle();
		}, this));

		BAJL.TogglePanel.storeInstance(this);
	}
}

/**
 * dispose this instace
 * @param {Boolean} preventRecursion    'true' to prevent recursion
 */
BAJL.TogglePanel.prototype.dispose = function(preventRecursion) {
	if (!preventRecursion) BAJL.TogglePanel.disposeInstance(this);
	$.each(this, BAJL.Delegate(function(prop) {
		delete this[prop];
		if (typeof this[prop] == 'function') this[prop] = new Function;
	}, this));
}

/**
 * open the panel with animation.
 * @param {Number}   [duration]       animation duration; if nonspecified, the default duration of this instance is used.
 * @param {Function} [func]           callback function/method which is called when opening/closing animation is completed.
 * @param {Object}   [aThisObject]    object that will be a global object ('this') in 'func'.
 * @returns this instance
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.prototype.open = function(duration, aCallback, aThisObject) {
	if (!this.opened) {
		this.toggle.apply(this, arguments);
	}
}

/**
 * close the panel with animation.
 * @param {Number}   [duration]       animation duration; if nonspecified, the default duration of this instance is used.
 * @param {Function} [func]           callback function/method which is called when opening/closing animation is completed.
 * @param {Object}   [aThisObject]    object that will be a global object ('this') in 'func'.
 * @returns this instance
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.prototype.close = function(duration, aCallback, aThisObject) {
	if (this.opened) {
		this.toggle.apply(this, arguments);
	}
}

/**
 * toggle opened/closed of the panel with animation.
 * @param {Number}   [duration]       animation duration; if nonspecified, the default duration of this instance is used.
 * @param {Function} [func]           callback function/method which is called when opening/closing animation is completed.
 * @param {Object}   [aThisObject]    object that will be a global object ('this') in 'func'.
 * @returns this instance
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.prototype.toggle = function(duration, aCallback, aThisObject) {
	if (!this.locked) {
		this.locked = true;

		var duration = (typeof duration == 'number') ? Math.max(0, duration) : this.duration;
		var callback = BAJL.Delegate(function() {
			this.opened = !this.opened;
			this.locked = false;
			if (this.opened) {
				this.addClass   (BAJL.settings.TogglePanel.className.opened);
				this.removeClass(BAJL.settings.TogglePanel.className.closed);
			} else {
				this.addClass   (BAJL.settings.TogglePanel.className.closed);
				this.removeClass(BAJL.settings.TogglePanel.className.opened);
			}
			this.doCallback('onComplete', this.opened);
		}, this);

		if ($.isFunction(aCallback)) {
			this.addCallback('onComplete', aCallback, aThisObject, 'disposable');
		}
		$(this.node).slideToggle(duration, callback);
		return this;
	}
}

/**
 * add className to constructional element nodes of thie instance.
 * @param {String} [cname]    className to add.
 * @returns this instance
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.prototype.addClass = function(cname) {
	$(this.node   ).addClass(cname);
	$(this.buttons).addClass(cname);
	return this;
}

/**
 * remove className from constructional element nodes of thie instance.
 * @param {String} [cname]    className to remove.
 * @returns this instance
 * @type BAJL.TogglePanel
 */
BAJL.TogglePanel.prototype.removeClass = function(cname) {
	$(this.node   ).removeClass(cname);
	$(this.buttons).removeClass(cname);
	return this;
}



/* -------------------- AutoSetup -------------------- */

$(function() {
	var autoSetup = BAJL.settings.TogglePanel.autoSetup;
	if (autoSetup.enabled) {
		$('body').live('click', function(e) {
			var $anchor = $(e.target).closest('a, area');
			var $panel  = $anchor.map(function() {
				var $anchor = $(this);
				var target  = $anchor.attr('target') || '_self';
				var href    = $anchor.attr('href'  ) || '';
				var hash    = $anchor.attr('hash'  ) || '#';
				var path    = href.replace(hash, '');
				return (   target == '_self'
				        && hash   != '#'
				        && (!path || path == location.href.split('#')[0])
				        && $(hash).hasClass('bajl-togglepanel'))
				        	? $(hash)
				        	: null;
			}).get(0);

			if ($panel && $panel.BAJL_HasElement() && !BAJL.TogglePanel.getInstance($panel)) {
				e.preventDefault();
				e.stopPropagation();

				// aware of a behavior of BAJL.PageScroller.
				if (typeof BAJL.PageScroller != 'undefined') {
					BAJL.PageScroller.abort();
					$anchor.focus().addClass(BAJL.GetValue('BAJL.settings.PageScroller.ignore'));
				}

				$panel.BAJL_TogglePanel($anchor);
				$anchor.triggerHandler('click');
			}
		});
	}
});



/* -------------------- for JSDoc toolkit output -------------------- */
/**
 * callback functions for {@link BAJL.TogglePanel}
 * @name BAJL.TogglePanel.callback
 * @namespace callback functions for {@link BAJL.TogglePanel}
 */
/**
 * a callback for when one of the slides is selected.
 * @name BAJL.TogglePanel.callback.onComplete
 * @function
 * @param {Boolean} opened    true when the panel block is currently opened.
 */



})(jQuery);
