/** 
 * Generic form handler classes and methods. 
 * 
 * An OO approach to form items and their validation.
 * Items should be declared at the beginning of the form page, including 
 * items that are merely DHTML switchers (or Showers) for other items. 
 *
 * @package FCValidation
 * @author Justin Moses
 * @copyright © Flexirent Capital Pty Ltd 
 * @version 1.1.2
 *
 * @history
 *      ----------------------------------------------
 *		1.1.2 - Implements two new parameters to ValidateDate - onlyPast and onlyFuture
 *      1.1.1 - Containers now have turnOn and turnOff to handle the 
 *              lack of control from radio buttons.
 *              Added like control to mirrors, for consistency. 
 *		1.1.0 - Reoverhaul From 1.0. 
 *				Now handles CHECKBOX/RADIO validation (FormList).
 *      ----------------------------------------------
 *		1.0.2 - Fixed: Used 'dob' modified to 'date' to refer to dates within validation. 
 *				Modified: Date check allows d/m/yyyy format, but now requires the / separator 
 *		1.0.1 - Added: Added 'dob' for date verification in DD/MM/YYYY format (with any separator) 
 * 		1.0.0 - First release. 
 *				Restricted form functionality - handles DHTML containeers and validation. 
 *				Validation limited to TEXT/SELECT input.
 *		---------------------------------------------------------------------------------
 */


 
/** MACROMEDIA:: Generic Dreamweaver function 
 *
 * Finds and returns an object via its ID in the DOM 
 */
function MM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}

/******************************************************/

//Flexirent namespace holder
FlexirentCapital = {}; 

/** 
 * Class to handle an entire form - 
 * with validators, DHTML containers, and mirror items
 * 
 */
function FC_FormHelper() 
{
	//this.name = formName;
	
	this.validators = new Array();
	this.vCount = 0;
	
	this.containers = new Array();
	this.cCount = 0;
	
	this.mirrors = new Array();
	this.mCount = 0; 
	
	//======================================//
	this.appendValidator = function(newVal) 
	{
		this.validators[this.vCount++] = newVal;
	}
	
	this.appendContainer = function(newCont) 
	{
		this.containers[this.cCount++] = newCont;
	}
	
	this.appendMirror = function(newMir) 
	{
		this.mirrors[this.mCount++] = newMir;
	}
	
	//=====================================//
	
	this.findContainer = function(contName) 
	{
		//find the container; 
		for (var i in this.containers) 
		{
			if (this.containers[i].id == contName) 
			{
				return this.containers[i];
			}
		}
	}
	
	this.toggleContainer = function(contName) 
	{
		var cont = this.findContainer(contName);
		cont.toggle();
	}
	
	this.turnOnContainer = function(contName) 
	{
	    var cont = this.findContainer(contName);
		cont.turnOn();
	}
	
	this.turnOffContainer = function(contName) 
	{
	    var cont = this.findContainer(contName);
		cont.turnOff();
	}
	
	this.findMirror = function(mirName) 
	{
	    //find the mirror 
		for (var i in this.mirrors) 
		{
			if (this.mirrors[i].id == mirName) 
			{
			    return this.mirrors[i];
			}
		}
	
	}
	
	this.toggleMirror = function(mirName) 
	{
		var mir = this.findMirror(mirName);
		mir.toggle();
	}
	
	this.turnOnMirror = function(mirName) 
	{
	    var mir = this.findMirror(mirName);
		mir.turnOn();
	}
	
	this.turnOffMirror = function(mirName) 
	{
	    var mir = this.findMirror(mirName);
		mir.turnOff();
	}
	
	//====================================//
	
	this.validateForm = function() 
	{
		var errorString = "";
		
		//loop over all validators, building an error string if any errors	
		for (var i in this.validators) 
		{
			var val = this.validators[i];
			
			if (!val.validate()) 
			{
				errorString += val.errMsg; 
			}	
		}	
			
		if (errorString != "") 
		{
			alert("The following error(s) occurred:\n" + errorString);
			return false;
		} 
		else 
		{
			return true;
		}
	}
	
	//===================================//
}

/** Handles TEXT and TEXTAREA and SELECT and BUTTON form items
 */
function FC_FormItem(id) 
{
	this.id = id;
	
	this.container;
	
	//If true, this directive ensures that if the container is off during validation, 
	//then the values of all form items within will be cleared
	this.clearIfOff = true;
	
	this.object = MM_findObj(this.id);
	
	//ensures that when the validation is parsed, that this 
	//container is active if validation is to be done
	this.setContainer = function(cont, clearIfOff) 
	{
		this.container = cont;
		if (clearIfOff != null) 
		{
			this.clearIfOff = clearIfOff;
		}
	}
	
	this.getValue = function() 
	{
		return this.object.value;
	}
	
	this.setValue = function(value) 
	{
		this.object.value = value;
	}
	
	this.mirror = function(mirrorTo) 
	{
		this.setValue(mirrorTo.getValue());
	}
	
	this.clear = function() 
	{
		this.setValue("");
	}
}


/** FormList - a form item of list elements, eg RADIO or CHECKBOX (not SELECT) 
 * 
 * @param name String The name of the item list 
 * @param children Array An array of the IDs of the children 
 */
function FC_FormList(name, childrenIds) 
{
	this.name = name; 
	
	
	//Array of element [DOMRadio or DOMCheckbox]
	this.children = new Array(); 
	
	this.container;
	this.clearIfOff = true;
	
	for (var i in childrenIds) 
	{
		this.children[i] = MM_findObj(childrenIds[i]);
	}
	
	
	this.setContainer = function(cont, clearIfOff) 
	{
		this.container = cont;
		
		if (clearIfOff != null) 
		{
			this.clearIfOff = clearIfOff;
		}
	}
	
	this.mirror = function(mirrorTo) 
	{
		if (this.children.length != mirrorTo.children.length) 
		{
			//an incorrectly set mirror 
			return;
		}
		
		for (var i in this.children) 
		{
			this.children[i].checked = mirrorTo.children[i].checked; 	
		}
	}
	
	this.clear = function() 
	{
		for (var i in this.children) 
		{
			this.children[i].checked = false; 	
		}
	}
	
}

/******** VALIDATION ************/

function FC_Validate(formItem, displayName, required) 
{
	this.formItem = formItem;
	this.displayName = displayName;
	this.errMsg = "";
	
	//Items that aren't required, will only be checked IF they are NOT empty 
	this.required = true;
	
	if (required != null)
	{
		this.required = required;
	}


	this.validate = function() {

	    //ignore any items not switched on
	    this.errMsg = "";

	    if (this.formItem.container && !this.formItem.container.on) {
	        //if the container isn't on then ignore this validation
	        if (this.formItem.clearIfOff) {
	            this.formItem.clear();
	        }
	        return true;
	    }

	    var fItem = this.formItem.object;


	    if (this.required && fItem.value == "") {
	        if (!this.displayName.toUpperCase().match("STATE")) {
	            this.errMsg = "- " + this.displayName + " is required.\n";
	            return false; 
	        }
	    }

	    return true;
	}
}



function FC_ValidateDate(formItem, displayName, separator, required, onlyPast, onlyFuture) 
{
	//inherit the constructor from FC_Validate
	FC_Validate.call(this,formItem,displayName);
	
	this.separator = separator; 
	
	//Items that aren't required, will only be checked IF they are NOT empty 
	this.required = true;
	
	this.onlyPast = false; 
	
	this.onlyFuture = false; 
	
	if (required != null)
	{
		this.required = required;
	}
	
	if (onlyPast != null) 
	{
		this.onlyPast = onlyPast;
	}
	
	if (onlyFuture != null)
	{
		this.onlyFuture = onlyFuture;
	}
	
	this.validate = function()
	{
		
		this.errMsg = "";
		
		//ignore any items not switched on
		if (this.formItem.container && !this.formItem.container.on) 
		{
			//if the container isn't on then ignore this validation
			if (this.formItem.clearIfOff) 
			{
				this.formItem.clear();
			}
			return true;
		}
		
		//get the object 
		var item = this.formItem.object;
		
		//allow unrequired items that are null to pass
		if (!this.required && item.value == "") 
		{
			return true; 
		}
		
		var date_items = item.value.split(this.separator);
		if (date_items.length != 3) 
		{
			this.errMsg = "- " + this.displayName + " must be in DD" + this.separator + "MM" + this.separator + "YYYY format.\n";
			return false;
		}
		else 
		{
			var day = date_items[0];
			var month = date_items[1];
			var year = date_items[2];
			
			if (isNaN(day) || day <= 0 || day > 31) 
			{
				this.errMsg = "- " + this.displayName + " must contain a valid day.\n";
				return false;
			}
			
			if (isNaN(month) || month <= 0 || month > 12)
			{
				this.errMsg = "- " + this.displayName + " must contain a valid month.\n";   
				return false;
			}
			
			//NOTE: the year limits here are hard coded to 1800-2200. These could be calculated dynamically (though that will depend on the user's computer time)
			if (isNaN(year) || year <= 1800 || year >= 2200) 
			{
				this.errMsg = "- " + this.displayName + " must contain a valid year.\n";
				return false;
			}  
			
			if (this.onlyPast) 
			{
				var myDate = new Date();
				myDate.setFullYear(year,(month-1),day);
				
				var today = new Date();
				if (myDate >= today) 
				{	
					this.errMsg = "- " + this.displayName + " must contain a valid date in the past.\n";
					return false;	
				}
			
			}
			
			if (this.onlyFuture) 
			{
				var myDate = new Date();
				myDate.setFullYear(year,(month-1),day);
				
				var today = new Date();
				if (myDate <= today) 
				{	
					this.errMsg = "- " + this.displayName + " must contain a valid date in the future.\n";
					return false;	
				}
			
			}
			
		}
		
		return true;
	
	}

}


function FC_ValidateList(formList, displayName, required) 
{
	//inherit the constructor from FC_Validate
	FC_Validate.call(this,formList,displayName);

	//Items that aren't required, will only be checked IF they are NOT empty 
	this.required = true;
	
	if (required != null)
	{
		this.required = required;
	}
	
	
	this.validate = function () 
	{	
		
		this.errMsg = "";
		
		//unrequired lists can pass
		if (!this.required) 
		{
			return true;
		}
		
		//ignore any items not switched on
		if (this.formItem.container && !this.formItem.container.on) 
		{
			//if the container isn't on then ignore this validation
			if (this.formItem.clearIfOff) 
			{
				this.formItem.clear();
			}
			return true;
		}
		
		var found = false; 
		
		for (var i in this.formItem.children) 
		{
			if (this.formItem.children[i].checked) 
			{
				found = true;
				break;
			}	
			
		}
		
		if (!found) 
		{
			this.errMsg = "- An option must be chosen for " + this.displayName + ".\n";
			return false;
		}
		
		return true;
	
	}
	
}


function FC_ValidateEmail(formItem, displayName, required) 
{
	//inherit the constructor from FC_Validate
	FC_Validate.call(this,formItem,displayName);

	//Items that aren't required, will only be checked IF they are NOT empty 
	this.required = true;
	
	if (required != null)
	{
		this.required = required;
	}
	
	this.validate = function() 
	{
		
		this.errMsg = "";
		
		//ignore any items not switched on
		if (this.formItem.container && !this.formItem.container.on) 
		{
			//if the container isn't on then ignore this validation
			if (this.formItem.clearIfOff) 
			{
				this.formItem.clear();
			}
			return true;
		}
		
		//get the object 
		var item = this.formItem.object;
		
		//allow unrequired items that are null to pass
		if (!this.required && item.value == "") 
		{
			return true; 
		}
		
		var at = item.value.indexOf("@");
		
		if (at < 1 || at == (item.value.length - 1)) 
		{
			this.errMsg = "- " + this.displayName + " must contain an e-mail address.\n";
			return false;
		}
		
		return true;
	}
	
	
}


function FC_ValidateNumber(formItem, displayName, required, exactDigits, min, max) 
{
	//inherit the constructor from FC_Validate
	FC_Validate.call(this,formItem,displayName);
	
	this.exact = exactDigits; 
	this.min = min;
	this.max = max;
	
	//Items that aren't required, will only be checked IF they are NOT empty 
	this.required = true;
	
	if (required != null)
	{
		this.required = required;
	}
	
	this.validate = function() 
	{
		this.errMsg = "";
		
		//ignore any items not switched on
		if (this.formItem.container && !this.formItem.container.on) 
		{
			//if the container isn't on then ignore this validation
			if (this.formItem.clearIfOff) 
			{
				this.formItem.clear();
			}
			return true;
		}
		
		//get the object 
		var item = this.formItem.object;
		
		//allow unrequired items that are null to pass
		if (!this.required && item.value == "") 
		{
			return true; 
		}
		
		//ensure something entered
		if (item.value == "") 
		{
			this.errMsg = "- " + this.displayName + " is required.\n"; 
			return false;
		}
		
		//ensure number
		if (isNaN(item.value)) 
		{
			this.errMsg = "- " + this.displayName + " must contain a number.\n"; 
			return false;
		}
		
		//try an exact search if specified
		if (this.exact && item.value.length != this.exact) 
		{
			this.errMsg = "- " + this.displayName + " must be " + this.exact + " digits long.\n"; 
			return false;
		}
		
		//try a range search if specified
		if (this.min && this.max && (item.value < this.min || this.max < item.value)) 
		{
			this.errMsg = "- " + this.displayName + " must contain a number between " +this.min + " and " + this.max + ".\n"; 
			return false;
		}
		
		return true;
	}
	
}

/********* CONTAINERS ***************/

function FC_Container(id, defaultState) 
{
	this.id = id;
	
	if (defaultState != null) 
	{
		this.on = defaultState;
	}
	else
	{
		this.on = false;
	}
	
	this.children = new Array(); 
	this.cCount = 0;
	
	this.addChild = function(child) 
	{
		this.children[this.cCount++] = child;
	}
	
	this.toggle = function() 
	{
		
		if (this.on) 
		{
			//turn all off 
			for (var i in this.children) 
			{
				this.children[i].hide();
			}
			
		}
		else
		{
			//turn all on 
			for (var i in this.children) 
			{
				this.children[i].show();
			}
			
		}
		
		this.on = !this.on;
	}
	
	
	this.turnOn = function() 
	{
	    this.on = false; 
	    this.toggle();
	}
	
	this.turnOff = function() 
	{
	    this.on = true;
	    this.toggle();
	}

}


/** 
 * Any HTML item within a container. 
 *
 * @param id The DOM ID for this element
 * @param method either DISPLAY or VISIBILITY (either remove the item from the display, or make it invisible)
 * @param container (FC_ContainerItem)
 */
function FC_ContainerItem(id, method, container) 
{
	this.id = id; 
	this.method = method;
	
	//add this child to the parent 
	container.addChild(this); 
	
	this.show = function() 
	{
	
		var formObj = MM_findObj(this.id);
		
		if (this.method == "VISIBILITY") 
		{
			formObj.style.visibility = "visible";
		}
		else
		{
			if (navigator.appName == "Netscape") formObj.style.display = "table-row-group";  else formObj.style.display = "inline";
		}
	}
	
	this.hide = function() 
	{
		var formObj = MM_findObj(this.id);
		
		if (this.method == "VISIBILITY") 
		{
			formObj.style.visibility = "hidden";
		}
		else
		{
			formObj.style.display = "none"; 
		}
	
	}
	
	//handles the showing of items on the BACK or RELOAD state
	if (container.on) 
	{
		this.show();
	}
}


/********** MIRRORS **************/

function FC_Mirror(id, defaultState) 
{
	this.id = id;
	
	
	if (defaultState != null) 
	{
		this.on = defaultState;
	}
	else
	{
		this.on = false;
	}
	 
	this.children = new Array();
	this.cCount = 0;
	
	this.addChild = function(child) 
	{
		this.children[this.cCount++] = child;
	}
	
	this.toggle = function() 
	{
		for (var i = 0; i < this.children.length; i++) 
		{	
			if (!this.on) 
			{
				this.children[i].doMirror(); 
			}
			else
			{
				this.children[i].clear();
			}		
		}
		
		this.on = !this.on;
	}
	
	this.turnOn = function() 
	{
	   this.on = false; 
	   this.toggle();
	}
	
	this.turnOff = function() 
	{
	    this.on = true;
	    this.toggle();
	}
}

/**
 *
 * @param original (FC_FormItem | FC_FormList)
 * @param mirror (FC_FormItem | FC_FormList)
 */
function FC_MirrorItem(formItemOrig, formItemMirr, mirror) 
{
	this.originalItem = formItemOrig;
	
	this.mirrorItem = formItemMirr; 
	
	mirror.addChild(this);
		
	//deemed inappropriate...
	//this.oneWay = oneWay; 
	
	this.doMirror = function()
	{
		this.mirrorItem.mirror(this.originalItem);
	}
	
	this.clear = function()
	{
		this.mirrorItem.clear();
	}
	

	if (mirror.on) 
	{
		this.doMirror();
	}
	else
	{
		
	}
	
} 







