/********************************/
/*********** CLASSES ************/
/********************************/

/**
 * FormControler
 */
var FormController = new Class({

	initialize : function() {
		window.addEvent('domready', function () {
			this.initDisableOnSubmit();
		}.bind(this));
	},

	/**
	 * Shows or hides targetElem when triggerElem is checked/selected
	 * @param triggerElem An ID of or reference to the trigger element
	 * @param targetElem An ID of or reference to the target element
	 * @param showOrHide 'show' to show, 'hide' to hide; default is to show
	 * @example formController.createDisplayToggler('areYouInCollege', 'collegeQuestions', 'show');
	 * 			formController.createDisplayToggler('areYouInCollege', 'hsQuestions', 'hide');
	 */
	createDisplayToggler : function (triggerElem, targetElem, showOrHide) { // displaySetting = show|hide

		window.addEvent('domready', function () {

			triggerElem = $(triggerElem); // accept an id or an element obj reference as a param
			targetElem = $(targetElem); // accept an id or an element obj reference as a param
			var showHideTarget; // the function that gets called that either shows or hides the targetElem; abstracts the show/hide behavior
			
			if (triggerElem != null && targetElem != null) {

				// determine function (checks for checked/selected status and shows or hides accordingly)
				if (showOrHide == 'hide') {

					showHideTarget = function () {

						if (triggerElem.checked || triggerElem.selected) {
							targetElem.addClass('jsHide'); // hide
						} else {
							targetElem.removeClass('jsHide'); // show
						}
					};
				} else { // assume show

					showHideTarget = function () {

						if (triggerElem.checked || triggerElem.selected) {
							targetElem.removeClass('jsHide'); // show
						} else {
							targetElem.addClass('jsHide'); // hide
						}
					};
				}

				// set events
				triggerElem.addEvent('keyup', showHideTarget);
				triggerElem.addEvent('click', showHideTarget);

				// run once on pageload
				showHideTarget();
				
			} else {
				global.log('FormController.createDisplayToggler: One of these elements does not exist. triggerElem = ' + triggerElem + " targetElem = " + targetElem);
			}
		});
	},

	/**
	 * Shows (or hides) targetElem when any triggerElem is checked/selected
	 * @param triggerElem A list of IDs to function as trigger elements
	 * @param targetElem An ID of or reference to the target element
	 * @param showOrHide 'show' to show, 'hide' to hide; default is to show
	 * @example formController.createDisplayToggler(['areYouACollegeJunior', 'areYouACollegeSenior'], 'collegeQuestions');
	 */
	createMultiDisplayToggler : function (triggerElemIDs, targetElem, showOrHide, callback) { // displaySetting = show|hide

		window.addEvent('domready', function () {

			targetElem = $(targetElem); // accept an id or an element obj reference as a param

			for (var i = 0; i < triggerElemIDs.length; i++) {

				var triggerElem = $(triggerElemIDs[i]);
				var showHideTarget; // the function that gets called that either shows or hides the targetElem; abstracts the show/hide behavior

				if (triggerElem != null && targetElem != null) {

					showHideTarget = function (triggerElemIDs, targetElem, showOrHide) {

						var doShowOrHide = false;

						for (var i = 0; i < triggerElemIDs.length; i++) {

							var triggerElem = $(triggerElemIDs[i]);

							if (triggerElem.checked || triggerElem.selected) {
								doShowOrHide = true;
							}
						}

						if (doShowOrHide) {

							if (showOrHide == 'hide') {
								global.trace('createMultiDisplayToggler() hide');
								targetElem.addClass('jsHide'); // hide
							} else {
								global.trace('createMultiDisplayToggler() show');
								targetElem.removeClass('jsHide'); // show
							}
						} else {

							if (showOrHide == 'hide') {
								global.trace('createMultiDisplayToggler() show');
								targetElem.removeClass('jsHide'); // show
							} else {
								global.trace('createMultiDisplayToggler() hide');
								targetElem.addClass('jsHide'); // hide
							}
						}

						if (typeof(callback) == 'function') {
							callback(doShowOrHide);
						}

					};

					// set events
					triggerElem.addEvent('keyup', showHideTarget.pass([triggerElemIDs, targetElem, showOrHide]));
					triggerElem.addEvent('click', showHideTarget.pass([triggerElemIDs, targetElem, showOrHide]));

					// run once on pageload
					showHideTarget(triggerElemIDs, targetElem, showOrHide);

				} else {
					global.log('FormController.createMultiDisplayToggler: One of these elements does not exist. triggerElem = ' + triggerElem + " targetElem = " + targetElem);
				}
			}



		});
	},

	/**
	 * Set an event to disable all .jsDisableOnSubmit buttons, inputs, options or selects on the page (other element's don't have a
	 * disabled attribute)
	 */
	initDisableOnSubmit : function () {
		$$('.jsDisableOnSubmit').each(function (elem) {
			elem.addEvent('click', function () {
				(function() {
					this.disabled = 'disabled';
					this.addClass('disabledBySubmit');
				}).delay(50, this);

			}.bind(elem));
		});
	},

	/**
	 * Checks the length of any form element and shows/hides an error
	 */
	checkLength : function (id, limit) {

		if ($(id).value.length > limit) {
			$(id + 'LengthError').removeClass('jsHide');
		} else {
			$(id + 'LengthError').addClass('jsHide');
		}
	}
});

/*
 * Requires the following to be included on the page: var formValidator = new FormValidator();
 * REQUIREMENT: the form must have an onsubmit="return false;" (or <input:form attributesText="onsubmit=\"return false;\"">)
 */
var FormValidator = new Class({

	/*
	 * The id of the form
	 */
	id : null,

	/*
	 * Reference object (often the enclosing object)
	 */
	referenceObj : null,

	/*
	 * A Hash of the errors on the page. Key: fieldName, Value: errorText
	 */
	errors : [],

	/*
	 * Boolean - whether or not to disable the submit button onSubmit (to repeat double submissions. NB: setting this will still allow the first submission to take place)
	 */
	isSubmitEnabled : true,

	/*
	 * Boolean - whether or not to submit the form (onsubmit="return true/false") after successful validation. Defaults to true.
	 */
//	doSubmitFormAfterValidation : true,

	/*
	 * Boolean - whether or not to scroll the form to the first error or top of page if validation fails
	 */
	doScrollToError : false,
	doScrollToTop : false,
	doErrorHighlighting : false,

	/*
	 * The Ajax object for validation
	 */
	ajax : Class.empty(),

	/*
	 * Initializer sets the id
	 */
	initialize : function (options) {

		this.id = options.id;
		this.referenceObj = options.referenceObj;

		this.doScrollToError = $pick(options.doScrollToError, false); // default to false
		this.doScrollToTop = $pick(options.doScrollToTop, false); // default to false
		this.doErrorHighlighting = $pick(options.doErrorHighlighting, false); // default to false
		// this.doSubmitFormAfterValidation = $pick(options.doSubmitFormAfterValidation, true);

		this.onSuccess = $pick(options.onSuccess, this.onSuccess).bind(this); // don't set onSuccess unless it exists

		window.addEvent('domready', function () {
			$(this.id).onsubmit = this.validateForm.bind(this);
		}.bind(this));

		this.ajax = new AjaxCpx('/ajax/validateForm.jsp', {

			referenceObj : this,

			/*
			 * Updates the errors array and displays them, then submits form if there are no errors
			 */
			onSuccess : function () {

				var newErrors = Json.evaluate(this.referenceObj.ajax.transport.responseText);

				// no errors
				if (newErrors.length == 0) {

					if ($defined(this.referenceObj.ajax.indicator)) {
						this.referenceObj.ajax.indicator.setStyle('display', 'inline');
						// continue to show indicator after form validation succeeds
					}

					this.referenceObj.onSuccess();

				// errors occurred
				} else { // reenable button & redisplay errors if there are new errors
					this.referenceObj.isSubmitEnabled = true;
					this.referenceObj.clearErrors();
					this.referenceObj.errors = newErrors;
					this.referenceObj.displayErrors();
				}

			}
		});
	},

	/*
	 * Sends the form data to the form validation jsp. The ajax response will be a (possibly empty, a.k.a. '[]') array of key/value (fieldName/errorMsg) pair objects like:
	 * [{'key' : 'phoneDaytime', 'value' : 'Fix your phone #'}, {'key' : 'firstName', 'value' : 'No swears please'}]
	 */
	validateForm : function () {

		if (this.isSubmitEnabled) {

			
			// disable to prevent repeated submission
			this.isSubmitEnabled = false;

			// serialize form
			var data = $(this.id).toQueryString();

			// submit the form via ajax
			this.ajax.send('/ajax/validateForm.jsp', data);
		}

		return false;
	},

	/*
	 * Displays the errors contained in the errors array.  Will create a new XXXErrorMsg p element and insert
	 * it before the corresponding field if one does not already exist. Looks for an input with the correct name,
	 * and if it can't find one looks for one with the correct id. Be sure to add either an XXXErrorMsg or an elem
	 * with id='XXX' for radio buttons and checklists (since they use duplicate names). 
	 */
	displayErrors : function () {

		var errorMsgElem;
//		var firstErrorMsgElem;

		// for each id in the error map
		for (var i = 0; i < this.errors.length; i++) {

			// if there is a form-level error, show it (it should already exist on the page statically, with a class of jsHide initially
			if ($(this.id + 'ErrorMsg') != null) {
				$(this.id + 'ErrorMsg').removeClass('jsHide');
			}

			var fieldName = this.errors[i].key;

			// if there is not a placeholder for the errorMsg
			if ($(fieldName + 'ErrorMsg') == null) {

				// look for field with this name or id (helps find the field in the correct form)
				var formFieldReferenceAsText = "$('" + this.id + "')." + fieldName;
				var fieldElem = $pick(eval(formFieldReferenceAsText), $(fieldName));

				// if there is more than one element of this name, it might be a pseudo-multi-select checklist or a radio button set, so place the error in front of an object with fieldName as its id rather than name
				if ($type(fieldElem) == 'collection') {
					fieldElem = $(fieldName);
				}

				if (!$defined(fieldElem)) global.log("Cannot find field of corresponding name or id: " + this.id);
				
				// create a new element to hold the errorMsg
				errorMsgElem = new Element('p', {
					id : fieldName + 'ErrorMsg',
					'class' : 'errorMsg'
				});

				try {
					errorMsgElem.injectBefore(fieldElem);
				} catch (e) { // if the first attempt fails, show the error in an alert (this should not happen but does sometimes because of a mootools bug)

//					global.log('this.id = ' + this.id);
//					global.log('fieldName = ' + fieldName);
//					global.log('$(this.id + "ErrorMsg") = ' + $(this.id + "ErrorMsg"));
//					global.log('$(this.id + "ErrorMsg").id = ' + $(this.id + "ErrorMsg").id);
//					global.log('$(this.id + "ErrorMsg").id = ' + $(this.id + "ErrorMsg").id);
//					global.log('eval(formFieldReferenceAsText) = ' + eval(formFieldReferenceAsText));
//					global.log('$("fieldName") = ' + $("fieldName"));
//					global.log('fieldElem = ' + fieldElem);
//					global.log('fieldElem.name = ' + fieldElem.name);
//					global.log('errorMsgElem = ' + errorMsgElem);
//					global.log('errorMsgElem.id = ' + errorMsgElem.id);

					alert("There was a problem with the information you provided regarding " + this.errors[i].key + ": " + this.errors[i].value);
				}

//				if (i == 0 && this.doScrollToError) { // scroll to first error
//					firstErrorMsgElem = errorMsgElem;
//				}
			}

			$(fieldName + 'ErrorMsg').setHTML(this.errors[i].value);
			$(fieldName + 'ErrorMsg').removeClass('jsHide');

			if (($$('.largeFormLayout').length != 0 || $$('.createAccountForm').length != 0) && $$('.errorMsg').length != 0) {

				var currentError = $(fieldName + 'ErrorMsg');

				while (currentError.parentNode != null && currentError.parentNode.nodeName != "FORM") {

					if (currentError.parentNode.nodeName == "FIELDSET") {
						$(currentError.parentNode).addClass('errorFieldset');
					}
					currentError = currentError.parentNode;
				}

				$($(fieldName + 'ErrorMsg').parentNode).addClass('errorFieldset');
			}
		}

		if (this.errors.length > 0 && this.doScrollToError) { // scroll to form-level error message
			new Fx.Scroll(window).toElement($(this.id + 'ErrorMsg'));
		} else if (this.errors.length > 0 && this.doScrollToTop) { // scroll to top of page
			new Fx.Scroll(window).toTop();
		}

		if (this.doErrorHighlighting) {
			highlightErrors();
		}
	},

	/**
	 * Hides all errors and clears the errors array. Does not remove the XXXErrorMsg p element from the dom.
	 */
	clearErrors : function () {

		// hide messages for all errors
		for (var i = 0; i < this.errors.length; i++) {
			var fieldName = this.errors[i].key;

			if ($(fieldName + 'ErrorMsg') != null) { // probably a redundant check
				$(fieldName + 'ErrorMsg').addClass('jsHide');

				var currentError = $(fieldName + 'ErrorMsg');

				while (currentError.parentNode != null && currentError.parentNode.nodeName != "FORM") {

					if (currentError.parentNode.nodeName == "FIELDSET") {
						$(currentError.parentNode).removeClass('errorFieldset');
					}
					currentError = currentError.parentNode;
				}
			}

			// if there is a form-level error, show it (it should already exist on the page statically, with a class of jsHide initially
			if ($(this.id + 'ErrorMsg') != null) {
				$(this.id + 'ErrorMsg').addClass('jsHide');
			}

		}

		// reset formError object
		this.errors = [];

	},

	/**
	 * Function to perform after a successful validation.  It submits the form by default
	 */
	onSuccess : function () {
		$(this.id).submit();
	}

});

// FormValidator.implement(new Events);

/********************************/
/***** DECLARE GLOBAL VARS ******/
/********************************/

var formController = new FormController();
// var optimizeForIE6 = false;

/********************************/
/******** EVENT HANDLERS ********/
/********************************/

window.addEvent('domready', function() {
	if (typeof(optimizeForIE6) == 'undefined' || !optimizeForIE6 || !window.ie6) {
		formHighlighting();
	}
	initChecklists();
});

/********************************/
/********** FUNCTIONS ***********/
/********************************/

/* Form Highlighting */

function formHighlighting() {

	if (!$$('.largeFormLayout')) {
		return false;
	}

	var fields = $$('.field');
	var fieldsets = $$('FIELDSET');

	for (var i = 0; i < fields.length; i++) {
		fields[i].onfocus = function() {
			var current = this;
			while (current.parentNode != null && current.parentNode.nodeName != "FORM") {
				if (current.parentNode.nodeName == "FIELDSET") {
					if (global.isSafari2down) {
						fieldsets.removeClass('active');
					}
					$(current.parentNode).addClass('active');
				}
				current = current.parentNode;
			}
		};

		if (global.isSafari2down) {
			fields[i].onclick = function() {
				var current = this;
				while (current.parentNode != null && current.parentNode.nodeName != "FORM") {
					if (current.parentNode.nodeName == "FIELDSET") {
						fieldsets.removeClass('active');
						$(current.parentNode).addClass('active');
					}
					current = current.parentNode;
				}
			};
		}

		fields[i].onblur = function() {
			var current = this;
			while (current.parentNode != null && current.parentNode.nodeName != "FORM") {
				if (current.parentNode.nodeName == "FIELDSET") {
					$(current.parentNode).removeClass('active');
				}
				current = current.parentNode;
			}

		};

	}

	return 0;
}

/* Error Highlighting */

window.addEvent('domready', highlightErrors);

function highlightErrors () {

	if (typeof(optimizeForIE6) == 'undefined' || !optimizeForIE6 || !window.ie6) {

		if (($$('.largeFormLayout').length != 0 || $$('.createAccountForm').length != 0) && $$('.errorMsg').length != 0) {

			var theErrorMessages = $$('.errorMsg');

			for (var i = 0; i < theErrorMessages.length; i++) {

				var currentError = theErrorMessages[i];

				if (!theErrorMessages[i].hasClass('jsHide')) {

					while (currentError.parentNode != null && currentError.parentNode.nodeName != "FORM") {

						if (currentError.parentNode.nodeName == "FIELDSET") {
							$(currentError.parentNode).addClass('errorFieldset');
						}
						currentError = currentError.parentNode;
					}
				}
			}
		}
	}
}

/* Checklists (multi-selects) */

function initChecklists() {
	if (window.ie6) {
		var checkListLabels = $$('ul.checklist label');
		checkListLabels.addEvent('mouseover', function() {
			this.addClass('hover');
		});
		checkListLabels.addEvent('mouseout', function() {
			this.removeClass('hover');
		});
	};
}

/* Finds all checkboxes and selects them. */

function checkAll(field) {
	if (field.length) {
		for (i = 0; i < field.length; i++)
			field[i].checked = true;
	} else if (!field.checked) {  // This is to handel when there is only 1 check box.
		field.checked = true;
	}
}

/* Finds all checkboxes and un-selects them. */

function uncheckAll(field) {
	if (field.length) {
		for (i = 0; i < field.length; i++)
			field[i].checked = false;
	} else if (field.checked) {  // This is to handel when there is only 1 check box.
		field.checked = false;
	}
}