//class to represent each panel
function daffControl_Panel(Panel, Exclude, Dirty)
{
	var elements;						//all the elements we will be checking
	var isDirty;						//will be used to store if we are currently dirty
	
	this.panelId       = Panel;			//stores the id of the panel (allows us to use getElementById)
	this.exclusionList = Exclude;		//the list of ids that will be exluded from checks (parent or children)
	this.startedDirty  = Dirty;			//whether the panel started dirty, in which case we don't need to check it
	
	var changedEvent;					//we will fake events by using the onclick of a hidden anchor tag
	var revertedEvent;					//see http://www.truerwords.net/articles/web-tech/custom_events.html for longterm (should implement more of it)
	
	
	this.checkForChanges = function(e)
	{
		if (e)	//this is of no use to us but a different project wants to know what the last control that was clicked/keyupped in the panel (regardless of if it was dirty)
		{
			var target = daffControl_EventTarget(e);
			if (target)
				this.eventSender = target.id;
		}
			
//		if (this.startedDirty)
//			return true;
			
		if (!this.elements)				//if we haven't loaded settings yet do so now
		{
			this.elements = daffControl_LoadElements(this.panelId, this.exclusionList);	//if this is the first time then load our elements to check
			this.isDirty = document.getElementById(this.panelId + '_isDirty');			//hidden variable for if we are currently dirty (allows us to give info to server)
			this.changedEvent = document.getElementById(this.panelId + 'OnChange');
			this.revertedEvent = document.getElementById(this.panelId + 'OnRevert');
		}
		
		var dirty = daffControl_CheckAll(this.elements);	//check the elements
		if (dirty && this.isDirty.value == 'false')			//if is NOW dirty and WAS clean then fire dirty event
		{
			this.isDirty.value = dirty ? 'true' : 'false';
			
			if (this.changedEvent && this.changedEvent.click)
				this.changedEvent.click();
		}
		else if (!dirty && this.isDirty.value == 'true')	//if is NOW clean and WAS dirty then fire clean event
		{
			this.isDirty.value = dirty ? 'true' : 'false';
			
			if (this.revertedEvent && this.revertedEvent.click)
				this.revertedEvent.click();
		}
		
		return true;										//always return true (ie DO NOT CANCEL event)
	}
	
	this.resetDefaultValues = function(e)
	{
		if (this.isDirty)
			daffControl_ResetAll(this.elements);				//reset all elements to their default values
			
		this.checkForChanges();
	}
		
	this.hasChanged = function(e)
	{
		if (this.startedDirty)
			return true;
		else if (this.isDirty)
			return this.isDirty.value == 'true' ? true : false;
		else
			return false;
	}
	
	this.updateChanged = function()							//used solely when updating changes before submission (to check on server)
	{
		if (!this.startedDirty)
		{
			if (!this.elements)				//if we haven't loaded settings yet do so now
			{
				this.elements = daffControl_LoadElements(this.panelId, this.exclusionList);	//if this is the first time then load our elements to check
				this.isDirty = document.getElementById(this.panelId + '_isDirty');			//hidden variable for if we are currently dirty (allows us to give info to server)
				this.changedEvent = document.getElementById(this.panelId + 'OnChange');
				this.revertedEvent = document.getElementById(this.panelId + 'OnRevert');
			}
			
			var dirty = daffControl_CheckAll(this.elements);	//check the elements
			this.isDirty.value = dirty ? 'true' : 'false';	//update hidden field
		}
		
		return true;						//always return true (ie DO NOT CANCEL SUBMIT)
	}
}

//TODO: move all the other misc functions relating to panel into the prototype instead of external
daffControl_Panel.prototype = {	
	configurePanel : function()
	{
		daffControl_AddSubmitEvent(document.forms[0], daffControl_BindFunction(this.updateChanged, this));
		daffControl_AddEvent(document.body, 'click', daffControl_BindFunction(this.checkForChanges, this));		//in case of dropdownlist that extends outside of panel
		
	}
}

//Adds all the elements we are going to monitor to our list
function daffControl_LoadElements(panelId, exclusions)
{
	var excluded = daffControl_LoadExclusions(exclusions);
		
	return daffControl_GetChildElements(document.getElementById(panelId), excluded);
}

//Takes a list of ids and adds them (or their children) to the exclusions list
function daffControl_LoadExclusions(list)
{
	if (!list)
		return;
		
	var found = new Array();
	var excluded = list.split(',');
	for(var i = 0, item; item = excluded[i]; i++)
	{
		var parent = document.getElementById(item);
		if (parent)
		{
			if (parent.tagName.match(/^input|select|textarea$/i))			//if the excluded element is an input add it
				found.push(parent);
			else															//otherwise exclude all its child inputs
				found = found.concat(daffControl_GetChildElements(parent));
		}
	}
	
	var exclusions = {};
	for (var i = 0, item; item = found[i]; i++)		//turn our array of exlucions into a hashtable (useful for using 'in' later)
	{
		if (item.id)
			exclusions[item.id] = '';
	}
	
	return exclusions;
}

//Gets all the child elements that can be tracked for changes
function daffControl_GetChildElements(parent, excluded)
{
	if (!parent)
		return null;

	var children = new Array();
	
	//get all input elements (textbox, hiddenfield, checkbox, radiobutton)
	var matching = parent.getElementsByTagName('input');
	for (var i = 0, item; item = matching[i]; i++)
	{
		if (excluded && (item.id in excluded))		//add the item if it isn't in the exclusions list
			continue;
			
		children.push(item);
	}
	
	//get all select elements (dropdownlist, listbox)
	matching = parent.getElementsByTagName('select');
	for (var i = 0, item; item = matching[i]; i++)
	{
		if (excluded && (item.id in excluded))		//add the item if it isn't in the exclusions list
			continue;
			
		children.push(item);
	}
	
	//get all textarea alements
	matching = parent.getElementsByTagName('textarea');
	for (var i = 0, item; item = matching[i]; i++)
	{
		if (excluded && (item.id in excluded))		//add the item if it isn't in the exclusions list
			continue;
			
		children.push(item);
	}
	
	return children;
}

//Checks all the elements in the list to see if any have changed
function daffControl_CheckAll(elements)
{	
	if (!elements)
		return false;
		
	var changed = false;

	for (var i = 0, element; element = elements[i]; i++)
	{
		if (changed |= daffControl_HasChanged(element))	//only loop until we find something that has changed
			break;
	}
	
	return changed;
}
//Resets all the elements in the list to their starting values
function daffControl_ResetAll(elements)
{	
	if (!elements)
		return;
	
	for (var i = 0, element; element = elements[i]; i++)
	{
		var inspector = inspectors[element.type];
		if (inspector)
			inspector.reset(element);		//reset the element
	}
}

/*Applies validation to field*/
function daffControl_HasChanged(element)
{
	try
	{
		if (element)
		{
			var inspector = inspectors[element.type];
			if (inspector)
				return inspector.check(element);
		}
		
		return false;
	}
	catch(ex){}	//there shouldn't be any exceptions but if they are we def don't care about them
}

/*Hashtable of objects containing further hashtables of objects that will perform the validation*/
var inspectors = {
    'text':{   
		check: function(element)
			{return daffControl_CheckTextControl(element);},
		reset: function(element)
			{return daffControl_ResetTextControl(element);}
    },
    'textarea':{   
		check: function(element)
			{return daffControl_CheckTextControl(element);},
		reset: function(element)
			{return daffControl_ResetTextControl(element);}
    },
    'hidden':{				
		check: function(element)
			{return false;},		//stupid telerik - changes hidden values even when nothing really changed... don't really need anyway
		reset: function(element)
			{return daffControl_ResetTextControl(element);}
    },	
    'checkbox':{   
		check: function(element)
			{return daffControl_CheckCheckBoxControl(element);},
		reset: function(element)
			{return daffControl_ResetCheckBoxControl(element);}
    },
    'radio':{   
		check: function(element)
			{return daffControl_CheckCheckBoxControl(element);},
		reset: function(element)
			{return daffControl_ResetCheckBoxControl(element);}
    },
    'select-one':{   
		check: function(element)
			{return daffControl_CheckListControl(element);},
		reset: function(element)
			{return daffControl_ResetListControl(element);}
    },
    'select-multiple':{   
		check: function(element)
			{return daffControl_CheckListControl(element);},
		reset: function(element)
			{return daffControl_ResetListControl(element);}
    }
   
};

function daffControl_CheckTextControl(element)
{
	return element.value != element.defaultValue;
}
function daffControl_ResetTextControl(element)
{
    //alert(element.defaultValue);
	element.value = element.defaultValue;
}

function daffControl_CheckCheckBoxControl(element)
{
	return element.checked != element.defaultChecked;
}
function daffControl_ResetCheckBoxControl(element)
{
	element.checked = element.defaultChecked;
}

function daffControl_CheckListControl(element)
{
	var hasDefault = false;		//use this to determine if the list has a default option
	
	if (element.size > 0)		//ie listbox not dropdown (can have nothing selected)
		hasDefault = true;
	else
	{
		for(var i = 0, option; option = element.options[i]; i++)
		{
			if(hasDefault |= option.defaultSelected)
				break;
		}
	}
	
	var i = hasDefault == true ? 0 : 1;	//if has a default then check all items, if does not have a default then first item will be selected so don't bother checking it
	while (option = element.options[i++])
	{
		if (option.selected != option.defaultSelected)
			return true;
	}
	
	return false;
}
function daffControl_ResetListControl(element)
{
	var hasDefault = false;		//use this to determine if the list has a default option
	
	if (element.size > 0)		//ie listbox not dropdown (can have nothing selected)
		hasDefault = true;
	else
	{
		for(var i = 0, option; option = element.options[i]; i++)
		{
			if(hasDefault |= option.defaultSelected)
				break;
		}
	}
	
	for (var i = 0, option; option = element.options[i]; i++)
	{
		if (!hasDefault && i == 0)
			option.selected = true;
		else	
			option.selected = option.defaultSelected;
	}
	
	return false;
}