/*==============================================================================
 *	TroonGolf functions class
 */

window.TroonGolf = {
/*	const	*/	ERROR : 'error',
/*	const	*/	WARNING : 'warning',
/*	const	*/	SUCCESS : 'success',

	numbers_only : function(s)
	{
		var t = new String();
		for(var i=0; i < s.length; i++)
		{
			if( new String( parseInt(s.charAt(i)) ) == s.charAt(i) )
			{
				t = t+s.charAt(i);
			}
		}
		return(parseInt(t));
	},
	
	flagPageWithMessage : function(msg, type)
	{				
		var target = $('alerts');
		var erdiv = document.createElement('DIV');
		
		Element.addClassName(erdiv,type);
		erdiv.appendChild( document.createTextNode(msg) );		
		target.appendChild(erdiv);
		Effect.Appear("alerts",{duration: 0.25, queue: "end"});	
		Effect.BlindUp(	"alerts",	{	duration: 0.5, 
										delay: 1, 
										queue: "end", 
										afterFinish : function() 
										{ 
											for(var i=target.childNodes.length; i > 0; i--)
											{
												target.removeChild(target.childNodes[i-1]);
											}
										}
									}
		);	
	}
}
  

/*==============================================================================
 *	Ajax warning management class
 */

window.PageAlert = {
	loadingTimeout	: 2000,
	alertsTimeout	: 3000,
	
	showingLoading 	: false,
	hideLoading 	: 0,
	showingAlerts 	: false,
	hideAlerts 		: 0,
	
	doLoad 			: function()
	{	//if it's hidden, show it with an appear.
		if(this.showingLoading == false)
		{
			Effect.Appear('ajax_loading',{duration: 0.25, queue: 'end'});
			this.showingLoading = true;
		}
		//cancel the previous timeout and go now with this one.
		this.resetHideLoading();
		//if we're still doing alerts, reset their timeout too
		if(this.showingAlerts === true)
		{
			this.resetHideAlerts();
		}
	},
	
	doAlerts 		: function()
	{	//if it's hidden, show it with an appear.
		if(this.showingAlerts == false)
		{
			Effect.Appear('alerts',{duration: 0.25, queue: 'end'});
			this.showingAlerts = true;
		}
		//cancel the previous timeout and go now with this one.
		this.resetHideAlerts();
	},
	
	resetHideAlerts : function() {
		window.clearTimeout(this.hideAlerts);
		this.hideAlerts = window.setTimeout(	
			function(){
				Effect.BlindUp(	'alerts',
								{	duration: 0.5, 
									queue: 'end',
									afterFinish : function() 
									{ 
										for(var i=$('alerts').childNodes.length; i > 0; i--)
										{
											$('alerts').removeChild($('alerts').childNodes[i-1]);
										}
									}
								}
				);
				PageAlert.showingAlerts = false;
			},
			this.alertsTimeout		
		);		
	},
	
	resetHideLoading : function() {
		window.clearTimeout(this.hideLoading);
		this.hideLoading = window.setTimeout(	
			function(){
				Effect.BlindUp(	'ajax_loading',
								{	duration: 0.5, 
									queue: 'end'
								}
				);
				PageAlert.showingLoading = false;
			},
			this.loadingTimeout		
		);
	}
};

Ajax.Responders.register({
	onCreate: function() {
		PageAlert.doLoad();
	},
	onComplete: function() {
		PageAlert.doAlerts();
	}
});

/*==============================================================================
 *	ErrorLog class
 */

function ErrorLog()
{
	if(!window.ErrorLogInstance)
	{
		window.ErrorLogInstance = this;
	}
	
	this.errors = new Array();
	
	this.add = function(msg)
	{
		this.errors[this.errors.length] = msg;
	}
	
	this.get = function()
	{
		op  = '<ol>';
		for(var i=0; i < this.errors.length; i++)
		{
			op += '<li>'+this.errors[i]+'</li>';
		}
		op += '</ol>';
		return(op);
	}
}

window.ErrorLogInstance = new ErrorLog();


/*==============================================================================
 *	FunctionListNode class and init instance
 */

/**
 *	A double linked list node comprised of a reference to the
 *	parent, the child, a name, and a function.
 */
function FunctionListNode ( func, parent, name )
{	//Here, we allow a name property to be set as a third argument
	//we need to know the parent, child, and function to call
	this.name = name;
	this.parent = parent;
	this.child = null;
	this.func = func;
	
	//run the function
	this.run = function() {
		alert('running '+this.name);
		if(arguments.length == 1 && arguments[0] != null)
		{
			this.func(arguments[0]);
		} else {
			this.func();
		}
	}
	
	/**
	 *	Insert a node after this one, and return that node. Preserves
	 *	child onto the new child.
	 */
	this.insertAfter = function( func ) {
		var name = null;
		if(arguments.length > 1) { name = arguments[1]; }		
		var childNode = this.child;			
		this.child = new FunctionListNode( func, this, name );
		this.child.child = childNode;
		if(this.child.child && this.child.child.parent)
		{
			this.child.child.parent = this.child;
		}
		return(this.child);
	}
	

	
	/**
	 *	Insert a node before this one, and return that node. Preserves
	 *	child onto this.
	 */
	this.insertBefore = function( func ) {	
		if(arguments.length > 1) { name = arguments[1]; } else { name = null; }		
		if(this.parent)
		{ //has a parent		
			this.parent.insertAfter(func, name);
			return(this.parent);
		} 
		else 
		{ //is a head
			this.parent = new FunctionListNode( func, null, name );
			this.parent.child = this;
			return(this.parent);
		}
	}
	
	//get the next element if it exists, null if not.
	this.next = function() {
		return(this.child);
	}
	
	//get the previous element if it exists, null if not.
	this.previous = function() {
		return(this.parent);
	}

	//delete this node and return a reference to the function.
	this.remove = function() {
		if(this.parent == null)
		{
			return(false);
		}
		this.parent.child = this.child;
		return(this.func);
	}
}


/**
 *	A double linked list of named and unnamed functions.
 */
function FunctionList()
{
	this.head = null;
	this.tail = null;
	
	//add another element to the end
	this.add = function( func ) {
		if(arguments.length > 1) { name = arguments[1]; } else { name = null; }				
		if(this.head == null)
		{
			this.head = new FunctionListNode( func, null, name );
			this.tail = this.head;
		}
		else
		{
			this.tail = this.tail.insertAfter( func, name );
		}
	}
	
	/**
	 *	Adds a new node after the named one if it exists and returns true,
	 *	otherwise adds it to the end and returns false.
	 */
	this.addBefore = function( findName, func ) {
		if(arguments.length > 2) { newName = arguments[2]; } else { newName = null }				
		if(this.head == null)
		{
			this.head = new FunctionListNode( func, null, newName );
			this.tail = this.head;
			return(false);
		}
		var lookAt = this.head;
		var index = 0;
		var foundIt = false;
		while(foundIt == false && lookAt != null)
		{
			if(lookAt.name === findName)
			{
				if(index === 0)
				{
					this.head = lookAt.insertBefore( func, newName );
				} else {
					lookAt.insertBefore( func, newName );
				}
				foundIt = true;
			}
			index++;
			lookAt = lookAt.child;
		}
		if(foundIt === true)
		{
			return(true);
		} else {
			this.add( func, newName );
			return(false);
		}
	}

	/**
	 *	Adds a new node after the named one if it exists and returns true,
	 *	otherwise adds it to the end and returns false.
	 */
	this.addAfter = function( findName, func ) {
		if(arguments.length > 2) { newName = arguments[2]; } else { newName = null }				
		if(this.head == null)
		{
			this.head = new FunctionListNode( func, null, newName );
			this.tail = this.head;
			return(false);
		}

		var lookAt = this.head;
		var foundIt = false;
		while(foundIt == false && lookAt != null)
		{
			if(lookAt.name === findName)
			{
				lookAt.insertAfter( func, newName );
				foundIt = true;
			}
			lookAt = lookAt.child;
		}
		if(foundIt === true)
		{
			return(true);
		} else {
			this.add( func, newName );
			return(false);
		}
	
	}
	
	/**
	 *	Adds a new node after the indexed one if it exists and returns true,
	 *	otherwise adds it to the end and returns false.
	 */
	this.addBeforeIndex = function( index, func ) {
		if(arguments.length > 2) { newName = arguments[2]; } else { newName = null }				
		if(this.head == null)
		{
			this.head = new FunctionListNode( func, null, newName );
			this.tail = this.head;
			return(false);
		}
		if(index == 0)
		{//we're adding to the head;
			this.head = this.head.insertBefore( func, newName );
			return(true);
		}
		
		var lookAt = this.head;
		var i = 0;
		while(lookAt != null && i < index)
		{
			lookAt = lookAt.child;
			i++;
		}	
		if(i == index)
		{//found it
			lookAt.insertBefore( func, newName );
		}
		else
		{//index too large
			this.add( func, newName );
		}
		
	}	
	
	/**
	 *	Adds a new node after the indexed one if it exists and returns true,
	 *	otherwise adds it to the end and returns false.
	 */
	this.addAfterIndex = function( index, func ) {
		if(arguments.length > 2) { newName = arguments[2]; } else { newName = null }				
		if(this.head == null)
		{
			this.head = new FunctionListNode( func, null, newName );
			this.tail = this.head;
			return(false);
		}
	
		var lookAt = this.head;
		var i = 0;
		while(lookAt != null && i < index)
		{
			lookAt = lookAt.child;
			i++;
		}	
		if(i == index)
		{//found it
			lookAt.insertAfter( func, newName );
		}
		else
		{//index too large
			this.add( func, newName );
		}

	}	
	
	this.popHead = function()
	{
		if(this.head == null)
		{
			return(false);
		}
		var func = this.head.func;
		this.head = this.head.child;
		if(this.head != null)
		{//there's a node below
			this.head.parent = null;
		}
		return(func);
	}
	
	this.popTail = function() {
		var func = this.tail.remove();
		return(func);
	}
	
	//run the queue
	this.run = function() {
		var e = (arguments.length == 1 ? arguments[0] : null);
		if(this.head == null)
		{
			return(false);
		} else {
			var head = this.head;
			while(head != null)
			{
				head.run(e);
				head = head.next();
			}
			return(true);
		}
	}
	
	this.runAndClear = function() {
		var e = (arguments.length == 1 ? arguments[0] : null);
		if(this.head == null)
		{
			return(false);
		} else {
			var func = null;
			do {
				func = this.popHead();
				if(func !== false)
				{
					func(e);
				}
			} while(this.head != null);
		}					
	}
}

/*==============================================================================
 *	random functions
 */
			
function application_navigation_toggle(toggle_from,toggle_to)
{
	var toggle_from_element = "application_navigation_" + toggle_from + "_links";
	var toggle_to_element = "application_navigation_" + toggle_to + "_links";
	[toggle_from_element,toggle_to_element].each(Element.toggle);
	new Effect.Highlight("application_navigation", {duration: 1.6, startcolor:'#fff4df'});
//	new Effect.Shake("application_navigation");
}


function isUndefined(v) { 
	var undef;
	return v===undef; 
}


/* Commented out because the $$ assigner is disabled
function do_popup(url, target, features) {
	var _POPUP_FEATURES = '';
	if (isUndefined(features)) { features = _POPUP_FEATURES; }
	if (isUndefined(target)) { target = '_blank'; }
	var new_window = window.open(url, target, features);
	new_window.focus();
	return new_window;
}

function better_popup_features(features) {
	return function(e) { 
		Event.stop(e);
		var target = (e.srcElement ? e.srcElement : e.target);
		do_popup(target,'',features); 
	}
}

function better_popup(e) {
	Event.stop(e);
	var target = (e.srcElement ? e.srcElement : e.target);
	do_popup(target);
}
*/
/*
Example of how to add to the init function so that override features can be defined for specific links
Must first stop the global popup link observer and then trigger a new one with the custom window features

window.InitFunctionsLinkedList.add(
		function() { 
			Event.stopObserving($('links_generator'),'click',better_popup);
			Event.observe($('links_generator'),"click",better_popup_features('width=700,height=700'));
		},'popups');
*/

/*
 *END DEFINITIONS, BEGIN CALLS
 */

window.InitFunctionsLinkedList = new FunctionList();

Event.observe(	window,
				'load',
				function(e) 
				{
					window.InitFunctionsLinkedList.runAndClear(e);
				},
				false
);

/* Add Observer for any element that has a class of popup_link to use the better_popup function */


/* THIS IS EVIL, NEVER USE PROTOTYPE $$ OPERATOR
window.InitFunctionsLinkedList.add(
		function() { 
			$$(".popup_link").each(
				function(element){
					Event.observe(element,"click",better_popup); 
				}
			);
		},'popups');
*/		
		
/*==============================================================================
 *	FormValidationObject instance
 */


/**
 *	Creates the form validator, registeres with the init list, then
 *	allows you to pass things to it to validate.
 */
window.FormValidationObject = {
/*	const */	version 			: '0.2',		//version information
/*	const */	errorElementClass 	: 'formValidationErrorClass',
/*	const */	errorDivClass 		: 'formValidationErrorClassMessageDiv',
/*	const */	errorEnclosureClass : 'formValidationContainerErrorClass',	
/*	var	*/	errorDivsToDelete : [],				//added divs
/*	var	*/	errorElementsToUnstyle : [],		//errored input elements
/*	var	*/	errorEnclosuresToUnstyle : [],		//errored enclosures
/*	var	*/	forms : [],							//form elements
/*	var	*/	elementListAndCheckFunction : [], 	//array of {check: [objs] , against: funcref }

	/**
	 *	Automatically runs on creation.
	 */
	constructor : function()
	{
		if(!window.InitFunctionsLinkedList)
		{
			window.ErrorLogInstance.add('FormValidation without InitFunctionLinkedList');
			return;
		}
		else
		{
			window.InitFunctionsLinkedList.add(	FormValidationObject.initializeForms, 'FormValidation');
		}
	},
	
	formSubmissionHandler : function(e) {
		var target = (e.srcElement ? e.srcElement : e.target);//Event.element(e);
		while(target.nodeName != 'FORM')
		{
			target = target.parentNode;
		}
		var valid = FormValidationObject.check(target);
		if(valid === false)
		{
			Event.stop(e);
		} else {
			if(target.doSubmit)
			{
				Event.stop(e);
				target.doSubmit(target);
			}
		} 
		return(valid);
	},
	
	initializeForms : function()
	{
		this.forms = new Array();
		for(var i=0; i < document.forms.length; i++)
		{
			this.forms[i] = document.forms[i];
			this.forms[i].hasPassedValidation = false;
			Event.observe( this.forms[i], 'submit', FormValidationObject.formSubmissionHandler, true);
		}
	},
	
	destructor : function()
	{
		for(var i=0; i < document.forms.length; i++)
		{
			document.forms[i].hasPassedValidation = null;
		}		
	},
	
	
	add : function(obj, func)
	{
		var next = this.elementListAndCheckFunction.length;
		if(typeof(obj) == 'string' /* is an id*/ )
		{
			var ray = new Array();
			ray[0] = obj;
			this.elementListAndCheckFunction[next] = {check:ray,against:func};
		}
		else if(typeof(obj) == 'object' && obj.nodeName /*is a reference*/)
		{
			var ray = new Array();
			ray[0] = $(obj);
			this.elementListAndCheckFunction[next] = {check:ray,against:func};
		}
		else if(typeof(obj) == 'object' && !obj.nodeName)
		{
			var ary = new Array();
			for(var i=0; i < obj.length; i++)
			{	//die if we're adding undefined things...
				if(typeof(obj[i]) == 'undefined')
				{
					return;
				}				
				ary[ary.length] = obj[i];
			}
			//if we have ids
			this.elementListAndCheckFunction[next] = {check:ary,against:func};
		}
		else
		{
			/*
			 * this is not an id, DOMNode, or an array, so don't add it.  Add it to the error log.
			 */
		}
	},
	
	addMany : function(ary, func)
	{
		for(var i=0; i < ary.length; i++)
		{
			var next = this.elementListAndCheckFunction.length;
			var ray = new Array();
			if(typeof(ary[i]) == 'object')
			{
				ray[0] = ary[i];
			}
			if(typeof(ary[i]) == 'string')
			{
				ray[0] = ary[i];
			}
			this.elementListAndCheckFunction[next] = {check:ray,against:func};
		}
	},	
	
	getObj : function (id)
	{
		
		return document.getElementsByName(id);

	},
	
	check : function(form)
	{	//first, delete all the old errors
		this.removeErrors();
		//then preform a new check, returning true on everything marked passes
		//NOTE: We're checking the intersection of required fields and fields
		//within the given form
		var isGood = true;	
		//we loop through the list of things to check, checking that each line has all elements
		//in the form submission 
		for(var j=0; j < this.elementListAndCheckFunction.length; j++)
		{
			var check = this.elementListAndCheckFunction[j].check;
			var against = this.elementListAndCheckFunction[j].against;
			var sendArgument = new Array();
			var foundAllFields = true;
			
			//loop through the multi-field declarations in order
			for(var k=0; k < check.length; k++)
			{
				var foundArgument = false;
				var checkTarget = check[k];
				if(typeof(checkTarget) == 'string')
				{
					checkTarget = document.getElementById(checkTarget);
					if(typeof(checkTarget) == 'undefined')
					{	//this was a bad element not existing, skip it.
						continue;
					}
				}
				for(var i=0; i < form.elements.length; i++)
				{
					if(form.elements[i].disabled === true)
					{
						continue;
					}
					if(checkTarget.id == form.elements[i].id)
					{
						foundArgument = true;
						sendArgument[sendArgument.length] = form.elements[i];
						break;
					}
				}
				if(foundArgument == false)
				{
					foundAllFields = false;
					break;
				}
			}
			if(foundAllFields == true)
			{//run it
				if(against(sendArgument) === false)
				{
					isGood = false;
				}
			}
		
		} 
		return(isGood);
	},

	flagElementWithError : function(obj,msg)
	{
		Element.addClassName(obj,this.errorElementClass);
		this.errorElementsToUnstyle[this.errorElementsToUnstyle.length] = obj;
		var i = this.errorDivsToDelete.length;
		var erdiv = document.createElement('DIV');
		//innerHTML is used because getting a %laquo; otherwise is hell.
		erdiv.innerHTML = msg;
		Element.addClassName(erdiv ,this.errorDivClass);
		obj.parentNode.appendChild(erdiv);
		this.errorDivsToDelete[i] = erdiv;
	},
	
	flagElementAsContainingError : function(obj)
	{
		Element.addClassName(obj,this.errorEnclosureClass);
		this.errorEnclosuresToUnstyle[this.errorEnclosuresToUnstyle.length] = obj;
	},	

	removeErrors : function()
	{	//first, delete all the old errors
		for(var i=0; i < this.errorDivsToDelete.length; i++)
		{	
			this.errorDivsToDelete[i].parentNode.removeChild(this.errorDivsToDelete[i]);
		}
		this.errorDivsToDelete = Array();
		//then remove the error styling to elements
		for(var i=0; i < this.errorElementsToUnstyle.length; i++)
		{
			var o = this.errorElementsToUnstyle[i];
			Element.removeClassName(o,this.errorElementClass);	
		}
		//then remove the error styling to enclosure elements
		for(var i=0; i < this.errorEnclosuresToUnstyle.length; i++)
		{
			var o = this.errorEnclosuresToUnstyle[i];
			Element.removeClassName(o,this.errorEnclosureClass);	
		}
	},
	
	toString : function() 
	{
		op = 'FormValidatior';
	}
}

//and call
FormValidationObject.constructor();


/*==============================================================================
 *	FormValidationFunctions functions class (for form validation)
 */


window.FormValidationFunctions = {
	requireSelection : function(ary)
	{		
		var someErrors = false;
		for(var i=0; i < ary.length; i++)
		{
			if($F(ary[i]) != '')
			{
				$name = $F(ary[i]);
				var $group = FormValidationObject.getObj($name);				

				myOption = -1;
				for (j=$group.length-1; j > -1; j--)
				{
					if ($group[j].checked)
					{
						myOption = j; j = -1;
					}
				}
				if (myOption == -1) {
					someErrors = true;
					for (k=0; k < $group.length; k++) {
						FormValidationObject.flagElementWithError($group[k],'');
					}
					if ($name.indexOf('[]') != -1)
						FormValidationObject.flagElementWithError(ary[i],'&laquo; Please choose at least 1.');
					else
						FormValidationObject.flagElementWithError(ary[i],'&laquo; Please make a selection.');
				}
			}
		}
		if(someErrors == true)
		{
			return(false);
		} else {
			return(true);
		}
	},
	
	requireNonempty : function(ary)
	{		
		var someErrors = false;
		for(var i=0; i < ary.length; i++)
		{
			if($F(ary[i]) == '')
			{
				someErrors = true;
				FormValidationObject.flagElementWithError(ary[i],'&laquo; This field may not be empty.');
				window.scrollTo(0,0);
			}
		}
		if(someErrors == true)
		{
			return(false);
		} else {
			return(true);
		}
	},
	
	requirePositiveInteger : function(ary)
	{
		var someErrors = false;
		for(var i=0; i < ary.length; i++)
		{
			var val = parseInt($F(ary[i]));
			if(isNaN(val) || val <= 0)
			{
				someErrors = true;
				FormValidationObject.flagElementWithError(ary[i],'&laquo; This field must be a positive integer.');
			}
		}
		if(someErrors == true)
		{
			return(false);
		} else {
			return(true);
		}
	},
	
	requireDate : function(arrayother)
	{
		var hasErrors = false;
		for(var k=0; k<arrayother.length; k++)
		{
			var input = arrayother[k];
			var unsplittable = false;
			var val = new String($F(input));
			var ary = val.split('-');
			//try splitting
			if(ary.length != 3)
			{
				ary = val.split('\\');
				if(ary.length != 3)
				{
					ary = val.split('/');	
				}  
			}
			if(ary.length != 3)
			{
				unsplittable = true;
			}
			else
			{
				if(	ary[0].length == 4)
				{//SQL format
					if(ary[1].length < 1 || ary[1].length > 2)
					{
						unsplittable = true;
					} else {
						while(ary[1].length < 2)
						{
							ary[1] = '0'+ary[1];
						}
					}
					if(ary[2].length < 1 || ary[2].length > 2)
					{
						unsplittable = true;
					} else {
						while(ary[2].length < 2)
						{
							ary[2] = '0'+ary[2];
						}
					}
					input.value = ary[0]+'-'+ary[1]+'-'+ary[2];
				}
				else if(ary[2].length == 4)
				{//Standard date
					if(ary[0].length < 1 || ary[0].length > 2)
					{
						unsplittable = true;
					} else {
						while(ary[0].length < 2)
						{
							ary[0] = '0'+ary[0];
						}
					}
					if(ary[1].length < 1 || ary[1].length > 2)
					{
						unsplittable = true;
					} else {
						while(ary[1].length < 2)
						{
							ary[1] = '0'+ary[1];
						}
					}
					input.value = ary[2]+'-'+ary[0]+'-'+ary[1];					
				}
				else
				{
					unsplittable = true;
				}
			}
			if( unsplittable )
			{
				FormValidationObject.flagElementWithError(input,'&laquo; This field must be a date in YYYY-MM-DD format.');
				hasErrors = true;
			} 
		}
		if(hasErrors)
		{
			return(false);
		} else {
			return(true);
		}
	},
	
	validateDate : function(ary)
	{
		var hasErrors = false;
		for(var i=0; i < ary.length; i++)
		{
			var check;
			if($F(ary[i]) != '')
			{
				check = window.FormValidationFunctions.requireDate(new Array( ary[i] ));
			}
			if(check == false)
			{
				hasErrors = true;
			}
		}
		if(hasErrors)
		{
			return(false);
		} else {
			return(true);
		}
	},
	
	requirePhone : function(ary)
	{
		var someErrors = false;
		for(var i=0; i < ary.length; i++)
		{
			var val = new String( TroonGolf.numbers_only($F(ary[i])) );
			if(val.length < 10)
			{
				someErrors = true;
				FormValidationObject.flagElementWithError(ary[i],'&laquo; This field must contain at least 10 numbers.');
			} else {
				ary[i].value = val;
			}
		}
		if(someErrors == true)
		{
			return(false);
		} else {
			return(true);
		}
	},
	
	requireEmail : function(ary)
	{
		var someErrors = false;
		for(var i=0; i < ary.length; i++)
		{
			var val = $F(ary[i]);
			if(val.indexOf('@') == -1 || val.length < 4)
			{
				someErrors = true;
				FormValidationObject.flagElementWithError(ary[i],'&laquo; This field must be a valid email address.');
			}
		}
		if(someErrors == true)
		{
			return(false);
		} else {
			return(true);
		}
	}
};