/* From scottandrew.com via simon.incutio.com */
function addEvent(obj, evType, fn, useCapture) {
	if (obj && obj.addEventListener) {
		obj.addEventListener(evType, fn, useCapture);
		return true;
	} else if (obj && obj.attachEvent) {
		var r = obj.attachEvent("on" + evType, fn);
		return r;
	} else {
		// alert('Handler could not be attached');
		return false;
	}
}

// capture the event type (used for blur events)
var formevent;

// fetch xml file
var xmlDoc;
function importXML(url, func) {
	xmlDoc = false;
	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest && !(window.ActiveXObject)) {
		try {
			xmlDoc = new XMLHttpRequest();
		} catch (e) {
			xmlDoc = false;
		}
		// branch for IE/Windows ActiveX version
	} else if (window.ActiveXObject) {
		try {
			xmlDoc = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {
				xmlDoc = false;
			}
		}
	}
	if (xmlDoc) {
		xmlDoc.onreadystatechange = processReqChange;
		xmlDoc.open("GET", url, true);
		xmlDoc.send("");
	}
}

function processReqChange() {
	// only if req shows "loaded"
	if (xmlDoc.readyState == 4) {
		// only if "OK"
		if (xmlDoc.status == 200) {
			parseFormXml();
		} else {
			alert("There was a problem retrieving the XML data:\n"
					+ xmlDoc.statusText);
		}
	}
}

// parse form
function parseFormXml() {
	form = document.getElementById(xmlDoc.responseXML.documentElement
			.getAttribute('id'));
	xmlDoc = xmlDoc.responseXML;
	// external lib check
	if (form === null) {
		// prototype
		if (typeof Prototype !== 'undefined') {
			form = $(xmlDoc.documentElement.getAttribute('id'));
		}
	}
	//onsubmit, form is checked
	if (!addEvent(form, 'submit', checkForm, false)) { // if using an image as submit
		addEvent(form, 'click', checkForm, false); // look for click event instead
	}
	validators = new Array();
	elements = xmlDoc.getElementsByTagName('element');
	//create element validator objects and add to array
	for ( var i = 0; i < elements.length; i++) {
		val = new ElementValidator(elements[i]);
		validators[val.id] = val;
		//onblur check validity
		addEvent(val.element, 'blur', onblurCheck, false); // change blur
	}
}

//called on form submit; cycle through make sure all elements are valid
function checkForm(event) {
	
	formevent = 'submit';
	
	var pass = true;
	
	for ( var i in validators) {
		// External librairies such as Prototype will often extend properties and functions on to our elements, this check makes sure we only get valid elements.        
		if (typeof validators[i].check != 'undefined') {
			validators[i].check();
			if (validators[i].valid == false) {
				pass = false;
			}
		}
	}

	if (pass == false) {
		
		// Build 693
		message = jspopup_errormessage;
		if (showAlert) {
			alert(message);
		}
		
		// Stop form submit

		// http://www.webdevelopersjournal.com/articles/jsevents2/jsevents2.html
		
		// Build 717 - IE 9 Now supports document.addEventListener (!)
		// http://robertnyman.com/2008/11/04/internet-explorer-8-fix-event-handling-or-dont-release-it/
		
		// ...which means while document.all returns true, we actually need to use the DOM Lvl 2 method 
		// for preventing event propegation. Hence, sniff for IE 9 and if so, issue event.preventDefault,
		// else we use the older method for > IE 9
		// var t = window.ScriptEngineMajorVersion(); // 9 for IE 9, 5 for IE 8 // This is an IE only function, it will break other browsers.
		
		if (document.all) { // IE
			// http://www.howtocreate.co.uk/tutorials/jsexamples/sniffer.html
			if(document.getElementById && document.compatMode && window.XMLHttpRequest && document.documentMode && window.ScriptEngineMajorVersion() && window.ScriptEngineMajorVersion() >= 9){
				event.preventDefault();
			}
			// Pre-IE 9
			event.cancelBubble = true
			event.returnValue = false;
		}
		
		//standard w3c model - moz
		else {
			event.preventDefault();
		}
	} else { // Build 633 - Return For Image Submit
		return pass;
	}
}

//get document name and calculate xml document to be imported
function getXmlUrl() {

	// Build 631
	var d = new Date();
	var randts = d.getTime();
	url = phppath + pageName + '?rand=' + randts;
	//alert(url);
	return url;

	// legacy
	var url = window.location.href;
	// if it ends with a get query, remove the query
	url = url.split('?')[0];
	// remove any anchor links
	url = url.split('#')[0];
	//Grab the filename
	if (url.match(/\w+\.[a-zA-Z0-9]+$/) !== null) {
		//Replace filename.extension with filename.xml
		url = url.match(/\w+\.[a-zA-Z0-9]+$/).toString();
	} else {
		url = 'index.php';
	}
	var dot = url.lastIndexOf('.');
	// 631 mod_rewrite protection
	if (dot == -1) {
		dot = url.length;
	}
	url = url.substring(0, dot);
	// Build 623 - added random seed
	var d = new Date();
	var randts = d.getTime();
	url = phppath + url + '.xml?rand=' + randts;
	//alert(url);
	return url;
}

function onblurCheck(event) {
	//ie
	if (document.all) {
		id = event.srcElement.getAttribute('id');
	}
	//moz
	else {
		id = this.getAttribute('id');
	}
	validators[id].check();
}

//Element Validator object
function ElementValidator(node) {
	this.id = node.getAttribute('id');
	this.element = document.getElementById(this.id);
	this.valid = true;
	this.min = node.getAttribute('min');
	this.max = node.getAttribute('max');
	
	// Build 668
	this.countmethod = node.getAttribute('countmethod');
	
	this.req = node.getAttribute('req') == "true";
	this.regs = new Array();
	regexes = node.getElementsByTagName('regex');
	for ( var i = 0; i < regexes.length; i++) {
		//grab text inside regex tag(s)
		this.regs[i] = new RegExp(regexes[i].childNodes[0].nodeValue);
	}
	err = node.getElementsByTagName('error')[0];
	if (err != null) {
		this.error = err.childNodes[0].nodeValue;
	} else {
		this.error = null;
	}
	this.name = node.getAttribute('name');
	this.sameAs = node.getAttribute('sameas');
	//get reference to element node that value should equal
	if (this.sameAs != null) {
		this.sameAs = document.getElementById(this.sameAs);
	}

	// enclosing elt - this caches our class name for resetting later during makeValid()
	if (this.element !== null) {
		this.parentClass = this.element.parentNode.className;
		this._cached_id = ""; // default value
	}

	this.check = function() {

		// Confirmation value. Must be same as confirming value + valid for confirming value's rules
		// Build 651 - Added element check and abort for IE
		if (this.element != null) {
			type = this.element.getAttribute('type');
		} else {
			return;
		}

		// Build 717 - Process Hidden Elements
		// Inspect each element and check if its parent container is hidden.
		
		// Create special case for calendar items, as they have a different naming convention.
		// That is, calendar items can pass three fields, we need to remove these extra bits.
		base_name = this.element.id;
		base_name = base_name.replace("date-", "");
		base_name = base_name.replace("-1", "");
		base_name = base_name.replace("-2", ""); // Build 724 - Check all(!) fields.
		base_name = base_name.replace("-3", "");
		
		if(base_name != this.element.id){
			
			if(document.getElementById('fb_fld-' + base_name).style.display == 'none'){
				this.makeValid(); // if no, mark as valid by default
				return;
			}
			
		} else { // all other items get standard check...
			
			if(type == 'checkbox' || type ==  'radio'){
				
				// Build 717 - Is this item visible?
				field_name = document.getElementById(this.element.id).getAttribute('name'); // get main node name, not indexed version (e.g. radio0 instead of radio01)
				
				// checkboxes have [], remove it
				field_name = field_name.replace('[]', '');
				
				parent_invisible = hasInvisibleParentNode(document.getElementById('fb_fld-' + field_name));
				
				if(document.getElementById(this.element.id).style.display == 'none' || parent_invisible == true){
					this.makeValid(); // if no, mark as valid by default
					return;
				}
				
			} else {
		
				// Build 717 - Is this item or any of its parents invisible?
				parent_invisible = hasInvisibleParentNode(document.getElementById('fb_fld-' + this.element.id));
				if(document.getElementById('fb_fld-' + this.element.id).style.display == 'none' || parent_invisible == true){
					this.makeValid(); // if no, mark as valid by default
					return;
				}
				
			}
			
		}
		
		
		// process non-text elements, iterate through each
		switch (type) {
		
		case 'checkbox':
			element_name = this.element.name;
			form_name = this.element.form.id;
			element_id = this.element.id;
			var x = document.getElementsByName(element_name);

			if ((this.req == false)) {
				this.makeValid();
				return;
			}

			// default false
			pass = false;
			
			// Build 706 - how many are checked logic
			checked_count = 0;
			for (i = 0; i < x.length; i++) {
				if(x[i].checked){
					checked_count++;
				}				
			}
			
			if ((this.min != null && checked_count < this.min)
					|| (this.max != null && checked_count > this.max)) {
				this.makeInvalid(this.checkboxLenErrMsg()); // new error handler for this logic
				return;
			}
			
			for (i = 0; i < x.length; i++) {

				var chk = x[i].checked;
				if (this.req && chk == true) {
					pass = true;
					this.makeValid();
					return;
				} else if ((this.req == false) && (chk == false)) {
					this.makeInvalid(this.reqErrMsg());
					return;
				}
			}
		
			
			if (pass == true) {
				pass = true;
				this.makeValid();
				return;
			} else {
				this.makeInvalid(this.reqErrMsg());
				return;
			}
			break;
			
		case 'radio':
			element_name = this.element.name;
			form_name = this.element.form.id;
			element_id = this.element.id;
			var x = document.getElementsByName(element_name);

			if ((this.req == false)) {
				this.makeValid();
				return;
			}

			// default false
			pass = false;
			for (i = 0; i < x.length; i++) {

				var chk = x[i].checked;
				if (this.req && chk == true) {
					pass = true;
					this.makeValid();
					return;
				} else if ((this.req == false) && (chk == false)) {
					this.makeInvalid(this.reqErrMsg());
					return;
				}
			}
			if (pass == true) {
				pass = true;
				this.makeValid();
				return;
			} else {
				this.makeInvalid(this.reqErrMsg());
				return;
			}
			break;

		}
		//
		if (this.sameAs != null) {
			if (this.element.value == this.sameAs.value) {
				if (validators[this.sameAs.getAttribute('id')].valid == true) {
					this.makeValid();
				} else {
					// This is triggered if a user hasn't filled in the first element of a same-as block
					var otherName = validators[this.sameAs.getAttribute('id')].name;
					this.makeInvalid('Your ' + otherName + ' is not correct.');
					// You can change this message to something else if need be, for example:
					//this.makeInvalid('Please fill out ' + otherName + ' first.');
				}
			} else {
				var otherName = validators[this.sameAs.getAttribute('id')].name;
				var msg = 'This value must be identical to your ' + otherName + '.';
				this.makeInvalid(msg);
			}
		} else {
			var val = this.element.value;
			//first check for required
			if (this.req && val == "") {
				this.makeInvalid(this.reqErrMsg());
				return;
			}
			//if not required and no value, is valid
			else if ((this.req == false) && (val == "")) {
				this.makeValid();
				return;
			}
			
			// check for length :: Build 668 - character or word count
			switch(this.countmethod){
				case null :
					if ((this.min != null && val.length < this.min)
							|| (this.max != null && val.length > this.max)) {
						this.makeInvalid(this.lenErrMsg());
						return;
					}
					break;
				case 'cntChars' :
					if ((this.min != null && val.length < this.min)
							|| (this.max != null && val.length > this.max)) {
						this.makeInvalid(this.lenErrMsg());
						return;
					}
					break;
				case 'cntWords' :
					var y = val;
					var r = 0;
					a = y.replace(/\s/g, ' ');
					a = a.split(' ');
					for (z = 0; z < a.length; z++) {
						if (a[z].length > 0)
							r++;
					}
					if ((a.length != null && a.length < this.min)
							|| (this.max != null && a.length > this.max)) {
						this.makeInvalid(this.lenErrMsg());
						return;
					}
					break;
			
			}

			
			//check that it matches at least one supplied regex, if any supplied
			if (this.regs.length > 0) {
				var pass = false;
				for ( var i = 0; i < this.regs.length; i++) {
					if (this.regs[i].test(val)) {
						pass = true;
						break;
					}
				}
				if (pass == false) {
					this.makeInvalid(this.error);
					return;
				}
			}
			
			//passed all tests
			this.makeValid();
		}
	}
	
	// Build 706
	this.checkboxLenErrMsg = function() {
		var cap = this.name.substring(0, 1).toUpperCase();
		var capName = cap + this.name.substring(1, this.name.length);

		if (this.min != null && this.max != null) {
			// if min and max are the same
			if(this.min == this.max){
				ret = "You must select " + this.min + " items";
			} else {
				ret = 'You must select between ' + this.min + ' and ';
				ret += this.max + ' items.';
			}
			return ret;
		} else if (this.min != null) {
			return 'You must select at least ' + this.min + ' items.';
		} else //max, no min
		{
			return 'You cannot select more than ' + this.max + ' items.';
		}
	};

	this.lenErrMsg = function() {
		var cap = this.name.substring(0, 1).toUpperCase();
		var capName = cap + this.name.substring(1, this.name.length);
		if (this.min != null && this.max != null) {
			ret = capName + ' must be between ' + this.min + ' and ';
			ret += this.max + ' characters.';
			return ret;
		} else if (this.min != null) {
			return capName + ' must be more than ' + this.min + ' characters.';
		} else //max, no min
		{
			return capName + ' must be less than ' + this.max + ' characters.';
		}
	};

	this.reqErrMsg = function() {
		letter = this.name.substring(0, 1);
		switch (letter) {
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			word = 'an';
			break;
		default:
			word = 'a';
			break;
		}

		// Build 584
		val = this.name.replace(/_/g, " ");

		// Build 586
		if (showDefault) {
			return '-You must supply ' + word + ' ' + val + '.';
		} else {
			return this.error;
		}

	};

	
	this.makeInvalid = function(errMsg) {
		
		switch(errorStyle){
			case 0 :
				
				// save existing styles
				if(errorBorderStyles[this.id] === undefined){
					errorBorderStyles[this.id] = this.element.style.border;
				}
				
				if (this.valid == false) {
					this.element.style.border = "1px dashed " + errorColor;
				} else {
					this.element.style.border = "1px dashed " + errorColor;
				}
				this.valid = false
				
				break;
				
			case 1 :
				
				this.element.parentNode.className = this.parentClass + " error";
				// Insert error message
				// If already invalid will have an error message already
				if (this.valid == false) {
					errorNode = document.getElementById(this.id + 'errmsg');
					if (showMessage) {
						textNode = document.createTextNode(errMsg);
					} else {
						textNode = document.createTextNode('');
					}
					//childnodes[0] is the old error text
					errorNode.replaceChild(textNode, errorNode.childNodes[0]);
				} else {
					//create and add to <li> a span with an error message
					span = document.createElement('span');
					span.className = "errormsg";
					span.setAttribute("id", this.id + "errmsg");
					
					if (showMessage) {
						textNode = document.createTextNode(errMsg);
					} else {
						textNode = document.createTextNode('');
					}
					
					span.appendChild(textNode);
					this.element.parentNode.appendChild(span);
				}
				this.valid = false;
				
				break;
				
			// Build 700 - Simple Icon Style	
			case 2 :
				
				// get id of node
				var node = this.element;
				
				while(node !== null){	
					if(node.id.search(/fb_fld-[\S]/) != -1){
						idtmp = node.id.split('fb_fld-');
						id = idtmp[1];
						document.getElementById(id + '-validation-style-3-icon').style.display = "block";
						
						if(tablemode != 1){
							document.getElementById(id + '-validation-style-3-line').style.display = "block";
						}
						
						if(showMessage && tablemode != 1 && layout != 1){
							document.getElementById(id + '-validation-style-3-message').innerHTML = errMsg;
							document.getElementById(id + '-validation-style-3-message').style.display = "block";
						}
						
						// custom style for tablemode - places icons to the direct left of field.
						if(tablemode == 1){
							document.getElementById(id + '-validation-style-3-icon').style.right = "-8px";
							document.getElementById(id + '-validation-style-3-icon').style.top = "-13px";
						}
						
						this._cached_id = id;
						break;
					} else {
						node = node.parentNode;
					}
				}

			
				this.valid = false;
			
				break;
		
		}

		// Build 663
		if(formevent == 'submit'){ // only scroll to when we submit
			ScrollToElement(document.getElementById('fb_fld-' + this.id));
		}
		
		formevent = null;
		
	};

	
	this.makeValid = function() {
		
		switch(errorStyle){
			case 0 :
				
				// get existing styles
				if(errorBorderStyles[this.id] !== undefined){
					cacheStyle = errorBorderStyles[this.id];
					if(cacheStyle != ""){
						this.element.style.border = cacheStyle;
					} else {
						this.element.style.border = "";
						// IE Bug Fix
						this.element.style.borderColor = "";
						this.element.style.borderWidth = "";
						this.element.style.borderStyle = "";
					}
				}
				
				this.valid = true;

				break;	
				
			case 1 :
				// remove error message if present
				if (this.valid == false) {
					errorNode = document.getElementById(this.id + 'errmsg');
					this.element.parentNode.removeChild(errorNode);
				}
				this.valid = true;
				this.element.parentNode.className = this.parentClass;
				
				break;
			
			// Build 700 - Simple Icon Style
			case 2 :
				
				// Clear items
				if(this._cached_id !== "" && this._cached_id !== undefined){
					
					// hide icon 
					document.getElementById(this._cached_id + '-validation-style-3-icon').style.display = "none";
					
					// hide line 
					document.getElementById(this._cached_id + '-validation-style-3-line').style.display = "none";
					
					// hide message 
					document.getElementById(this._cached_id + '-validation-style-3-message').style.display = "none";
					
				}
				
				this.valid = true;
				

				break;
		}
	};
	
}

// Build 663
function ScrollToElement(theElement) {

	var selectedPosX = 0;
	var selectedPosY = 0;

	while (theElement != null) {
		selectedPosX += theElement.offsetLeft;
		selectedPosY += theElement.offsetTop;
		theElement = theElement.offsetParent;
	}

	window.scrollTo(selectedPosX, selectedPosY);

}

function cntMCEChars(w, eid, mx) {
	var y = w.length;
	document.getElementById('count_' + eid).innerHTML = y;
	document.getElementById('left_' + eid).innerHTML = (mx - y);
	if (y > mx) {
		document.getElementById(eid + '_err').innerHTML = "Too many characters!";
	} else {
		document.getElementById(eid + '_err').innerHTML = "";
	}
}

function cntChars(w, eid, mx) {
	var y = w.value.length;
	document.getElementById('count_' + eid).innerHTML = y;
	document.getElementById('left_' + eid).innerHTML = (mx - y);
	if (y > mx) {
		document.getElementById(eid + '_err').innerHTML = "Too many characters!";
	} else {
		document.getElementById(eid + '_err').innerHTML = "";
	}
}

function cntMCEWords(w, eid, mx) {
	var y = w;
	var r = 0;
	a = y.replace(/\s/g, ' ');
	a = a.split(' ');
	for (z = 0; z < a.length; z++) {
		if (a[z].length > 0)
			r++;
	}
	document.getElementById('count_' + eid).innerHTML = a.length;
	document.getElementById('left_' + eid).innerHTML = (mx - a.length);
	if (a.length > mx) {
		document.getElementById(eid + '_err').innerHTML = "Too many words!";
	} else {
		document.getElementById(eid + '_err').innerHTML = "";
	}
}

function cntWords(w, eid, mx) {
	var y = w.value;
	var r = 0;
	a = y.replace(/\s/g, ' ');
	a = a.split(' ');
	for (z = 0; z < a.length; z++) {
		if (a[z].length > 0)
			r++;
	}
	document.getElementById('count_' + eid).innerHTML = a.length;
	document.getElementById('left_' + eid).innerHTML = (mx - a.length);
	if (a.length > mx) {
		document.getElementById(eid + '_err').innerHTML = "Too many words!";
	} else {
		document.getElementById(eid + '_err').innerHTML = "";
	}
}

function hasInvisibleParentNode(node){
	while(node.parentNode !== null){
		node = node.parentNode;
		if(node.style!== undefined && node.style.display == 'none'){
			return true;
		} else {
			hasInvisibleParentNode(node);
		}
	}
	
	return false;
}

/**
 * Utility Functions for common tasks
 */

var fb = {

	FormBoss_Utility_Version :'637',

	/**
	 * Toggle a field's default value on and off, but only if the fields value
	 * has not been changed by the user.
	 * @since 637
	 * @author nicSoft
	 */
	toggle : {

		fb_flds :new Array(),
		fb_flds_original :new Array(),
		fb_tripped :new Array(),

		// Save a text fields value and id to an array
		save : function(elt) {
			id = elt.id;
			value = elt.value;
			// save unique id in hash
			hit = false;
			for (i = 0; i < fb_tripped.length; i++) {
				if (fb_tripped[i] == id) {
					hit = true;
				}
			}
			// remove on first hit
			if (!hit) {
				fb_tripped.push(id);
				// store the value
				fb_flds[id] = value;
				// remove the value
				document.getElementById(id).value = "";
			} else {
				// remove if later hit
				for (i = 0; i < fb_tripped.length; i++) {
					if (fb_tripped[i] == id && fb_flds[id] == value) {
						document.getElementById(id).value = "";
					}
				}
			} // else
		},

		revert : function(elt) {
			id = elt.id;
			value = elt.value;
			if (value == '') {
				document.getElementById(id).value = fb_flds[id];
			}
		}
	}
// end toggle

}; // end fb
