/** { [lib.objects.window.validationObject.v2]
* @dependency :: [lib.functions.window.AddListener]
* @dependency :: [lib.functions.window.Catch]
* @dependency :: [lib.functions.window.markit]
* @dependency :: [lib.functions.window.validPhone]
* @dependency :: [lib.functions.window.isZip]
* @dependency :: [lib.functions.window.evalEmail]
* @dependency :: [lib.functions.window.numOnly]
* @dependency :: [lib.functions.dom.getElem]
* @dependency :: [lib.functions.window.replaceall]
* @dependency :: [lib.functions.window.CCCheck]
* @dependency :: [lib.functions.window.CCVCheck]
* @edit :: Friday, March 13, 2009 - label as an register args object will display the passed label in the warning while still attempting to put the * on label if required
* @edit :: Friday, February 20, 2009 - added support for CreditCard and CCV Types (requires CCType value sent in ARGS)
* @edit :: Friday, February 20, 2009 - added meetsMinimumInfoReq to check if all needed elements exist for validation to occure
* @edit :: Tuesday, January 27, 2009 - updated for compiling
**/
/* to add validation to an item, see documentation for usage.
* if error occures see cError of error log. this is an array
* documentation not. Params are displayed as
* @param :: NAME - type expected :: [Default Value if not set] :: explanation
* 		double indented items after a param are child params or accepted values
*/
/** Class window.validationObject
* @purpose :: takes form element registration and establishes actions based on definition
* @author :: Scott McDonald
* @version :: 2.1
* @revision :: Thursday, October 16, 2008 - added delayed registration to allow elements
*		to load - this is used for DOM level creation of elements. To use this, set this.waitForInit=true
*		then call this.initList() after DOM created elements have completely loaded to page
*
* [Properties]
*
* [Methods]
* @Method :: setRequired
* @purpose sets/unsets required for a given item
* @param :: bRequired as boolen [true]
* @param :: nIndex as int [all items] valid 0 based index (check item.index)
* @param :: args as Object [null] used for future features
* @return void
*/

window.validationObject = function() {
	var target = this;
	this.typename = "lib.objects.window.validationObject.v2";
	this.items = new Array();
	this.form = undefined;
	this.cError = new Array();
	this.addToValidate = function() { return (""); };
	this.submitButton = undefined;
	this.waitForInit = false;
	/*if true, then register will not occure until initList is called */

	AddListener(window, "load", function() { target.init(); });

	this.getFields = function() {
		try {
			var i = 0;
			var l = this.items.length;
			var arr = new Array();
			for (i; i < l; i++) {
				arr[arr.length] = this.items[i].element;
			};
			return (arr);
		} catch (e) { Catch(this.typename + ".getFields(base)", e, true); };
	};

	this.setRequired = function(bRequired, nIndex, args) {
		try {
			bRequired = (bRequired != false);
			if (nIndex == "all") { nIndex = undefined; };
			startat = (nIndex == undefined) ? 0 : nIndex;
			stopat = (nIndex == undefined) ? this.items.length : nIndex + 1;
			for (startat; startat < stopat; startat++) {
				try {
					this.items[startat].required = bRequired;
					this.markLabel(this.items[startat].element);
				} catch (e) { Catch(this.typename + ".setRequired", e); };
			};
		} catch (e) { Catch(this.typename + ".setRequired(base)", e, true); };
	};

	/** looks at the field type and any other information passed and will determan if there is
	* enough information to process/validate - e.g. if type=creditcard then we will need something
	* to indicate the card type. this can be passed as a value VISA|M/C|AMEX or as a pointer to
	* a form field.
	*
	* Friday, February 20, 2009
	*/
	this.meetsMinimumInfoReq = function(o) {
		switch (o.type) {
			case ("creditcard"):
				{
					if (o.args.CCType == undefined) {
						Catch(this.typename + ":" + o.id, { message: "CCType must be defined as either a String or as a pointer to a Form Element" }, true);
						return (false);
					}
					break;
				}
			case ("CCV"):
				{
					if (o.args.CCType == undefined) {
						Catch(this.typename + ":" + o.id, { message: "CCType must be defined as either a String or as a pointer to a Form Element" }, true);
						return (false);
					}
					break;
				}
		};
		return (true);
	};

	/** performs validation on a specific item
	* @param :: me as object pointer
	*/
	this.validateMe = function(me, o) {
		try {
			var _return = { valid: true, error: "" };
			var target = this;
			var label;
			if (!o) { o = this.getInfoForObject(me.id); };
			if (!o) { return ({ valid: false, error: "\t- Cannot find element.\n" }); };

			try {
				/* check for required and empty */
				if (o.required && me.value == "") {
					_return.valid = false;
					_return.error = "\t- " + o.labeltext + " is Empty.\n";
					return (_return);
				} else if (!o.required && me.value == "") {
					return ({ valid: true, error: "" });
				};
			} catch (e) { Catch(this.typename + ".this.validateMe", e); };

			switch (o.type.toLowerCase()) {
				case ("creditcard"):
					{
						try {
							if (o.args.CCType) {
								var ccType;
								if (typeof (o.args.CCType) == "string") {
									ccType = o.args.CCType
								} else {
									ccType = o.args.CCType.value
								};
								var ccValid = CCCheck(me.value, ccType);
								return ({ valid: ccValid.valid, error: "\t - " + ccValid.error + "\n" });
							} else {
								Catch(thistory.typename + ".validateMe(CreditCard)", { message: "couldn't find CCType" }, true);
							}
						} catch (e) { Catch(this.typename + ".validateMe(CreditCard)", e, true); };
						break;
					}
				case ("ccv"):
					{
						try {
							if (o.args.CCType) {
								var ccType;
								if (typeof (o.args.CCType) == "string") {
									ccType = o.args.CCType
								} else {
									ccType = o.args.CCType.value
								};
								var ccValid = CCVCheck(me.value, ccType);
								return ({ valid: ccValid.valid, error: "\t - " + ccValid.error + "\n" });
							} else {
								Catch(thistory.typename + ".validateMe(CCV)", { message: "couldn't find CCType" }, true);
							}
						} catch (e) { Catch(this.typename + ".validateMe(CCV)", e, true); };
						break;
					}
				case ("string"): { break; }
				case ("date"): { break; }
				case ("zip"):
					{
						if (!isZip(me.value)) { return ({ valid: false, error: "\t- " + o.labeltext + " is Invalid.\n" }); };
						break;
					}
				case ("phone"):
					{
						var isValid = validPhone(me.value);
						me.value = isValid.value;
						if (!isValid.valid) { return ({ valid: false, error: "\t- " + o.labeltext + " is Invalid. [" + isValid.error + "]\n" }); };
						break;
					}
				case ("email"):
					{
						var isValid = evalEmail(me.value);
						me.value = isValid.value;
						if (!isValid.valid) { return ({ valid: false, error: "\t- " + o.labeltext + " is Invalid. [" + isValid.error + "]\n" }); };
						break;
					}
				case ("select"):
					{
						if (me.value == ".none") { return ({ valid: false, error: "\t- " + o.labeltext + " Has No Option Selected.\n" }); };
						break;
					}
				case ("numeric"):
					{
						me.value = numOnly(me.value, ".");
						if (me.value == "") {
							return ({ valid: false, error: "\t- " + o.labeltext + " is Empty.\n" });
						};
						if (o && o.args && o.args.range) {
							var range = o.args.range;
							if (range) {
								if (!this.numberFoundInRange(me.value, range)) {
									return ({ valid: false, error: "\t- " + o.labeltext + " Not within allowed range.\n" });
								};
							};
						};
						break;
					};
				case ("radio"):
					{
						var _selectedIndex = selectedRadio(me);
						if (_selectedIndex > -1) {
							return ({ valid: true, error: "" });
						} else {
							return ({ valid: false, error: "\t- " + o.labeltext + " is Empty.\n" });
						}
						break;
					};
			};
		} catch (e) { Catch(this.typename + ".validateMe(base)", e, true); };
		return ({ valid: true, error: "" });
	};

	this.numberFoundInRange = function(num, range) {
		try {
			if (range && num) {
				var csv = range.split(",");
				var csv_i = 0;
				var csv_l = csv.length;
				var hsv, hsvi, hsvl;
				for (csv_i; csv_i < csv_l; csv_i++) {
					hsv = csv[csv_i].split("-");
					if (hsv.length == 0) {
						if (hsv[0] == num) {
							return (true);
						};
					} else {
						if (((hsv[0]) / 1) <= ((num) / 1) && ((num) / 1) <= ((hsv[1]) / 1)) {
							return (true);
						};
					};
				};
			};
		} catch (e) { Catch(this.typename + ".numberFoundInRange(base)", e, true); };
		return (false);
	};

	/** performs validation on all items in form
	*/
	this.validateForm = function(displayAlert, args) {
		try {
			displayAlert = (displayAlert != false);
			var r = "";
			var i = 0;
			var l = this.items.length;
			var me;
			var v;
			for (i; i < l; i++) {
				me = this.items[i];
				v = this.validateMe(me.element, me);
				if (!v.valid) {
					markit(me.element, true);
					me.element.title = v.error;
					r += v.error;
				} else {
					me.element.title = "";
					markit(me, false);
				};
			};

			r += this.addToValidate();
			if (r != "") {
				if (!displayAlert) {
					return (r);
				} else {
					alert("The form cannot be submitted; please correct the following and resubmit:\n\n" + r);
					return (false);
				};
			} else {
				if (!displayAlert) {
					return ("");
				} else {
					return (true);
				};
			};
		} catch (e) { Catch(this.typename + ".validateForm(base)", e, true); };
	};

	/** @method :: register
	* @purpose adds an item to the validation object
	* @param id :: as String :: [exception] :: name/id as string of item to add to validation
	* @param type :: as String :: [string] :: type method to perform on item for validation
	* 	@type :: String - Performs simple "HAS_VALUE" check
	* 	@type :: Phone - Performs check on valid phone number
	*		@type :: Zip - Performs US Zipcode check
	*		@type :: Email - Performs Email Check
	*		@type :: Select - defines a select-list (combo box)
	*		@type :: CheckBox - defines a Checkbox (Note: will only fire callback on blur after change)
	*		@type :: Radio - defines a Radio List
	*		@type :: Numeric - string check that only numbers exist
	*		@type :: CreditCard
	*		@type :: CCV [Number]
	* @param required :: as Boolean :: [true] :: some fields are validated IF they have a value, otherwise should be left alone.
	*		(example, fax number should resolve to a phone number if it's populated, but if it's empty then the form
	* 	should still be able to be submitted) Setting this to false, will allow you to define those fields.
	* @param args :: as Object :: [null] :: allows for options
	* 	@arg :: onsuccess :: as function :: [null] :: called if validate is successfull
	* 	@arg :: onfailed :: as function :: [null] :: called if validate fails
	* 	@arg :: callback :: as function :: [null] :: called regardless of validation
	*		@arg :: range :: range of numbers which when set will be applied against the value. [1-5,8,12,22]
	*		@arg :: label :: if the item is a hidden element, you can specifiy the label in the error message by passing this item
	*	@return :: Boolean - True if successfully registered, False if not. If not, check the cError property for info
	*/
	this.register = function(id, type, required, args) {
		try {
			var newindex;
			var thisItem;
			newindex = this.items.length;

			if (typeof (id) == "object") {
				thisItem = id;
				id = thisItem.id;
				type = thisItem.type;
				required = thisItem.required;
				args = thisItem.args;
			} else {
				thisItem = {
					typename: "window.validationObject.item",
					id: id,
					type: type,
					required: required,
					index: newindex,
					args: args
				};
			};


			if (thisItem.label && typeof (thisItem.label) == "string") {
				thisItem.labeltext = thisItem.label;
			} else {
				thisItem.labeltext = "";
			};
			thisItem.label = undefined;
			thisItem.type = thisItem.type.toLowerCase();

			/** run information check on items to make sure minimum information is set [by field Type]
			*/
			if (!this.meetsMinimumInfoReq(thisItem)) {
				trace("item does not meet minimum info requirements, do not add to validation load.");
				return;
			}

			this.items[newindex] = thisItem;

			if (this.waitForInit) {
				return (this.items[newindex]);
			};



			var target = this;
			if (id == undefined) {
				this.addError("Form Element ID required to register element");
				return (false);
			};
			if (type == undefined) { type = "string"; };
			if (required == undefined) { required = true; };

			var o = getElem(id);

			if (o) {
				/** find the label and set it up in the object if found **/
				this.markLabel(thisItem);
				var arr;
				try {
					var theaction;
					if (o.type == "radio") {
						theaction = "mouseup";
						o = o.form[o.id];
						var i = 0;
						var l = o.length;
						var elem;
						for (i; i < l; i++) {
							elem = o[i];
							this.addTheListener(elem, theaction, thisItem);
						};
					} else {
						if (o.type == "hidden") {
							theaction = "change";
						} else {
							theaction = "blur";
						};
						this.addTheListener(o, theaction, thisItem);
					};
				} catch (e) { Catch(this.typename + ".register(187)", e); };

				thisItem.parent = this;
				thisItem.id = id;
				thisItem.type = type;
				thisItem.required = required;
				thisItem.element = o;
				thisItem.setRequired = function(bool) { this.parent.setRequired(bool, this.index); };

				this.items[newindex] = thisItem;
				return (this.items[newindex]);
			} else {
				this.addError("Could not find form item object - be sure you do not register it until AFTER it is defined in the HTML");
				return (false);
			}
		} catch (e) { Catch(this.typename + ".register(base)", e, true); };
	};

	this.markLabel = function(o) {
		var id = o.id;
		var required = o.required;
		try {
			/* alter item label to add required marks and remove breakable spacing */
			var label = document.getElementById(id + "_label");
			if (!label) {
				label = document.getElementById(id + "_Label");
			}
			if (label) {
				o.label = label;
				if (o.labeltext == "" || o.labeltext == undefined) {
					o.labeltext = label.innerText;
				}
			}
			if (label && required) {
				if (label.innerHTML.indexOf("*") == -1) {
					/* add astick to label */
					var temp = "* " + label.innerHTML;
					/* replace spaces with &nbsp; */
					temp = temp.replace(/ /gi, "&nbsp;");
					/* reset label */
					label.innerHTML = temp;
				};
			} else if (label) {
				var temp = label.innerHTML;
				temp = replaceall(temp, "\*\&nbsp;", "");
				label.innerHTML = temp;
			} else if (!label && required) {
				if (o.args && o.args.label && o.args.label == "") {
					Catch(this.typename + ".markLabel(431)", {
						message: "LABEL: \"" + id + "_label\" not found. <br /> Resolve this issue before going live or validator will not function properly.<br /> If no label element can be defined [item is hidden - or part of a group], pass args.label value for validators use"
					}, true);
				};
			};
			return (label);
		} catch (e) { Catch(this.typename + ".markLabel" + e.message); };
	};

	this.addTheListener = function(o, theaction, itemobject) {
		try {
			var target = this;
			AddListener(o, theaction, function() {
				/* validate form element content
				*/
				var valid = target.validateMe(o, itemobject);
				/* mark or unmark visual element
				*/
				markit(o, valid.valid == false);
				if (itemobject.args) {
					/* if the validate is true look for onsuccess callback
					*/
					if (valid.valid) {
						if (itemobject.args.onsuccess) { itemobject.args.onsuccess(o); };
						/* if the validate is false look for onfailed callback
						*/
					} else if (!valid.valid) { if (itemobject.args.onfailed) { itemobject.args.onfailed(o); }; }
					/* no matter how the validation goes, look for the callback callback
					*/
					if (itemobject.args.callback) { itemobject.args.callback(o); };
				};
				/* always return false
				*/
				return (false);
			});
		} catch (e) { Catch(this.typename + ".addTheListener(base)", e, true); };
	};

	this.getItemFromLabel = function(o) {
		try {
			if (o.args.useLabel && o.args.useLabel != "") {
				return (o.args.useLabel);
			} else {
				var r = o.label.innerHTML;
				r = replaceall(r, "\*", "");
				r = r.replace(/:/gi, "");
				r = r.replace(/&nbsp;/gi, " ");
				return (r);
			};
		} catch (e) { Catch(this.typename + ".getItemFromLabel(base)", e, true); };
	};

	this.getInfoForObject = function(name) {
		try {
			var i = 0;
			var l = this.items.length;
			for (i; i < l; i++) {
				if (this.items[i].id == name) {
					return (this.items[i]);
				};
			};
		} catch (e) { Catch(this.typename + ".getInfoForObject(base)", e, true); };
	};

	this.addError = function(text) {
		try {
			this.cError[this.cError.length] = text;
		} catch (e) { Catch(this.typename + ".addError(base)", e, true); };
	};

	this.registerSubmit = function() {
		try {
			var target = this;
			if (this.submitButton && this.form) {
				this.submitButton.onclick = function() {
					target.onsubmit(target);
				};
			};
		} catch (e) { Catch(this.typename + ".registerSubmit(base)", e, true); };
	};


	this.init = function() {
		try {
			if (this.form != undefined) {
				if (typeof (this.form) == "string") {
					this.form = getElem(this.form);
				};
			};
			if (this.submitButton != undefined) {
				if (!this.submitButton.type && typeof (this.submitButton) == "string") {
					this.submitButton = getElem(this.submitButton);
				};
			};
			if (this.form && this.onsubmit) {
				this.registerSubmit();
			};
		} catch (e) { Catch(this.typename + ".init(base)", e, true); };
	};


	this.initList = function() {
		try {
			var i = 0;
			var l = this.items.length;
			for (i; i < l; i++) {
				this.register(this.items[i]);
			};
		} catch (e) { Catch(this.typename + ".initList(base)", e, true); };
	};
};

/** } [lib.objects.window.validationObject.v2] **/
