// CRS_Utils Utility Object
// To use, instantiate one of these and call its functions...

function CRS_Utils(sharedUrl, helpUrl)
{
	this.suppressAllAlerts = false;
	this.sharedUrl  = sharedUrl;
	this.helpUrl    = helpUrl;
	this.selectBoxDefaultText = "- Choose -";
	this.plusArrow  = null; // must be initialized by caller
	this.minusArrow = null; // must be initialized by caller
}

// Set your HTML body's onload attribute to this function! It performs essential CRS_Utils initialization
// and then calls your own doOnLoad function (if defined) where you may perform any additional
// initialization.
CRS_Utils.prototype.doOnLoad = function(child)
{
	dtCh = '/'; // date validation
	//	imgDir = "/mycrs-shared/images/popcalendar/";	// TODO: do this here when popcalendar.js isn't so lame!!
	init(); // popupcalendar

	if ((child != null) && (child.doOnLoad != null))
	{
		child.doOnLoad();
	}
	else
	{
		doOnLoad();
	}
}

// Utility wrapper for getElementById. Throws exception if element not found.
// Also, by default, displays alert unless suppressAlert parameter is set or
// global gSuppressAllAlerts is set.
CRS_Utils.prototype.getRequiredElement = function(id, suppressAlert)
{
	var element = document.getElementById(id);
	
	if (element == null)
	{
		var msg = "Required element missing! id=[" + id + "]";
		
		if (!this.suppressAllAlerts && ((suppressAlert == null) || !suppressAlert))
		{
			alert(msg);
		}

		throw new Error(msg);
	}
	
	return element;
}

// Turns a series of checkboxes on or off.
// Items must have id's of the form [idStem]1, [idStem]2, etc.
CRS_Utils.prototype.multiCheckboxToggle = function(idStem, numItems, checkedVal)
{
    for (var i = 1; i <= numItems; i++)
    {
	    var element = CRS_Utils.prototype.getRequiredElement(idStem + i);
	    
	    element.checked = checkedVal; 
    }
}

// Wraps call to popupcalendar.js
CRS_Utils.prototype.doCalendar = function(calWidget, elementId, dateFormat)
{
	dateFormat = (dateFormat != null) ? dateFormat : 'mm/dd/yyyy';
	
	popUpCalendar(calWidget, this.getRequiredElement(elementId), dateFormat);
}


CRS_Utils.prototype.toggleElementVisibilty = function(elementID,forceVisible)
{
	var element = crs.getRequiredElement(elementID);	
	var hidden = (forceVisible == null) ? (element.style.display == "none") : forceVisible;
	
	element.style.display = hidden ? "block" : "none";
	
	var image = document.getElementById(elementID + "-img");
	
	if (image != null)
	{
		image.src = hidden ? this.minusArrow : this.plusArrow;
	}
}

// Open Help window.
//   - if editable=true user will be able to edit help text, and even create if entryId does not exist
//   - categoryId is optional, and is used simply to set suggested category when entryId does not exist
//   - editable flag is obviously very hackable. TODO: Fix?
CRS_Utils.prototype.showHelp = function(entryId, editable, categoryId)
{
	if (categoryId == "") categoryId = -1;
	
	var destURL = this.helpUrl + "/edit?helpId=" + entryId + "&editable=" + editable + "&categoryId=" + categoryId;
	window.open(destURL,"","height=600,width=640,scrollbars=yes,resizable=yes");
}

	
CRS_Utils.prototype.showHelpCategory = function(categoryId,editable)
{
	var destURL = this.helpUrl + "/editCategory?categoryId=" + categoryId + "&editable=" + editable;
	window.open(destURL,"","height=400,width=440,scrollbars=yes,resizable=yes");
}


/*
	Dynamic Lists (e.g. Countries, States, etc.)
	
	- Take data supplied in page "model" and populate Javascript data structures to support dynamic browser-side behavior:
		-	Populate select boxes that can be "chained" (e.g. selecting a country changes the list of states)
		-	Handle display of existing data by selecting items appropriately
		-	Play nice with validation scheme
		-	Allow for the same data to be used in multiple places on the page (e.g. country and state lists
			that appear in both a business and shipping address).
		-	Allow for the hooking up of these lists to also be somewhat dynamic. That is, technique should
			not be overly dependent on knowing the entire form layout in advance (e.g. support DataFlexer schema
			forms).
		
	TBD:
		-	What if existing data no longer corresponds to available choices?
		-	Can we support input fields where data can be entered directly, but that use our (possibly chained)
			select boxes as "helpers"?
		
	IMPLEMENTATION NOTES:
		-	Since select boxes can be dependent on other choices, their population must orchestrated after the
			page loads via Javascript. The role of the FreeMarker code is to set up the HTML elements and generate
			Javascript that populates whatever data structures may be needed.
		-	The needed data structures are:
			-	Arrays for each of the lists, containing the code and name used to populate select boxes. If an
				array is dependent, it must be "addressable" by the code of the array it depends on.
			-	Something to capture the meta-information about the lists, e.g. their dependencies and which
				form fields are tied to them.
*/
CRS_Utils.prototype.populateSelect = function(selectBox,optList,value)
{
	selectBox.options.length = 0;
	var companion = document.getElementById(selectBox.id + "_buddy");
	var companionVisible = (companion != null) && (companion.style.display != "none");
	 
	if (optList == null)
	{
		if ((companion != null) && !companionVisible)
		{
			companion.style.display = "block";
			selectBox.style.display = "none";
			companion.name = selectBox.id;
			selectBox.name = selectBox.id + "_buddy";
		}
	}
	else if (optList.length > 0)
	{
		if (companion != null)
		{
			companion.value = "";

			if (companionVisible)
			{
				companion.style.display = "none";
				selectBox.style.display = "block";
				companion.name = selectBox.id + "_buddy";
				selectBox.name = selectBox.id;
			}
		}
		
		selectBox.options[0] = new Option(this.selectBoxDefaultText,"");
		
		for (var i = 0; i < optList.length; i++)
		{
			var item = optList[i];
			
			selectBox.options[i + 1] = new Option(item.name,item.code);
		}
	}
	
	if (value != null)
	{
		selectBox.value = value;
		
		if (selectBox.value != value)
		{
			alert('CRS_Utils.populateSelect: Couldn\'t set field: ' + selectBox.name + ' to: ' + value);
		}
		else
		{
			selectBox.onchange();
		}
	}
}


CRS_Utils.prototype.dynamicSelect = function(selectBox,page)
{
	if ((page != null) && !page.validationInProgress)
	{
		var dynamicList = page.dynamicLists[selectBox.id];

		if ((dynamicList != null) && (dynamicList.target != null))
		{
			var targetList = page.dynamicLists[dynamicList.target];
			var target = this.getRequiredElement(targetList.fieldId);
			var targetOpts = (selectBox.value.length > 0) ? targetList.list[selectBox.value] : null;

			if ((targetOpts != null) && (targetOpts.length == 0))
			{
				targetOpts = null;	// TODO: FIX: distinguish between null and empty?
			}
			
			this.populateSelect(target,targetOpts,null);
		}
	}
	
	return this.alwaysValid(selectBox);
}

/*
	Populate dynamic lists, including chained select boxes. 
	NOTE: dependent lists must come after their dependecies, because this
	"triggers" each point in the chain.
*/
CRS_Utils.prototype.initDynamicUILists = function(page)
{
	for (var i = 0; i < page.dynamicLists.length; i++)
	{
		var item = page.dynamicLists[i];
		
		if (item.fieldId != null)
		{
			page.dynamicLists[item.fieldId] = item;
			
			var field = this.getRequiredElement(item.fieldId);

			if (!item.useSelector)
			{
				this.populateSelect(field,item.list,item.value);
			}
			else
			{
				field.value = item.value;
			}
		}
	}
}

/*
	Caller should populate a data structure identifying the form fields to be validated,
	as follows:
		fieldAttributes = [ { id:"blahblah", required:true, valid:false }, ... ];
		
	Fields with validation rules beyond simple required/optional should have an
	onchange function. In addition to performing immediate validation, this function
	will be called during batch validation below. It should call CRS_Utils.hiliteField(valid,message)
	for the field (or fields) in question.
	
	A number of generic onchange functions are provided below.
*/

/*
	link up field attributes w/dynamic lists
*/
CRS_Utils.prototype.linkFieldAttributes = function(page)
{
	for (var i = 0; i < page.fieldAttributes.length; i++)
	{
		var item = page.fieldAttributes[i];
		
		if (item.dynamicList != null)
		{
			page.dynamicLists[item.dynamicList].fieldId = item.id;
			page.dynamicLists[item.dynamicList].value = item.listValue;
		}
	}
}

CRS_Utils.prototype.clearForm = function(page)
{
	for (var i = 0; i < page.fieldAttributes.length; i++)
	{
		var attrs = page.fieldAttributes[i];
		var field = this.getRequiredElement(attrs.id);
		
		field.value = "";
	}
}

CRS_Utils.prototype.validateForm = function(page)
{
	page.validationInProgress = true;

	var valid = true;
		
	try
	{
		var fieldAttributes = page.fieldAttributes;
		
		for (var i = 0; i < fieldAttributes.length; i++)
		{
			var attrs = fieldAttributes[i];
			var field = this.getRequiredElement(attrs.id);
			
	//		alert('field: ' + field.name);
			if (attrs.required && (field.value.length == 0) && (field.style.display != "none"))
			{
				attrs.valid = false;
			}
			else
			{
				attrs.valid = (field.onchange != null) ? field.onchange() : true;
			}
	
			valid = valid && attrs.valid;
	//		alert('field: ' + field.name + ' valid: ' + attrs.valid + ' overall: ' + valid);
		}
		
		valid = valid && page.doExtraValidation();
		
		if (!valid)
		{
			this.hiliteInvalidFields(fieldAttributes);
			alert("Please correct form errors...");
		}
	}
	finally
	{
		page.validationInProgress = false;
	}
	
	return valid;
}

CRS_Utils.prototype.hiliteInvalidFields = function(fieldAttributes)
{
	for (var i = 0; i < fieldAttributes.length; i++)
	{
		var attrs = fieldAttributes[i];
		
		this.hiliteField(this.getRequiredElement(attrs.id),attrs.valid,null);
	}
}

/* 	TODO: handle the various types of input fields differently/explicitly.
	For example, the backgroundColor works for select boxes on Firefox and IE, but
	not Safari.
*/ 
CRS_Utils.prototype.hiliteField = function(field,valid,message)
{
	field.style.backgroundColor = (valid ? "white" : "red");
	
	if (message != null) // caller wants title to refer to field's validity
	{
		field.title = valid ? null : message;
	}
}


CRS_Utils.prototype.alwaysValid = function(field)
{
	this.hiliteField(field,true);
	
	return true;
}


CRS_Utils.prototype.isAlphaNumeric = function(field)
{
	var valid = true;
	var str = field.value.toString();
	var numberMatch = /^\w*$/;	

	valid = str.match(numberMatch) != null;

	this.hiliteField(field,valid,"Must be alpha-numeric!");
	
	return valid;
}


CRS_Utils.prototype.isNumber = function(field)
{
	var valid = true;
	var str = field.value.toString();
	var numberMatch = /^[-]?\d*\.?\d*$/;	

	valid = str.match(numberMatch) != null;

	this.hiliteField(field,valid,"Must be numeric!");
	
	return valid;
}
