/* form v1.0 */

//	Class FormField
//		Class for form field manipulation
//	
//	
//	PROPERTIES
//	
//	String fieldId
//		Each FormField objects works with one input field. This property specifies id of that object.
//	
//	HtmlElement field
//		Link to the element that the class instance works with
//	
//	String labelText
//		Text of field label. Can be used in error messages.
//		If the object has property "label" set, labelText is text content of that label element
//	
//	HtmlElement label
//		If there is any label element for the "field", this property contains link to that label element.
//	
//	String invalidClass
//		If object checks "field" value and finds it invalid, it adds this class to "field". It can be used 
//		to distinguish incorrect values graphically.
//	
//	String defaultValue
//		If "field" is not set, FormField sets it to this value
//	
//	Array checkFunctions
//		Array of objects. Each object contains 2 properties - function "fn" and error message "err".
//		Function "fn" reads value of FormField and determines if it is correct (then returns true) or incorrect
//		(then returns false). Field can have multiple checkFunctions, each function can have it's own error
//		message "err".
//		"err" can contain some special identifiers that are replaced before output.
//	
//	Object errorMsg
//		When field is checked and some checkFunctions return false (this means if value is invalid),
//		checking functions writes "reports" to errorMsg property. This property is object containing various
//		error message formats. Default formats are "html", "javascript" and "simple"
//			Array errorMsg.html - error message in XHTML format, suitable to be written to HTML page.
//			Array errorMsg.javascript - error message in escaped text format, suitable to be used with javascript.
//				Double quotes have to be escaped in this case.
//			String errorMsg.simple - simple text error message, without special characters, suitable for general use.
//			%label% is replaced by labelText
//			%value% is replaced by field value (returned by method getValue)
//	
//	
//	CONSTRUCTOR
//	
//	FormField([String fieldId, String labelText, String defaultValue])
//	
//	
//	PUBLIC METHODS
//	
//	Boolean init([HtmlElement|String field, HtmlElement|String label, String defaultValue])
//		Initialization of object after page load. Parameters are mandatory unless proper parameters
//		were specified in constructor.
//	
//	TODO
//	Boolean setValid()
//	Boolean setInvalid()
//	String getValue()
//	Boolean setValue(String newValue)
//	Boolean setLabel(HtmlElement label)
//	Boolean setLabelText(String labelText)
//	Boolean addCheck(String checkType, Function checkFunction, String errorMsg)
//	Boolean addCheckRegExp(String checkType, Function checkRegExp, String errorMsg)
//	Boolean check()
//	Object getErrorMsg(String msgType)

function FormField(fieldId, labelText, defaultValue) {
	var own             = this;
	this.fieldId        = "";
	this.field          = null;
	this.labelText      = "";
	this.label          = null; // link to field label
	this.invalidClass   = "invalid";
	this.isValid        = true;
	this.defaultValue   = "";
	this.checkFunctions = new Array();
	this.errorMsg       = {
		simple:     "",
		html:       "",
		javascript: ""
	};

	if (typeof fieldId != "undefined")
		own.fieldId = fieldId;
	if (typeof labelText == "string")
		own.labelText = labelText;
	if (typeof defaultValue != "undefined")
		own.defaultValue = defaultValue;

	this.init = function(field, label, defaultValue) {
		// set field and fieldId properties
		own.field = elm.get(field);
		if (own.field == null) own.field = elm.get(own.fieldId);
		if (own.field == null) return false;
		if (typeof own.field.id != "undefined")
			own.fieldId = own.field.id;

		own.setLabel(elm.get(label));

		// set default field value if specified
		if (typeof defaultValue != "undefined")
			own.defaultValue = defaultValue;
		if ((own.defaultValue != "") && (own.getValue() == ""))
			own.setValue(own.defaultValue);

		// check field when changed
		evt.add(own.field, "focus", own.check);
		evt.add(own.field, "change", own.check);
		evt.add(own.field, "keyup",  own.check);

		// check field now
		own.check();
		return true;
	};

	var setValid = function() {
		own.isValid = true;
		cls.remove(own.field, own.invalidClass);
		return true;
	};

	var setInvalid = function() {
		own.isValid = false;
		cls.add(own.field, own.invalidClass);
		return true;
	};

	this.getValue = function() {
		if (typeof own.field.value != "undefined")
			return own.field.value;
		return null;
	};

	this.setValue = function(newValue) {
		if (typeof own.field.value != "undefined")
			own.field.value = newValue;
		else return false;
		return true;
	};

	this.setLabel = function(label) {
		own.label = label;
		if ((own.label != null) && (own.label.innerHTML)) {
			own.labelText = own.label.innerHTML;
		} else return false;
		return true;
	};

	this.setLabelText = function(labelText) {
		if (typeof labelText == "string") {
			own.labelText = labelText;
			if ((own.label != null) && (own.label.innerHTML)) {
				own.label.innerHTML = labelText;
			}
		} else return false;
		return true;
	};

	this.addCheck = function(checkType, checkFunction, errorMsg) {
		if (typeof errorMsg != "string")
			errorMsg = "";
		own.checkFunctions[checkType] = {
			fn: checkFunction,
			err: errorMsg
		}
		own.check();
		return true;
	};

	this.addCheckArray = function(checkFunctionsArray) {
		// add check functions according input class
		for (checkType in checkFunctionsArray) {
			if (checkFunctionsArray[checkType].fn && cls.has(own.field, checkType)) {
				own.addCheck(checkType, checkFunctionsArray[checkType].fn, checkFunctionsArray[checkType].err);
			}
		}
		own.check();
		return true;
	};

	this.addCheckRegExp = function(checkType, checkRegExp, errorMsg) {
		var checkFunction = function() {
			var value = own.getValue();
			if (value == "") return true;
			return (value.search(checkRegExp) < 0) ? false : true;
		};
		own.addCheck(checkType, checkFunction, errorMsg);
		return true;
	};

	this.addCheckRegExpArray = function(checkRegExpArray) {
		// add check regExps according input class
		for (checkType in checkRegExpArray) {
			if (checkRegExpArray[checkType].reg && cls.has(own.field, checkType)) {
				own.addCheckRegExp(checkType, checkRegExpArray[checkType].reg, checkRegExpArray[checkType].err);
			}
		}
		own.check();
		return true;
	};

	this.check = function() {
		var idx; // String index
		var resultOk = true;
		var actualTestOk;
		own.errorMsg.html = "";
		own.errorMsg.javascript = "";
		own.errorMsg.simple = "";
		for (idx in own.checkFunctions) {
			if (typeof own.checkFunctions[idx].fn == "function") {
				actualTestOk = own.checkFunctions[idx].fn(own.getValue());
				resultOk = resultOk && actualTestOk;
				if (!actualTestOk) {
					// TODO - messages have to be transformed to html and js - 
					// this is suitable only for most simple messages
					own.errorMsg.html       = own.checkFunctions[idx].err;
					own.errorMsg.javascript = own.checkFunctions[idx].err;
					own.errorMsg.javascript = own.checkFunctions[idx].err;
				}
			}
		}
		if (!resultOk) {
			setInvalid();
			return false;
		}
		setValid();
		return true;
	};

	this.getErrorMsg = function(msgType) {
		if (typeof msgType == "undefined") msgType = "html";
		var result = own.errorMsg[msgType];
		result = result.replace(/\{label\}/g, "&quot;" + own.labelText + "&quot;");
		result = result.replace(/\{value\}/g, "&quot;" + own.getValue() + "&quot;");
		if (msgType != 'html') {
			result = result.replace(/&nbsp;/g, ' ');
			result = result.replace(/&amp;/g, '&');
			result = result.replace(/&quot;/g, '"');
		}
		return result;
	}
}

//	Class FormSection
//		Generic part of form containing formFields
//	
//	
//	PROPERTIES
//	TODO
//
//
//	CONSTRUCTOR
//	TODO
//	
//	
//	PUBLIC METHODS
//	TODO
//	

function FormSection(sectionId) {
	var own             = this;
	this.sectionId      = "";
	this.section        = null;
	this.isValid        = true; // True if all fields are valid
	this.fields         = new Array();

	if (typeof sectionId != "undefined")
		own.sectionId = sectionId;

	this.init = function(section) {
		// set section and sectionId properties
		own.section = elm.get(section);

		if (own.section == null) own.section = elm.get(own.sectionId);
		if (own.section == null) return false;
		if (typeof own.section.id != "undefined")
			own.sectionId = own.section.id;

		var i = 0; // Int
		var field = null; // htmlElement
		var labels      = own.section.getElementsByTagName("label");
		var inputFields; // htmlCollection
		var textFields; // htmlCollection

		// add INPUT fields
		inputFields = own.section.getElementsByTagName("input");
		for (i = 0; i < inputFields.length; i++) {
			own.addField(inputFields[i]);
		}
		// add TEXTAREA fields
		textFields = own.section.getElementsByTagName("textarea");
		for (i = 0; i < textFields.length; i++) {
			own.addField(textFields[i]);
		}
		return true;
	}; // end init method

	this.addCheck = function(checkType, checkFunction, errorMsg) {
		if (typeof errorMsg != "string")
			errorMsg = "";
		var i; // int iterator
		// add function to each field with proper className
		for (i = 0; i < own.fields.length; i++) {
			if (cls.has(own.fields[i], checkType))
				own.fields[i].addCheck(checkType, checkFunction, errorMsg);
		}
		own.check();
		return true;
	};

	this.addCheckArray = function(checkFunctionsArray) {
		var i; // int iterator
		for (i = 0; i < own.fields.length; i++) {
			own.fields[i].addCheckArray(checkFunctionsArray);
		}
		own.check();
		return true;
	};

	this.addCheckRegExp = function(checkType, checkRegExp, errorMsg) {
		if (typeof errorMsg != "string")
			errorMsg = "";
		var i; // int iterator
		// add regExp to each field with proper className
		for (i = 0; i < own.fields.length; i++) {
			if (cls.has(own.fields[i], checkType))
				own.fields[i].addCheckRegExp(checkType, checkRegExp, errorMsg);
		}
		own.check();
		return true;
	};

	this.addCheckRegExpArray = function(checkRegExpsArray) {
		var i; // int iterator
		for (i = 0; i < own.fields.length; i++) {
			own.fields[i].addCheckRegExpArray(checkRegExpsArray);
		}
		own.check();
		return true;
	};

	this.addField = function(field) {
		// add label to field
		var i; // Int
		var labels      = own.section.getElementsByTagName("label");
		// search labels and link them to their fields
		for (i = 0; i < labels.length; i++) {
			if (field.id == elm.getAttribute(labels[i], "for")) {
				field.label = labels[i];
			}
		}
		var newFieldIndex = own.fields.length;	// Int - index of newly added formField
		var newField; // Object - newly added formField
		own.fields[newFieldIndex] = new FormField();
		own.fields[newFieldIndex].init(field, field.label);
	};

	this.check = function() {
		var i; // Int
		var isValid = true; // Boolean
		for (i = 0; i < own.fields.length; i++) {
			if (typeof own.fields[i].isValid != "undefined") {
				isValid = own.fields[i].isValid && isValid;
				own.isValid = isValid;
			}
			if (!isValid) return false;
		}
		return isValid;
	};

	this.getErrorMsg = function(msgType) {
		if (typeof msgType == "undefined") msgType = "html";
		var result = ""; // String
		var actualMsg = ""; // String
		var i; // Int
		for (i = 0; i < own.fields.length; i++) {
			actualMsg = own.fields[i].getErrorMsg(msgType);
			if (actualMsg != "") {
				if (msgType == "html")
					result += "<li>";
				result += actualMsg;
				if (msgType == "html")
					result += "</li>";
				if (msgType == "javascript")
					result += "\n";
				if (msgType == "simple")
					result += "; ";
			}
		}
		if ((msgType == "html") && (msgType != ""))
			result = "<ul>" + result + "</ul>";
		return result;
	};
}

function SimpleForm(frmId) {
	var own             = this;
	this.frmId          = "";
	this.frm            = null;
	this.isValid        = true; // True if all fields are valid
	this.frmSection     = null;

	if (typeof frmId != "undefined")
		own.frmId = frmId;

	own.frmSection = new FormSection(frmId);

	this.init = function(frm) {
		// set frm and frmId properties
		own.frm = elm.get(frm);

		if (own.frm == null) own.frm = elm.get(own.frmId);
		if (own.frm == null) return false;
		if (typeof own.frm.id != "undefined")
			own.frmId = own.frm.id;

		own.frmSection.init(own.frm);

		evt.add(own.frm, "submit", function(e) {own.checkSubmit(e);});

		return true;
	}; // end init method

	this.addCheck = function(checkType, checkFunction, errorMsg) {
		return own.frmSection.addCheck(checkType, checkFunction, errorMsg);
	}
	
	this.addCheckArray = function(checkFunctionsArray) {
		return own.frmSection.addCheckArray(checkFunctionsArray);
	}
	
	this.addCheckRegExp = function(checkType, checkRegExp, errorMsg) {
		return own.frmSection.addCheckRegExp(checkType, checkRegExp, errorMsg);
	}
	
	this.addCheckRegExpArray = function(checkRegExpsArray) {
		return own.frmSection.addCheckRegExpArray(checkRegExpsArray);
	}
	
	this.addField = function(field) {
		return own.frmSection.addField();
	}

	this.check = function() {
		return own.frmSection.check();
	}

	this.getErrorMsg = function(msgType) {
		return own.frmSection.getErrorMsg(msgType);
	}

	this.checkSubmit = function(e) {
		e = evt.fix(e);
		if (typeof own.frmSection.isValid != "undefined")
			if (!own.frmSection.isValid) {
				alert(own.getErrorMsg('javascript'));
				evt.stop(e);
			}
	}
}



// Object checkRegExps

var checkRegExps = {}

// basic private definitions
checkRegExps._df                    = {};
checkRegExps._df.specChars          = " \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xD7\xF7";
checkRegExps._df.alphaLowercaseISO  = "\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
checkRegExps._df.alphaUppercaseISO  = "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDE";
checkRegExps._df.alphaLowercaseUTF  = "\u00E1\u00E9\u0115\u00ED\u00F3\u00FA\u016F\u00FD\u017E\u0161\u010D\u0159\u010F\u0165\u0148" + checkRegExps._df.alphaLowercaseISO;
checkRegExps._df.alphaUppercaseUTF  = "\u00C1\u00C9\u0114\u00CD\u00D3\u00DA\u016E\u00DD\u0160\u017D\u010c\u0158\u010E\u0164\u0147" + checkRegExps._df.alphaUppercaseISO;


checkRegExps.percentage = {
	// number -  exponential form allowed, unlimited decimal digits
	reg: new RegExp("^(([0-9]{1,2})|(100{1}))$"),
	err: "Field {label} must contain a number from 0 to 100."
};
checkRegExps.number = {
	// notNegative -  positive numbers and zero, exponential form allowed, unlimited decimal digits
	reg: new RegExp("^([\+\-]?[0-9]{1,}([\.][0-9]{1,})?(e[\+\-]{1}[0-9]{1,2})?)$"),
	err: "Field {label} must contain number."
};
checkRegExps.notNegative = {
	// notNegative -  only positive numbers (without zero), exponential form allowed, unlimited decimal digits
	reg: new RegExp("^([\+]?[0-9]{1,}([\.][0-9]{1,})?(e[\+\-]{1}[0-9]{1,})?)$"),
	err: "Field {label} must contain positive number or 0."
};
checkRegExps.positive = {
	reg: new RegExp("^(([\+]?[0-9]*[1-9]{1}[0-9]*([\.][0-9]{1,})?(e[\+\-]{1}[0-9]{1,})?)|([\+]?[0-9]{1,}([\.][0-9]*[1-9]{1}[0-9]*){1}(e[\+\-]{1}[0-9]{1,})?))$"),
	err: "Field {label} must contain number greater than 0."
};
checkRegExps.email = {
	reg: new RegExp("^[a-zA-Z0-9]+[a-zA-Z0-9\._-]*[a-zA-Z0-9]+@[a-zA-Z0-9]+[a-zA-Z0-9\._-]*[a-zA-Z0-9]+[\.]{1}[a-zA-Z]{2,4}$"),
	err: "Field {label} must contain a valid e-mail address."
};
checkRegExps.safeChars = {
	reg: new RegExp("^[0-9a-zA-Z_]{1,}$"),
	err: "Field {label} must contain only safe characters."
};
checkRegExps.alphaLowercase = {
	reg: new RegExp("^.*$"),
	// reg: new RegExp("^[a-z" + checkRegExps._df.alphaLowercaseUTF + " ]{1,}$")),
	err: "Field {label} must contain only lowercase alphabet characters."
};
checkRegExps.alphaUppercase = {
	reg: new RegExp("^.*$"),
	// new RegExp("^[A-Z" + checkRegExps._df.alphaUppercaseUTF + " ]{1,}$")),
	err: "Field {label} must contain only uppercase alphabet characters."
};
checkRegExps.alpha = {
	reg: new RegExp("^.*$"),
	// reg: new RegExp("^[a-zA-Z" + checkRegExps._df.alphaUppercaseUTF + checkRegExps._df.alphaLowercaseUTF + " ]{1,}$")),
	err: "Field {label} must not contain numbers or special characters."
};
checkRegExps.alphanumeric = {
	reg: new RegExp("^.*$"),
	// reg: new RegExp("^[a-zA-Z" + checkRegExps._df.alphaUppercaseUTF + checkRegExps._df.alphaLowercaseUTF + "0-9 ]{1,}$"))
	err: "Field {label} must contain only alphanumeric characters."
};
checkRegExps.text = {
	reg: new RegExp("^.*$"),
	// reg: new RegExp("^[a-zA-Z" + checkRegExps._df.alphaUppercaseUTF + checkRegExps._df.alphaLowercaseUTF + "0-9" + specChars + "]{1,}$")),
	err: "Field {label} must contain only text."
};
checkRegExps.dynamicParams = {
	reg: new RegExp("^[ -~" + checkRegExps._df.alphaUppercaseUTF + checkRegExps._df.alphaLowercaseUTF + "]{1,}$"),
	err: "Field {label} must not contain any numbers or special characters except \"-\" or \"~\"."
};
checkRegExps.color = {
	reg: new RegExp("^[#]{1}[0-9a-fA-F]{6}$"),
	err: "Field {label} must contain hexadecimal RGB color definition (for example #336699)."
};
checkRegExps.url = {
	reg: new RegExp("^http(s)?://(([a-zA-Z0-9]+([-_.]?[a-zA-Z0-9])*\.([a-zA-Z]){2,4})|(([0-9]){1,3}\.([0-9]){1,3}\.([0-9]){1,3}\.([0-9]){1,3}))(:[0-9]{2,5})?(/([-!_:~&=$?.|;,%a-zA-Z0-9[\\]]|(%[0-9a-fA-F]{2}))*)*$"),
	def:  "http://",
	err: "Field {label} must contain valid URL (for example \"http://www.google.com\")."
};
checkRegExps.telephone = {
	reg: new RegExp("^[0-9()+-/ ]{1,}$"),
	err: "Field {label} must contain a valid phone number."
};
checkRegExps.domains = {
	// general check for the list of domains made problems (MS IE)
	// the following regular expression were unsuccessfully tried and then commented out:

	// reg: new RegExp("^(([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*\.[a-zA-Z]{2,4})[,\f\n\r]\n?)*$"),
	// reg: new RegExp("^(([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[\.]{1}[a-zA-Z]{2,4})([ \f\n\r]*[,]?[ \f\n\r]*)+)*$"),
	// reg: new RegExp("^(([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[\.]{1}[a-zA-Z]{2,4}(\/{1}[-_~&=\?\.a-zA-Z0-9]*)*)[,]?)*$"),

	// current solution only checks whether characters are from the valid set
	// the reg.expr. also contains various white spaces to enable the comfortable editing
	// ( white space char (\s) did not work)
	reg: new RegExp("^[0-90-9a-zA-Z\-\_\.\~\&\=\?,\r\n\t\f ]{1,}$"),
	err: "Field {label} must contain domain names."
};
// local specific items.
// international, no exponential form - old version without internationalization
checkRegExps.numeric = {
	reg: new RegExp("^[\+\-]?[0-9 ]{1,}$"),
	err: "Field {label} must contain domain names."
};
checkRegExps.floatNumber = {
	// 2 decimal digits
	reg: new RegExp("^[\+\-]?[0-9]+([.][0-9]{1,2})?$"),
	err: "Field {label} must contain domain names."
};
checkRegExps.date = {
	// in format (D)D.(M)M.YYYY
//	reg: new RegExp("^[0-9]{1,2}[.][0-9]{1,2}[.][0-9]{4}$"),
	reg: new RegExp("^[0-9]{4}[\-]{1}[0-9]{1,2}[\-]{1}[0-9]{1,2}$"),
	err: "Field {label} must contain date in format YYYY-MM-DD."
};


var checkFunctions = {
	required: {
		fn:
			function(str) {
				return (str.search(/\S/) < 0) ? false : true;
			},
		err: "Field {label} must not be empty."
	},
	
	multipleEmails: {
		// requires checkRegExps.email
		fn:
			function(str) {
				if (str == "") return true;
				var addresses = str.split(','); // Array of Strings
				var fieldOK = true; // Boolean
				var rule = checkRegExps.email; // RegExp
				var i; // Int
				var actualAddress; // String
				for (i = 0; i < addresses.length; i++) {
					if (addresses[i].trim) actualAddress = addresses[i].trim();
					else actualAddress = addresses[i];
					fieldOK = fieldOK && ((actualAddress.search(rule) < 0) ? false : true);
				}
				return fieldOK;
			},
		err: "Field {label} must contain only valid e-mail addresses separated by commas \",\"."
	}
}; // end checkFunctions


