/*
 * Expandable, a jQuery plugin to dynamically group and hide web content
 * Copyright (C) 2009  Marc Diethelm
 * License: (GPL 3, http://www.gnu.org/licenses/gpl-3.0.txt) see license.txt
 */

(function($j)
{

	// BUG:
	// worksforme? nested expanders: icon of inner expander way too low, header to tall (innerHeight is wrong!)

	// TO DO:
	// create a mechanism using css to make expandable invisible before creation (but have height); use .css() to make it visible when finished (as I did with new-nav in my portfolio)
	// option: content css [add inline]
	// use line-height on title... (if not empty)
	// customizable icon on right side of header (throbber, add icon, etc) with onclick callback

	// going UI will break the callback arg#2

	$j.fn.expandable = function()
	{

		//console.log(typeof $j.ui);

		var action;
		var userOptions = {};

		if ( arguments.length == 1 ) {

			action = ( arguments[0].constructor == String ? arguments[0] : null );
			userOptions = ( arguments[0].constructor == Object ? arguments[0] : null );
		} else if ( arguments.length == 2 ) {

			action = ( arguments[0].constructor == String ? arguments[0] : null );
			userOptions = arguments[1];
		}



		// apply options vs. settings

        //if (userOptions) {
        //    var options = $j.extend({}, $j.fn.expandable.defaults, userOptions);
        //}

        var options = $j.extend({}, $j.fn.expandable.defaults, userOptions);


		return this.each( function() {

			if (action == "destroy") { // this certainly is not the way it's done. But I don't know better for now, and it works.
				// restore original element
				$j(this).removeClass("ui-widget ui-expandable ui-expandable-open");

				$j(".ui-widget-content", this).remove().contents().appendTo(this);

				if ( $j(this).data("elTitle") ) {
					$j(".ui-widget-header", this).unbind("click").remove();
					$j(this).prepend( $j(this).data("elTitle") );
				}

				return this;

			} else if (action == "close") {

				this.closeExpandable();
				return this;

			} else if (action == "open") {

				this.openExpandable(null, options); // hack for restore onload, with arg "open" and one-time options (eg: {duration: 0})
				return this;
			}


			$j(this).hide() // hide everything quickly
			.addClass("ui-expandable ui-widget");

			var title = "";

			// user has created a title child. replaced later.
			if ( $j(".ui-expandable-title", this).length > 0 ) {
				var $jtitle = $j(".ui-expandable-title", this).eq(0).remove();
				title = $jtitle.text();
				$j(this).data("elTitle", $jtitle);
				delete $jtitle;
			}

			title = options.title || title;

			// wrap the content in an animatable box
			if ( $j(".ui-widget-content", this).length == 0 ) {

				//$j(this).wrapInner('<div class="ui-widget-content ui-helper-clearfix"></div>'); // fails if there is no content. jQuery bug #3552
				// workaround (same as my patch to jQuery trunk)
				var html = '<div class="ui-widget-content ui-helper-clearfix"></div>';
				var $jthis = jQuery( this );

				if ( $jthis.contents().length ) {
					$jthis.contents().wrapAll( html );
				} else {
					$jthis.html( html );
				}
			}

			var $jcontent = $j(".ui-widget-content", this);

			if ( options.startopen ) {
				$j(this).addClass("ui-expandable-open");
			} else {
				$jcontent.hide();
			}

			if ( options.uiIconClosed && options.uiIconOpen ) {
				//console.log("ui closed = %s, ui open = %s, %s", options.uiIconClosed, options.uiIconOpen, $j(this).attr("id"))
				var iconstartclass = ( options.startopen ? options.uiIconOpen : options.uiIconClosed );
				var iconclosedclass = options.uiIconClosed;
				var iconopenclass = options.uiIconOpen;
			} else {
				//console.log("default icon (non UI), %s", $j(this).attr("id"))
				var iconstartclass = ( options.startopen ? "icon-open" : "icon-closed" );
				var iconclosedclass = "icon-closed";
				var iconopenclass = "icon-open";
			}

			var extra_icon = "";

			if ( options.extraIcon ) {
				extra_icon = " ui-icon " + options.extraIcon;
			}

			//var $jtitle = $j('<div class="ui-state-default ui-widget-header" title="'+options.tooltip+'"><div class="ui-icon '+iconstartclass+'"></div><div class="ui-expandable-title">'+title+'</div></div>');


			var $jtitle = $j(
				'<div class="ui-state-default ui-widget-header" title="'+options.tooltip+'">' +
				'	<div class="ui-expandable-icon ui-icon '+iconstartclass+'"></div>' +
				'	<div class="ui-expandable-title">'+title+'</div>' +
				'	<div class="ui-expandable-extraIcon'+extra_icon+'"></div>' +
				'</div>'
			);

			$j(".ui-widget-header", this).length ? $j(".ui-widget-header", this).replaceWith($jtitle) : $j(this).prepend($jtitle);

			$j(this).show(); // show the finished expander

			// adjust vertical position of the icon to look nice // produces weird bug in nested expanders, grr.
			var $jicon = $j(".ui-icon", this);
			//$jtitle.css("background-color", "red");
			//$jicon.css("background-color", "yellow");
			var y_offset = Math.floor((($jtitle.innerHeight() - $jicon.height()) / 2 ));
			//console.log("title innerHeight = %i", $jtitle.innerHeight());
			//console.log("title height = %i", $jtitle.height());
			//console.log($jtitle.get(0));
			if (y_offset > 0) {
				$jicon.css("margin-top", y_offset);
			}


			// I wish I understood the scope complexities better...
			// maybe it can be cleaned up...
			var $jexpandable = $j(this);
			var $jself = this; // actually it should be self not $jself, but self is the window

			if (options.close) {
				this._close = options.close; // I need 'this' in the callback to be the one from this scope. // ...use call
			};

			if (options.open) {
				this._open = options.open;
			}


			$jtitle.bind("click", null, function(event)
			{ // register clicks on title

				if ( $jexpandable.hasClass("ui-expandable-open") ) {

					$jself.closeExpandable(event);

				} else {

					$jself.openExpandable(event, options);
				};
			});


			this.closeExpandable = function(_event)
			{

				$j(".ui-expandable-icon", $jtitle).removeClass(iconopenclass).addClass(iconclosedclass);

				// closing now
				$jcontent.animate(options.animationClose, options.duration, options.easing, function () {

					$jexpandable.removeClass("ui-expandable-open");
					// user callback
					if (options.close) {
						$jself._close(_event, options);
					}
				});
			};


			this.openExpandable = function(_event, options)
			{

				$j(".ui-expandable-icon", $jtitle).removeClass(iconclosedclass).addClass(iconopenclass);

				// opening now
				$jcontent.animate(options.animationOpen, options.duration, options.easing, function()
				{

					$jexpandable.addClass("ui-expandable-open");
					// user callback
					if (options.open) {
						$jself._open(_event, options);
					}
				});
			};


			$jtitle.hover(
				function() {
					$j(this)
					.removeClass("ui-state-default")
					.addClass("ui-state-hover");
				},
				function() {
					$j(this)
					.removeClass("ui-state-hover")
					.addClass("ui-state-default");
				}
			);

			return this;

		});

	};



	$j.fn.expandable.defaults = {
		startopen: false,
		title: null,
		tooltip: "Click to expand",
		uiIconClosed: "ui-icon-triangle-1-e",
		uiIconOpen: "ui-icon-triangle-1-s",
		/*uiIconClosed: null,
		uiIconOpen: null,*/
		animationClose: { height: "hide" },
		animationOpen: { height: "show" },
		duration: 500,
		easing: "swing",
		open: null, // callbacks: also not the way it's done it seems... (but works alright)
		close: null,
		extraIcon: null
	};


})(jQuery);
