/**
 * modules.js
 *
 * Provides JavaScript classes that extend the mootools library.
 */

/**
 * Global Storage
 *
 * A basic tool to store global variables or objects. Provides a
 * simple get/set style way of dropping and picking up objects or
 * variables for later use.
 *
 * TODO: add support for "Session" like capabilities.
 *
 * @author Josh Zeigler <jzeigler@resource.com>
 * @author Toby Miller <tmiller@resource.com>
 */
var Global = {
	/**
	 * storage array
	 */
	_storage: {},

	/**
	 * create a key
	 *
	 * @param void
	 * @return string key
	 */
	createKey: function() {
		var currentDate = new Date();
		return(currentDate.getTime().toString());
	},

	/**
	 * gets a global variable
	 *
	 * @param string key
	 * @return mixed object
	 */
	get: function(key) {
		return(this._storage[key]);
	},

	/**
	 * sets a global variable
	 *
	 * @param mixed value
	 * @param string key (optional)
	 * @return string key
	 */
	set: function(value, key) {
		if(key == undefined) key = createKey();
		this._storage[key] = value;
		return(key);
	},

	/**
	 * get an array of values by keyword
	 *
	 * @param keyword
	 * @return array of keys
	 */
	keywordGet: function(keyword) {
	   var matches = [];
	   for(var key in this._storage) {
			if(key.contains(keyword)) {
				matches.push(key);
			}
	   }
	   return(matches);
	}
};

Element.extend({
    getFormValues: function() {
        vals = {};
        this.getFormElements().each(function(el) {
            var name = el.name;
            var val  = el.getValue();
            
            if (val === false || !name || el.disabled) return;
            //if exists value for input checkbox, append value in a new array
            if ($chk(vals[name])&&this.type=='checkbox') vals[name] = [vals[name]]; 
            if ($type(vals[name])=='array') vals[name].push(val)
            else vals[name] = val;
        });
        return(vals);
    }
});

/**
 * Modified Moo Tools Ajax evalScripts function
 *
 * This is a modified version of the evalScripts function that is included in
 * the Moo Tools Ajax Class. This will allow JavaScript to be executed after the
 * included HTML is written to the page. It also allows execution of the body
 * onLoad() event.
 *
 * @author Josh Zeigler <jzeigler@resource.com>
 */
Ajax.implement({

	evalScripts: function(complete) {
		if (this.response) {
			//Global Properties declared.
			var script, scripts, theBody;
			var inScriptCount = 0;
			var extScriptCount = 0;
			var thePageSource = this.response.text;

			//Function that executes all scripts
			var onComplete = function() {
				if (scripts) (window.execScript) ? window.execScript(scripts) : window.setTimeout(scripts, 0);
			}

			//Function checks that all external scripts have loaded and adds onload events.
			var checkComplete = function() {
				if (extScriptCount == 0) {
					var onloadre = new RegExp(/.*<body[^>]+onload="([^"]+)".*/gi);
					var theOnLoad = onloadre.exec(thePageSource);

					//alert(onloadre.test(thePageSource));
					//Check for onload attributes with a single and double quotes. Need to work on cleaning up RegEx to handle both.
					if (theOnLoad) {
						scripts.push(theOnLoad[1]);
					}
					else {
						onloadre = new RegExp(/.*<body[^>]+onload='([^']+)'.*/gi);
						theOnLoad = onloadre.exec(thePageSource);
						if (theOnLoad) scripts.push(theOnLoad[1]);
					}

					//Scripts are joined into a string to be executed.
					if(complete != undefined) {
						scripts.push(complete);
					}
					scripts = scripts.join('\n');
					onComplete();
				}
			}

			//Function uses Ajax call to retrieve external scripts.
			var getExternalScript = function(theSrc, arrPos) {
				//If the ajax is successful, add the response to the scripts array and take one off the count.
				var onSuccess = function(responseText) {
					scripts[arrPos] = responseText;
					extScriptCount--;

					//Check to see if we are done!
					checkComplete();
				}
				//If the ajax request failes, add a comment to the scripts array and take one off the count.
				var onFailure = function() {
					//scripts[arrPos] = "\\\\Script failed to load";
					extScriptCount--;

					//Check to see if we are done!
					checkComplete();
				}
				//Request the source!
				new Ajax(theSrc, {
					'method': 'get',
					'onSuccess': onSuccess,
					'onFailure': onFailure
				}).request();
			}

			//Regular expressions for finding script blocks and external script calls with single and double quotes. Need to work on cleaning up RegEx to handle both.
			var regexp = new RegExp(/<script[^>]*>([\s\S]*?)<\/script>/gi);
			var srcre = new RegExp(/<script[^>]+src="([^"]+)".*/gi);
			var srcre2 = new RegExp(/<script[^>]+src='([^']+)'.*/gi);
			scripts = [];

			//Loop through the scripts
			while (script = regexp.exec(thePageSource)) {
				var theExtScript = srcre.exec(script);
				var theExtScript2 = srcre2.exec(script);
				//Conditional block for script sources wrapped in double quotes
				if (theExtScript) {
					extScriptCount++;
					var theSrc = theExtScript[1];
					scripts.push("");

					//Call the function that will grab the script and insert it via AJAX
					getExternalScript(theSrc, scripts.length-1);
				}
				//Conditional block for script sources wrapped in single quotes
				else if (theExtScript2) {
					extScriptCount++;
					var theSrc = theExtScript2[1];
					scripts.push("");

					//Call the function that will grab the script and insert it via AJAX
					getExternalScript(theSrc, scripts.length-1);
				}
				//Conditional block that adds an internal script block to the scripts array to be executed
				else {
					scripts.push(script[1]);
				}
			}
			//Loop finished, check to see if all JavaScript has been pulled in to be executed.
			checkComplete();
		}
	}
});

/**
 * ModalWindow
 * Builds and launches a modal window populated with an external HTTP response
 *
 * Modified version of MOOdalBox: http://www.e-magine.ro/moodalbox
 *
 * @author Toby Miller <tmiller@resource.com>
 * @author Josh Zeigler <jzeigler@resource.com>
 * @author Christopher Flohr <cflohr@resource.com>
 */
var ModalWindow = {
	// init the ModalWindow
	init: function (options) {
		// init default options
		this.options = Object.extend({
			'resizeDuration': 		900,		// Duration of height and width resizing per 100 pixels (ms)
			'initialWidth': 		200,		// Initial width of the box (px)
			'initialHeight': 		120,		// Initial height of the box (px)
			'contentsWidth': 		200,		// Actual width of the box (px)
			'contentsHeight': 		120,		// Actual height of the box (px)
			'minContentsWidth': 	200,		// Minimum width of the box (px)
			'minContentsHeight':	120,		// Minimum height of the box (px)
			'enableCaption': 		true,		// Enable/Disable caption
			'evalScripts': 			true,		// Option to evaluate scripts in the response text
			'evalResponse': 		false,		// Option to evaluate the whole response text
			'ajaxMethod':			'get',		// Option to evaluate the whole response text
			'status':				'closed',	// Status of ModalWindow (open, recycling or closed)
			'ready':				false,		// Show whether the ModalWindow is ready to be used for the first time or not
			'errorMessage':			'Oops.. there was a problem with your request.<br /><br />Please try again.<br /><br /><em>Click anywhere to close.</em>'
									// the error message displayed when the request has a problem
		}, options || {});

		// scan anchors for those opening a ModalWindow
		this.anchors = [];
		$A($$('a')).each(function(el){
			// we use a regexp to check for links that
			// have a rel attribute starting with "modalwindow"
			if(el.rel && el.href && (el.rel.test('^modalwindow', 'i') || el.rel.test('^modalbox', 'i') || el.rel.test('^moodalbox', 'i'))) {
				el.onclick = this.click.pass(el, this);
				this.anchors.push(el);
			}
		}, this);

		// add event listeners
		this.eventKeyDown = this.keyboardListener.bindWithEvent(this);
		this.eventPosition = this.position.bind(this);

		// init the HTML elements
		// the overlay (clickable to close)
		this.overlay = new Element('div').setProperty('id', 'mb_overlay').setStyle('display', 'none').injectInside(document.body);

		// the center element
		this.center = new Element('div').setProperty('id', 'mb_center').setStyles({'width': '0px', 'height': '0px', 'marginLeft': '-' + (this.options.initialWidth / 2) + 'px', 'display': 'none'}).injectInside(document.body);

		// the actual page contents
		this.contents = new Element('div').setProperty('id', 'mb_contents').injectInside(document.body);//this.center);

		// the top part (caption / close)
		this.titleBar = new Element('div').setProperty('id', 'mb_titleBar').setStyle('display', 'none').injectInside(document.body);
		this.closelink = new Element('a').setProperties({'id': 'mb_close_link', 'href': 'javascript:void(0);'}).injectInside(this.titleBar);
		this.caption = new Element('div').setProperty('id', 'mb_caption').injectInside(this.titleBar);
		new Element('div').setProperty('class', 'clear').injectInside(this.titleBar);

		this.error = new Element('div').setProperty('id', 'mb_error').setHTML(this.options.errorMessage);

		// attach the close event to the close button / the overlay
		this.closelink.onclick = this.overlay.onclick = this.close.bind(this);

		this.ajaxRequest = Class.empty;
		this.options.ready = true;
	},

	click: function(link) {
		return(this.open(link.href, link.title, link.rel));
	},

	/**
	 * open
	 *
	 * Opens a new window and populates it with markup code from either a local
	 * (to the same domain) GET url or a form submission result.
	 *
	 * @param string url to load
	 * @param string title to display
	 * @param string relationship information (width & height)
	 * @param [optional] string form id (if submitting a form)
	 * @return boolean always false for href cancellation (call from onclick)
	 */
	open: function(sLinkHref, sLinkTitle, sLinkRel, sFormId) {
		if (this.options.status == 'open')
		{
			// close the modal window before attempting to "re-open" it
			this.options.status = 'recycling';
			this.close();
		}
		this.href = sLinkHref;
		this.title = sLinkTitle;
		this.rel = sLinkRel;
		this.formId = (sFormId ? sFormId : "");

		if(this.formId != "") this.method = ($(this.formId).getProperty('method') != "" ? $(this.formId).getProperty('method') : this.options.ajaxMethod);
		else this.method = this.options.ajaxMethod;

		this.position();
		if (this.options.status != 'recycling') this.prepare(true);

		this.top = (Window.getScrollTop() + 30 + (Window.getHeight() / 15)) - 2; //Sets the top of the center element - all other elements cascade from this
		if (this.options.status != 'recycling') this.center.setStyle('display', '');
		this.center.setStyle('top', this.top);

		if (this.options.status != 'recycling') {
			this.overlay.setStyles({'opacity': 0, 'display': ''});
			this.overlay.effect('opacity', {'duration': this.options.resizeDuration}).start(0.7);
		}

		// check to see if there are specified dimensions
		// if not, fall back to default values
		var aDim = this.rel.match(/([0-9]|auto)+/gi);

		if(parseInt(aDim[0]) >= this.options.minContentsWidth) {
			this.options.contentsWidth = aDim[0];
			this.options.initialWidth = aDim[0];
			this.contents.setStyle('width', this.options.contentsWidth + 'px');
		}
		else {
			this.options.contentsWidth = 'auto';
			this.contents.setStyles({'float': 'left', 'width': ''});
		}

		if(parseInt(aDim[1]) >= this.options.minContentsHeight) {
			this.options.contentsHeight = aDim[1];
			//this.options.initialHeight = aDim[1];
			this.contents.setStyles({'height': this.options.contentsHeight + 'px'});
		}
		else {
			this.options.contentsHeight = 'auto';
			this.contents.setStyles({'float': 'left', 'height': ''});
		}

		this.contents.setStyles({'top': this.top+'px'});

		if (this.options.status != 'recycling')
		{
			this.titleBar.setStyles({'opacity': '0', 'height': '0px', 'display': 'none'});
			this.center.setStyles({'width': this.options.initialWidth + 'px', 'height': this.options.initialHeight + 'px', 'marginLeft': -(parseInt(this.options.initialWidth) / 2)});
		}
		else
		{
			this.caption.empty();
			this.titleBar.effects({'duration': this.options.resizeDuration}).start({'width': this.options.initialWidth + 'px', 'marginLeft': -(parseInt(this.options.initialWidth) / 2)});
			this.center.effects({'duration': this.options.resizeDuration}).start({'width': this.options.initialWidth + 'px', 'height': this.options.initialHeight + 'px', 'marginLeft': -(parseInt(this.options.initialWidth) / 2)});
		}

		return(this.loadContents(sLinkHref));
	},

	/**
	 * position
	 *
	 * Keep the overlay (background cover) positioned so that it completely
	 * covers the web page underneath the ModalWindow.
	 *
	 * @param void
	 * @return void
	 */
	position: function() {
		this.overlay.setStyles({'top': Window.getScrollTop() + 'px', 'height': Window.getHeight() + 'px'});
	},

	/**
	 * prepare
	 *
	 * Prepare the stage for ModalWindow to be either loaded or unloaded. The
	 * most important thing that this method does is hide ActiveX objects in
	 * Windows that would otherwise take z-index precedence over the
	 * ModalWindow and cover it from user view. The elements are then un-hidden
	 * when the ModalWindow is closed.
	 *
	 * @param boolean whether we are opening a modal or not
	 * @return void
	 */
	prepare: function(open) {
		var elements = $A($$('object'));
		elements.extend($$(window.ActiveXObject ? "select" : "embed"));
		elements.each(function(el){ el.style.visibility = open ? "hidden" : ""; });
		var fn = open ? "addEvent" : "removeEvent";
		window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition);
		document[fn]('keydown', this.eventKeyDown);
	},

	/**
	 * loadContents
	 *
	 * Load the page contents into the ModalWindow
	 *
	 * @todo Clean up the $().send vs new Ajax() differences
	 *
	 * @param void
	 * @return void
	 */
	loadContents: function() {
		this.contents.setStyle('display', 'none');
		this.center.className = "mb_loading";
        
        var qsData = {};
        
        if(this.formId != "") {
            qsData = $(this.formId).getFormValues();
        }
        
		// AJAX call here
		var openWindow = this.openWindow.bind(this);
		var ajaxFailure = this.ajaxFailure.bind(this);
		var ajaxOptions = {
			'method':			this.method,
			'update':			this.contents,
            'data':           qsData,
			'evalScripts': 	false,
			'evalResponse': 	this.options.evalResponse,
			'onComplete': 	openWindow,
			'onFailure': 		ajaxFailure
		};

		this.ajaxRequest = new Ajax(this.href, ajaxOptions).request();

		return(false);
	},

	ajaxFailure: function (){
		this.contents.setHTML('');
		this.error.clone().injectInside(this.contents);
		this.openWindow();
		this.center.setStyle('cursor', 'pointer');
		this.titleBar.setStyle('cursor', 'pointer');
		this.center.onclick = this.titleBar.onclick = this.close.bind(this);
	},

	getDimensions: function() {
		this.contents.setStyles({'display': ''});
		if (this.options.contentsWidth == "auto") {
			if(this.contents.offsetWidth > (Window.getWidth()-60)) this.options.contentsWidth = (Window.getWidth()-60);
			else if(this.contents.offsetWidth < this.options.minContentsWidth) this.options.contentsWidth = this.options.minContentsWidth;
			else this.options.contentsWidth = this.contents.offsetWidth;
		}

		if(this.options.contentsHeight == "auto") {
			if((this.contents.offsetHeight > (Window.getHeight()-60)) && ((Window.getHeight()-60) > 0)) this.options.contentsHeight = (Window.getHeight()-60);
			else if(this.contents.offsetHeight < this.options.minContentsHeight || this.options.minContentsHeight > Window.getHeight()) this.options.contentsHeight = this.options.minContentsHeight;
			else this.options.contentsHeight = (this.contents.offsetHeight);
		}

		if((this.options.contentsHeight + 2) < this.contents.offsetHeight) this.options.contentsWidth = parseInt(this.options.contentsWidth) + 35;
		if((this.options.contentsWidth + 2) < this.contents.offsetWidth) this.options.contentsHeight = parseInt(this.options.contentsHeight) + 35;

		this.contents.setStyle('float', 'none')
		this.center.adopt(this.contents);
	},

	openWindow: function() {
		this.getDimensions();

		this.center.setStyle('background', '#FFFFFF');
		this.center.setStyle('cursor', 'default');
		this.titleBar.setStyle('cursor', 'default');
		this.center.onclick = this.titleBar.onclick = "";
		this.caption.setHTML(this.title);
		this.contents.setStyles({'width': this.options.contentsWidth + 'px', 'height': this.options.contentsHeight + 'px'});
		this.center.setStyles({'width': this.contents.offsetWidth + 'px', 'marginLeft': -(parseInt(this.contents.offsetWidth) / 2)});

		if(this.options.enableCaption) {
			this.titleBar.setStyles({'top': (this.top-27) + 'px', 'width': this.contents.style.width, 'marginLeft': this.center.style.marginLeft, 'display': ''});
			this.titleBar.setStyles({'opacity': '1', 'height': this.titleBar.scrollHeight + 'px'});
		}

		var displayWindow = function() {
			this.contents.effect('opacity', {'duration': ModalWindow.options.resizeDuration, 'onComplete': function(){ModalWindow.contents.setStyle('overflow', 'auto')}}).start(0,1);
			if (this.options.evalScripts) this.ajaxRequest.evalScripts();
			ModalWindow.options.status = 'open';
		}

		var fx = [];
		var queue = new Group(null);

		if(this.center.clientWidth != this.contents.offsetWidth) {
			var fxWidth = this.center.effects({'duration': this.options.resizeDuration, 'transition': Fx.Transitions.Expo.easeOut}).start({'width': [this.center.clientWidth, this.contents.offsetWidth]});
			fx.push(fxWidth);
		}

		if(this.center.clientHeight != this.contents.offsetHeight) {
			var fxHeight = this.center.effects({'duration': this.options.resizeDuration, 'transition': Fx.Transitions.Expo.easeOut}).start({'height': [this.center.clientHeight, this.contents.offsetHeight]});
			fx.push(fxHeight);
		}

		queue.initialize.apply(queue, fx);
		queue.addEvent('onComplete', displayWindow.bind(this));
	},

	keyboardListener: function(event) {
		// close the ModalWindow when the user presses CTRL + W, CTRL + X, ESC
		if((event.control && event.key == "w") || (event.control && event.key == "x") || (event.key == "esc")) {
			this.close();
			event.stop();
		}
	},

	close: function() {
		if (this.options.status != 'recycling')
		{
			// close the overlay
			this.center.setStyles({'width': '0px', 'height': '0px'});
			this.center.setStyle('display', 'none');
			this.titleBar.setStyle('display', 'none');
			this.center.setStyle('background', '#DDDDDD url(../images/moodalbox/loading.gif) no-repeat center center;');
			this.center.className = "mb_loading";
			this.contents.setStyle('display', 'none');
			this.contents.setStyles({'opacity': '0', 'overflow': 'hidden'});
			document.body.adopt(this.contents);
			this.center.setStyles({'width': '0px', 'height': '0px'});
	
			this.overlay.setStyle('opacity', '0');
	
			this.options.contentWidth = this.options.initialWidth;
			this.options.contentHeight = this.options.initialHeight;

			this.prepare(false, this);
			this.options.status = 'closed';
		}
		else
		{
			// cancel closing, but allow for a fade from one overlay to the next
			this.center.setStyle('background', '#FFFFFF url(../images/moodalbox/loading.gif) no-repeat center center;');
			//this.center.className = "mb_loading";
			this.contents.setStyles({'overflow': 'hidden'});
			this.contents.effect('opacity', {'duration': this.options.resizeDuration}).start(0.000000000000001);
			//document.body.adopt(this.contents);
	
			//this.options.contentWidth = this.options.initialWidth;
			//this.options.contentHeight = this.options.initialHeight;
		}

		return(false);
	}

};

// Create an alias for ModalWindow (once lived life as ModalBox)
var ModalBox = {
	open: function(sLinkHref, sLinkTitle, sLinkRel, sFormId) { ModalWindow.open(sLinkHref, sLinkTitle, sLinkRel, sFormId); },
	close: function() { ModalWindow.close(); }
}

// Create an alias for ModalWindow (once lived life as MOOdalBox)
var MOOdalBox = {
	open: function(sLinkHref, sLinkTitle, sLinkRel, sFormId) { ModalWindow.open(sLinkHref, sLinkTitle, sLinkRel, sFormId); },
	close: function() { ModalWindow.close(); }
}

// Initialize the ModalWindow onDomReady
Window.onDomReady(ModalWindow.init.bind(ModalWindow));

/**
 * modalready
 *
 * Executes a function when the modal window codebase is loaded.
 *
 * @author Toby Miller <tmiller@resource.com>
 */
Element.Events.modalready = {
	add: function(fn){
		if (ModalWindow.ready) {
			fn.call(this);
			return;
		}
		else
		{
			var modalReady = function() {
				if (ModalWindow.ready) return;
				ModalWindow.ready = true;
				ModalWindow.timer = $clear(ModalWindow.timer);
				this.fireEvent('modalready');
			}.bind(this);

			ModalWindow.timer = function() {
				if (ModalWindow.options.ready) modalReady();
			}.periodical(50);
		}
	}
};

/*compatibility*/

window.onModalReady = function(fn){
	return this.addEvent('modalready', fn);
};

/**
 * Countable
 * Limits the number of characters that can be entered into a
 * textarea field and displays a running count to the user.
 *
 * Original code provided by Stephane Thomas via the mootools
 * forums.
 *
 * @author Toby Miller <tmiller@resource.com>
 */
var Countable = new Class({

	/**
	 * defaultOptions
	 * The default options set if the user doesn't overwrite
	 */
	defaultOptions: {
		'maximum': 30,
		'offset': 20,
		'warningPercentage': 90,
		'inject': 'after',
		'title': '',
		'labelId': ''
	},

	/**
	 * initialize
	 * Initialize an instance of Countable
	 *
	 * @param string textarea element id
	 * @param mixed array or object represention of options
	 * @return void
	 */
	initialize: function(inputId, options) {
		var htmlText = '';

		// Merges the default options with the ones given as parameters
		this.setOptions($merge(this.defaultOptions, options));

		// Retrieves the textarea element based on its name
		input = $(inputId);

		if (input) {
			// Build appropriate HTML
			if (this.options.title != '') htmlText = this.options.title + ' (' + this.options.maximum + ' max characters):';
			else htmlText = this.options.maximum + ' characters allowed';

			// Creates an element to serve as a handle, if not provided
			if (this.options.labelId != '')
			{
				this.handle = $(this.options.labelId);
				this.handle.setHTML(htmlText);
			}
			else
			{
				this.handle = new Element('div', {'class': 'count'});
				if (this.options.inject == 'after') this.handle.setHTML(htmlText).injectAfter(input);
				else this.handle.setHTML(htmlText).injectBefore(input);
			}

			// Fires action upon events
			input.addEvent('keydown', this.onKeyPress.bindWithEvent(this));
			input.addEvent('keyup', this.onKeyPress.bindWithEvent(this));

			// Keeps a reference to the handle
			this.input = input;
		}
	},

	/**
	 * onKeyPress
	 * Trap onKeyPress event and use it as a trigger to update the counter
	 *
	 * @param event event object
	 * @return void
	 */
	onKeyPress: function(event) {
		event = new Event(event);
		if(!event.shift && !event.control && !event.alt && !event.meta) this.update();
	},

	/**
	 * update
	 * Update the counter as well as some counter options as the user
	 * interacts with the textarea.
	 *
	 * @param void
	 * @return void
	 */
	update: function() {
		// Removes the character entered as the maximum has been reached
		if (this.input.value.length > this.options.maximum) this.input.value = this.input.value.substring(0, this.options.maximum);

		// Calculates the number of characters left
		var remaining = this.options.maximum - this.input.value.length;

		// Default text color for this label
		var startColor = new Color('#111100');

		// Highlight color for this label to show when the limit has been hit
		var endColor = new Color('#EE0000');

		// If the content has gone over a set percentage of allowable text,
		// represent the label count with color, else make sure it's the
		// same color as the rest of the label text
		var highlightColor = startColor;
		if (this.input.value.length >= (this.options.maximum * (this.options.warningPercentage * .01))) highlightColor = startColor.mix(endColor, parseInt((1 - (remaining / (this.options.maximum * ((100 - this.options.warningPercentage) * .01)))) * 100));

		// Handles the display of the number of remaining characters
		if (this.input.value.length >= (this.options.maximum - this.options.offset)) {
			// Processes message
			var charMessage = '<span style="color:' + highlightColor.rgbToHex() + '">' + this.input.value.length + ' of ' + this.options.maximum + '</span> characters';

			// Processes title with message (if applicable)
			var htmlText = '';
			if (this.options.title != '') htmlText = this.options.title + ' (' + charMessage + '):';
			else htmlText = charMessage;

			// Displays information message
			this.handle.setHTML(htmlText);
		}
	}
});

// Adds options management support
Countable.implement(Options.prototype);

/**
 * Tabs
 * Builds and launches a tab driven div populated with an external HTTP response
 *
 * @usage
 * var tabs = new Tabs($('tabContainer'), {
 *      'tabSelector': '.tabOption'
 *  });
 *
 * Modified version of SimpleTabs: Harald Kirschner <mail@digitarald.de>
 *
 * @author Josh Zeigler <jzeigler@resource.com>
 * @author John Keber <jkeber@resource.com>
 */

var Tabs = new Class({

    /**
     * Options
     * 
     * show: Number, default 0: index for the initial selected tab
     * tabSelector: String, default ".tabDefault". Selector to find the tab elements under the given parent element
     * classMenu: String, default ".tabOption". className for the ul that hold the tab items
     * classWrapper: String, default "tabWrapper". className for the wrapper that holds the container elements
     * classContainer:  String, default "tabContainer". className for the single container elements
     * onShow: Event. Fires when a container is shown, arguments: (tabElement, containerElement, tabIndex, tabElementOld, containerElementOld, tabIndexOld)
     * onHide: Event. Fires when a container is hidden, same arguments
     * onRequest: Event. Fires when Ajax request starts, same arguments
     * onComplete: Event. Fires when Ajax request is completed successfully, same arguments
     * onFailure: Event. Fires when a Ajax request fails, same arguments
     * getContent: Function. Callback to return the tab content element for an entry element, default is Element::getNext()
     * 
     */
    options: {
        'show': 0,
        'tabSelector': '.tabOption',
        'classWrapper': 'tabWrapper',
        'classMenu': 'tabMenu',
        'classContainer': 'tabContainer',
        'onShow': function(toggle, container, index) {
            toggle.addClass('tabSelected');
            container.setStyle('display', '');
        },
        'onHide': function(toggle, container, index) {
            toggle.removeClass('tabSelected');
            container.setStyle('display', 'none');
        },
        'onRequest': function(toggle, container, index) {
            container.addClass('tabLoading');
        },
        'onComplete': function(toggle, container, index) {
            container.removeClass('tabLoading');
        },
        'onFailure': function(toggle, container, index) {
            container.removeClass('tabLoading');
        },
        'getContent': null
    },

    /**
     * Constructor
     * 
     * @param {Element} The parent Element that holds the entry elements
     * @param {Object} Options
     */
    initialize: function(el, options) {
        this.setOptions(options);
        this.element = $(el);
        this.selected = null;
        this.build();
    },

    build: function() {
        this.entries = [];
        this.menu = new Element('ul', {'class': this.options.classMenu});
        this.wrapper = new Element('div', {'class': this.options.classWrapper});
        this.element.getElements(this.options.tabSelector).each(function(el) {
            var content = el.href || (this.options.getContent ? this.options.getContent.call(this, el) : el.getNext());
            this.addTab(el.innerHTML, el.title || el.innerHTML, content);
        }, this);
        this.element.empty().adopt(this.menu).adopt(this.wrapper);
        if (this.entries.length) this.select(this.options.show);
    },

    /**
     * Add a new tab at the end of the tab menu
     * 
     * @param {String} inner Text
     * @param {String} Title
     * @param {Element|String} Content Element or URL for Ajax
     */
    addTab: function(text, title, content) {
        if ($type(content) == 'string' && !$(content)) var url = content;
        var container = $(content) || new Element('div');
        this.entries.push({            
            'name': title,
            'container': container.setStyle('display', 'none').addClass(this.options.classContainer).inject(this.wrapper),
            'toggle': new Element('li').adopt(new Element('a', {
                'href': '#',
                'title': title,
                'events': {
                    'click': this.onClick.bindWithEvent(this, [this.entries.length])
                }
            }).setHTML(text)).inject(this.menu),
            'url': url || null
        });
        return this;
    },

    onClick: function(evt, index) {
        $('productTabs').focus();
        
        if(index != this.selected) {
            evt.stop();
            var entry = this.entries[this.selected];

            var tabImage = $("tab_" + entry.name);
            tabImage.src = "../catalog/images/tab_" + entry.name + ".gif";
            tabImage.addClass('mouseover_on');
            mouseoverSetup("tab_" + entry.name);

            this.select(index);
        }
        return(false);
    },

    /**
     * Select the tab via tab-index
     * 
     * @param {Number} Tab-index
     */
    select: function(index) {
        if (this.selected === index || !this.entries[index]) return this;
        var entry = this.entries[index];
        
        var params = [entry.toggle, entry.container, index];
        if (this.selected !== null) {
            var current = this.entries[this.selected];
            if (this.ajax && this.ajax.running) this.ajax.cancel();
            params.concat([current.toggle, current.container, this.selected]);
            this.fireEvent('onHide', [current.toggle, current.container, this.selected]);
        }
        
        var tabImage = $("tab_" + entry.name);
        tabImage.src = "../catalog/images/tab_" + entry.name + "_active.gif";
        tabImage.removeEvents('mouseenter');
        tabImage.removeEvents('mouseleave');
        tabImage.removeClass('mouseover_on');
        
        this.fireEvent('onShow', params);
        if (entry.url && !entry.loaded) {
            this.ajax = new Ajax(entry.url, $merge({
                'onRequest': this.fireEvent.pass(['onRequest', params], this),
                'onFailure': this.fireEvent.pass(['onFailure', params], this),
                'onComplete': function(resp) {
                    entry.loaded = true;
                    entry.container.empty().setHTML(resp);
                    this.fireEvent('onComplete', params);
                }.bind(this)
            }, this.options.ajaxOptions)).request();
        }
        this.selected = index;
        return this;
    }

});

Tabs.implement(new Events, new Options);
