/* default empty callback function called by file attachment window's onload event */
function AF_fileAttachmentCallback(pformId, pfieldName) {
}
var AF_popupAttachmentPickerWindow = null;

var mvSeparator = ":";
// called by form html when a listbox field is set to an inconisten value
function AF_signalDataInconsistency(pformId, pfieldName) {
	var myform = document.getElementById(pformId);
	var myfield = myform.elements[pfieldName];
	
	try {
		var mycell = myfield.parentNode.parentNode;
		mycell.className = 'autoform-cell-inconsistent-data';
	} catch (e) {
		alert('A list box field value is inconsistent with the list of available options');
	}
}

function AF_selectDocument(pdocumentKey, myfn, myie) {
	if (( myfn != '') && (myie != '')) {
		// a document is selected from a view to update a doc attachment field
		window.opener.AF_setFieldValue(myfn, myie, pdocumentKey);
		self.close();
	} 
}

function AF_popupUrl(url, windowName, winWidth, winHeight, popupProps, returnHandler) {

	if(winWidth == null || winWidth == "null")
		winWidth = 800;
	if(winHeight == null || winHeight == "null")
		winHeight = 700;
	if(popupProps == null || popupProps == "null")
		popupProps = "scrollbars=yes,status=yes,resizable=yes";
		
	popupProps = "width=" + winWidth + ",height=" + winHeight + "," + popupProps;
		
	AF_popupAttachmentPickerWindow = window.open(url, windowName , popupProps + ',top=' + ((window.screen.availHeight - 480) / 2) + ',left=' + ((window.screen.availWidth - 700) / 2));
	if ( AF_popupAttachmentPickerWindow != null) {
		if( AF_popupAttachmentPickerWindow.opener == null) {
			AF_popupAttachmentPickerWindow.opener = self;
		}
	}
	AF_popupAttachmentPickerWindow.focus();
	
	if (returnHandler) {
		return AF_popupAttachmentPickerWindow;
	}
}

// function called by views checkbox to select/deselect
// all forms in view
function AF_selectDeselectAll(state)
{
	chbs = document.getElementsByTagName("INPUT");
	for(var i=0; i<chbs.length; i++)
	{
		if(chbs[i].id != "seldesel")
		if(chbs[i].getAttribute("type").toLowerCase() == "checkbox")
		chbs[i].checked = state;
	}

}
function AF_selectOne(state){
	var inputs = document.getElementsByTagName("INPUT");
	var selectAll = document.getElementById("seldesel");
	if (state == false){
		selectAll.checked = state;
		return;
	}else{
		for ( var i = 0 ; i < inputs.length ; i++ ){
			if(inputs[i] != null ){
				if(inputs[i].id != "seldesel"){
					if(inputs[i].getAttribute("type").toLowerCase() == "checkbox"){
						if (!inputs[i].checked){
							return;
						}
					}
				}	
			}	
		}
	selectAll.checked = true;	
	}
}

// tested on ie and firefox
// called when a user clicks a tab in a section that contains <page> elements
// this function is immune to "cross-talk" because the psectionid param is garanteed unique
var AF_PageSection_Id = -1;
var AF_Page_Index = -1;
function AF_showTabPage(psectionid, pindex) {
	AF_PageSection_Id = psectionid;
	AF_Page_Index = pindex;

	// no more than 100 tabs !!
	for (var i=1; i <= 100; i++) {
		var tab = document.getElementById('autoforms-' + psectionid + '-tab_' + i);
		var tabpage = document.getElementById('autoforms-' + psectionid + '-tabpage_' + i);
		if (tab == null || tabpage == null) break;
		
		if (i == pindex) {
			tab.className = 'autoform-tab-active';
			tabpage.className = 'autoform-tab-page-visible';
		} else {
			tab.className = 'autoform-tab-inactive';
			tabpage.className = 'autoform-tab-page-hidden';
		}
	}
	document.getElementsByTagName("table")[0].focus(); // loose the focus of the last element; resolves issue with fckeditors in tabs 
}

// tested on ie and firefox
// called by a submit button onclick event handlers when the button is not a true submit
// this function is immune to "cross-talk" because the pform param is garanteed unique
function AF_fireEvent(form, peventValue, pmustValidate) {
	form.elements['AF_autoformsEvent'].value = peventValue;
	
	if(pmustValidate == null)
	pmustValidate = eval("AF_"+form.id+"_"+peventValue+"_Validate");
	
	// try - catch needed in case the array AF_HtmlAreas is not defined
	// which will be the case if no html editors are defined in the form
	try {
		for (var i = 0; i < AF_HtmlAreas.length; i++) {
			AF_HtmlAreas[i]._textArea.value = AF_HtmlAreas[i].getHTMLNonEmpty();
		}
	} catch (e) {}
	
	if(pmustValidate)
		return AF_autoformSubmit(form);
	else
		return AF_checkFieldTypesAndSubmit(form);
}

// tested on ie
// called from po renditions on load, shows an img element linked to an attachment if the attachment is an image (bmp, jpg, jpeg, etc...)
// potential cross-talk here
function AF_manageFileAttachments(pattachmentId) {
	var myimg = document.getElementById(pattachmentId).alt.toLowerCase();
	
	if (!(myimg.substr(myimg.length - 4, 4) == '.jpg' || myimg.substr(myimg.length - 5, 5) == '.jpeg' || myimg.substr(myimg.length - 4, 4) == '.gif' || myimg.substr(myimg.length - 4, 4) == '.bmp' || myimg.substr(myimg.length - 5, 5) == '.tiff')) {
		document.getElementById('td' + pattachmentId).removeNode(true);
	}
}


// tested on ie and firefox
// called from po renditions on load, replaces select elements (list boxes) with the displayed caption
// this function is immune to "cross-talk" because the pformId param is garanteed unique
function AF_manageSelectElement(pformId, pelementName) {
	var myform = document.getElementById(pformId);
	var myselect = myform.elements[pelementName];
	myselect.parentNode.innerHTML = myselect.options[myselect.selectedIndex].innerHTML;
}


// called from onload, onchange, onpick, etc... this function manages a field's readonly/readwrite state and the corresponding formatting
// this function is immune to "cross-talk" because the pobject param is garanteed unique
function AF_setReadOnly(pobject, pstate) {
	pobject.readOnly = pstate;
	return 0;
}


// called from onload, onchange, onpick, etc... this function manages a field's disabled/enabled state and the corresponding formatting
// this function is immune to "cross-talk" because the pobject param is garanteed unique
function AF_setDisabled(pobject, pstate) {
	pobject.disabled = pstate;
	return 0;
}


// function used to trim strings
function AF_trim(pvalue) {
	return pvalue.replace(/^ +/, '').replace(/ +$/, '');
}

// returns a date object out of a dd/mm/yyyy date string
function AF_parseDate(pvalue) {
	var mytd = pvalue.substr(3, 2) + '/' + pvalue.substr(0, 2) + '/' + pvalue.substr(6);
	return new Date(mytd);
}

// tested on ie and firefox
// lookup callback function
// this function is immune to "cross-talk" because the formId param is garanteed unique
function AF_setFieldValue(pformId, pfieldName, pvalue) {
	document.getElementById(pformId).elements[pfieldName].value = pvalue;
}


// tested on ie and firefox
// lookup callback function (used by the file attachment window to set the text of the visible label)
// this function is immune to "cross-talk" because the plabelId param is garanteed unique
function AF_setLabelText(plabelId, ptext) {
	document.getElementById(plabelId).innerHTML = ptext;
}


// called from platform specific javascript functions
// returns the value of a param as it is in the query string
// no risk of "cross-talk" here
function AF_getParamValue(paramName) {
	var url = location.href
	paramIndex = url.indexOf(paramName);
	if(paramIndex < 0)
		return null;
	else {
		var ul = new String(url);
		for(i=paramIndex;i<ul.length;i++) {	
			cChar = ul.charAt(i);
            if(cChar == '&') break;	
		}
	return ul.substring(paramIndex+paramName.length+1,i);
	}
}

// called from platform specific javascript functions
// removes a param=value pair from a query string
// no risk of "cross-talk" here
function AF_removeParam(pparam, pqstring) {
	var myqs = pqstring + '&';
	var i1, i2;

	i1 = myqs.indexOf(pparam);
	while (i1 >= 0) {
		i2 = myqs.indexOf('&', i1 + 1);
		myqs = myqs.substr(0, i1) + myqs.substr(i2 + 1);
		
		i1 = myqs.indexOf(pparam);
	}
	
	if (myqs.charAt(myqs.length - 1) == '&') {
		myqs = myqs.substr(0, myqs.length - 1);
	}
	return myqs;
}





function AF_ShowInvisibleTabTab(pobj) {
	var isvisible = true;
	var current = pobj.parentNode;
	while (true) {
		try {
			if (current.tagName == 'FORM') break;
			current.className = current.className.replace(/^\s+|\s+$/g, '');
			// grab this item [tabcontent]
			if (current.className == "item" && current.parentNode.className == "content") {
				// find out the index so we can access the tabtab using the same index
				currentIndex = 0 ;
				for (x = 0 ; x < current.parentNode.childNodes.length ; x++) {
					if (current.parentNode.childNodes[x] == current) {
						currentIndex = x;
						break;
					}
				}
				// find the navigation tabs
				t = current.parentNode.parentNode.childNodes;
				for (i = 0 ; i < t.length ; i++) {
					if (t[i].className == "navigation") {
						// find the current items tabtab					
						current = t[i].childNodes[currentIndex];						
						break;		
					}
				}
				// select should be selecting the navigation tab not the content	
				AF_selectTab(current);
		 		isvisible = false;
		 		break;
		 	}
		} catch (e) {}
		current = current.parentNode;
	}
	return isvisible;	
}

// called from AF_autoformSubmit, returns whether the param field is contained in a visible tab page
// this function is immune to "cross-talk" because the pobj param is garanteed unique
function AF_isInVisibleTabPage(pobj) {
	var isvisible = true;
	var current = pobj.parentNode;
	while (true) {
		try {
			if (current.tagName == 'FORM') break;
			if (current.className == 'autoform-tab-page-hidden') {
				isvisible = false;
				break;
			}
		} catch (e) {}
		current = current.parentNode;
	}
	return isvisible;
}

// called from AF_autoformSubmit, returns the unique string used in the id of the tab page containing the param field
// this function is immune to "cross-talk" because the pobj param is garanteed unique
function AF_getSectionId(pobj) {
	var sectionId = '';
	var current = pobj.parentNode;
	while (true) {
		try {
			if (current.tagName == 'FORM') break;
			if (current.className == 'autoform-tab-page-hidden' || current.className == 'autoform-tab-page-visible') {
				var tpid = current.id;
				sectionId = tpid.substring(tpid.indexOf('-') + 1, tpid.lastIndexOf('-'));
				break;
			}
		} catch (e) {}
		current = current.parentNode;
	}
	return sectionId;
}

// called from AF_autoformSubmit, returns the index of the tab page containing the param field
// this function is immune to "cross-talk" because the pobj param is garanteed unique
function AF_getTabIndex(pobj) {
	var tabIndex = 1;
	var current = pobj.parentNode;
	while (true) {
		try {
			if (current.tagName == 'FORM') break;
			if (current.className == 'autoform-tab-page-hidden' || current.className == 'autoform-tab-page-visible') {
				var tpid = current.id;
				tabIndex = tpid.substring(tpid.lastIndexOf('_') + 1);
				break;
			}
		} catch (e) {}
		current = current.parentNode;
	}
	return tabIndex;
}
function AF_checkFieldTypesAndSubmit(form) {

	var field, value, description, validation, mandatory, message, constraint, language, systemvalue, addendum, pattern;
	var msgmandatory, msgnumeric, msgphone, msgdate, msgconstraint, msgconfirm;
	var evisible, esectionid, etabindex;
	var tvisible;	
	

	for( i = 0; (field = form.elements[i]) != null; i++){	
		
		if(field.tagName.toLowerCase() == 'fieldset')
			continue;

		var name = field.getAttribute('name');

		var type = field.getAttribute('type');

		description = field.getAttribute('validationmessage');
		//FHA : Description attribute does not exist anymore. it seems that it was replaced
		// 	    by title attribute but it kept this way for backward compatablitiy
		if(description == null || description == '') description = field.getAttribute("validationMessage");
		validation = field.getAttribute('validation');
		mandatory = field.getAttribute('mandatory');
		constraint = field.getAttribute('constraint');
		language = field.getAttribute('language');
		systemvalue = field.getAttribute('issystem');
		pattern = field.getAttribute('pattern');
		
		evisible = AF_isInVisibleTabPage(field);
		esectionid = AF_getSectionId(field);
		etabindex = AF_getTabIndex(field);
		
		//AF_ShowInvisibleTabTab(field);
		
		value = AF_trim(field.value);
		
		if (description == null) description = '';
		if (validation == null) validation = '';
		if (mandatory == null) mandatory = '';
		if (constraint == null) constraint = '';
		if (language == null) language = '';
		if (systemvalue == null) systemvalue = '';
		if (pattern == null) pattern = '';
		
		
		msgmandatory = 'Input is required for';
		msgdefault = 'A valid input is required for';
		msgnumeric = 'Numeric input is required for';
		msgemail = 'A valid email address input is required for';
		msgphone = 'Numeric input is required for';
		msgdate = 'A valid date input is required for';
		msgconstraint = 'Input is required for';
		msgconfirm = 'Do you want to submit this data?';
		if(field.disabled || field.hidden || field.readonly) continue;
		if (systemvalue == 'true') {
			addendum = '\\n' + 'Please update your user profile.';
		} else {
			addendum = '';
		}
		
		if (validation == '') {
		
			if (value != '' &&  !AF_regexMatch(value, pattern)){
				if (description == '') {
					description = msgdefault + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'N') {
			minvalue = field.getAttribute("minvalue");
			maxvalue = field.getAttribute("maxvalue");
			if (value != '' &&  !AF_isNum(value, minvalue, maxvalue, pattern)){  //isNaN(parseInt(value))) {
				if (description == '') {
					description = msgnumeric + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'P') {
			if (value != '' &&  !AF_isPhone(value, pattern)){
				if (description == '') {
					description = msgphone + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'E') {
			if (value != '' &&  !AF_isEmail(value, pattern)){
				if (description == '') {
					description = msgemail + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'D') {
			if (value != '' &&  !AF_isDate(field, pattern)) { 
				if (description == '') {
					description = msgdate + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
	}
	return true;
}


function AF_isTemplateField(fieldName) { 
	return (AF_regexMatch(fieldName, '^template'+mvSeparator) != null);
}
// not tested and not used yet
function AF_isMultiValueField(fieldName) { 
	return (AF_regexMatch(fieldName, mvSeparator + '[0-9]{1,}'+mvSeparator) != null);
}
function AF_autoformSubmit(form) {

	var field, value, description, validation, mandatory, message, constraint, language, systemvalue, addendum, pattern;
	var msgmandatory, msgnumeric, msgphone, msgdate, msgconstraint, msgconfirm;
	var evisible, esectionid, etabindex;
	var tvisisble;
	var elementsMap = [];
	var valuesMap = [];
	var mvFieldsNames = [];
	var numericsList = [];	
	

	for( i = 0; (field = form.elements[i]) != null; i++){	
		if(field.tagName.toLowerCase() == 'fieldset')
			continue;
		var name = field.getAttribute('name');
		if(AF_isTemplateField(name)){
			var fieldName = name.substr(9,name.length);
			var minCardinality = field.getAttribute("mincardinality");
			var maxCardinality = field.getAttribute("maxcardinality");			
			mvFieldsNames[mvFieldsNames.length] = fieldName;
			if(field.getAttribute('validation')!=null && field.getAttribute('validation') == 'N')
				numericsList[encodeFieldName(fieldName)]='isNumeric';
			continue;
		}

		var type = field.getAttribute('type');

//------------------set the count for mv fields.----------------------
			if(AF_regexMatch(name, mvSeparator) != null){
				var fieldName = name.substr(0, name.indexOf(mvSeparator));
				var currentCount = elementsMap[encodeFieldName(fieldName)];
				var values = valuesMap[encodeFieldName(fieldName)];
				if(values == null)
					values = new Array();
				if(currentCount == null)
					currentCount = 0;
				//first get the type
				if (type == 'text' || type == 'hidden'){
					//validating cardinality for all input of type text:numeric/text/date/email/phone
					if(field.value != ''){
						currentCount++;
						values[values.length] = field.value;
					}
				}
				if(type == 'checkbox'){
					if(field.checked){
						currentCount++;
						values[values.length] = field.value;
					}
				}
				if(type == 'radio') {
					if(field.checked){
						currentCount++;
						values[values.length] = field.value;
					}
				}
				if(field.tagName == 'SELECT'){
					if(field.value != ''){
						currentCount++;
						values[values.length] = field.value;
					}
				}
				elementsMap[encodeFieldName(fieldName)] = currentCount;
				valuesMap[encodeFieldName(fieldName)] = values;
			}			
//------------------end of count setting for mv fields------------------
		description = field.getAttribute('validationMessage');
		//FHA : Description attribute does not exist anymore. it seems that it was replaced
		// 	    by title attribute but it kept this way for backward compatablitiy
		if(description == null || description == '') description = field.getAttribute("title");
		validation = field.getAttribute('validation');
		mandatory = field.getAttribute('mandatory');
		constraint = field.getAttribute('constraint');
		language = field.getAttribute('language');
		systemvalue = field.getAttribute('issystem');
		pattern = field.getAttribute('pattern');
		
		evisible = AF_isInVisibleTabPage(field);
		esectionid = AF_getSectionId(field);
		etabindex = AF_getTabIndex(field);
		
		if (description == null) description = '';
		if (validation == null) validation = '';
		if (mandatory == null) mandatory = '';
		if (constraint == null) constraint = '';
		if (language == null) language = '';
		if (systemvalue == null) systemvalue = '';
		if (pattern == null) pattern = '';
		
		msgmandatory = 'Input is required for';
		msgdefault = 'A valid input is required for';
		msgnumeric = 'Numeric input is required for';
		msgemail = 'A valid email address input is required for';
		msgphone = 'Numeric input is required for';
		msgdate = 'A valid date input is required for';
		msgconstraint = 'Input is required for';
		msgconfirm = 'Do you want to submit this data?';

		if(field.disabled || field.hidden || field.readonly) continue;
		if (systemvalue == 'true') {
			addendum = '\\n' + 'Please update your user profile.';
		} else {
			addendum = '';
		}

		if(field.tagName == 'SELECT'){

			//iterate on options to see how many are selected
			total = 0;
			for (var loop=0; loop < field.options.length; loop++){
				if(field.options[loop].selected && field.options[loop].value != '')
					total++;
			}
			var minCardinality = field.getAttribute('mincardinality');
			var maxCardinality = field.getAttribute('maxcardinality');
			var mandatory = field.getAttribute('mandatory');
			var validationMessage = field.getAttribute('validationMessage');
			//the mandatory field is set for the non-multi-valued list boxes
			if(mandatory == 'true') {
				if(total != 1){
					alert(validationMessage);				
					return false;
				}
			}
			else {
				if(maxCardinality < 0) {
					maxCardinality = total + 1;
				}
				
				if(!validCardinalityCheck(maxCardinality, minCardinality, total, name)) {
					return false;
				}
			}
		}
		
		value = "";
		// if field is a radio button, the value is the value of the checked radio
		if(field.type.toLowerCase() == "radio") {
			siblings = form.elements[field.name];
			for(var r=0; r<siblings.length; r++) {
				if(siblings[r].checked) {
					value = siblings[r].value;
					break;
				}
			}
		}
		else{
		// if this is an htmlarea
		var isFCKbodyEmpty = false;
  	    var fckBodyRange = "";
		if(field.getAttribute("type") == "htmlarea") {
			// FHA: update the value of the textarea before reading its value
			var editorFrame;
			if (window.frames[field.id + "___Frame"]) {
                editorFrame = window.frames[field.id + "___Frame"];
				if ((editorFrame.FCK != null) && (editorFrame.FCK.EditorDocument != null)) {
					editorFrame.FCK.UpdateLinkedField();
					//FHA: js code specific to IE ( does not work on gecko browsers)
  	            	//alert("createTextRange().text=["  +editorFrame.FCK.EditorDocument.body.createTextRange().text + "]" );
  	            	if(AF_trim(editorFrame.FCK.EditorDocument.body.createTextRange().text)=="")
            			isFCKbodyEmpty = true;
            	}
			} else {
				// Mozilla doesn't provide access via window.frames[field.id + "___Frame"]
				// so loop on location.href which also has field name
    			for (var j=0; j< window.frames.length; j++) {
    				if (window.frames[j].location.href.indexOf(field.id) >= 0) {
						editorFrame = window.frames[j];
						if ((editorFrame.FCK != null) && (editorFrame.FCK.EditorDocument != null)) {
							editorFrame.FCK.UpdateLinkedField();
							 //FHA: js code specific to gecko browsers ( does not work on IE)
							fckBodyRange = editorFrame.FCK.EditorDocument.createRange();
							fckBodyRange.selectNodeContents(editorFrame.FCK.EditorDocument.body); 
	  	                    //alert("fckBodyRange.toString()=[" + fckBodyRange.toString() + "]");
	  	                    if(AF_trim(fckBodyRange.toString())=="")
								isFCKbodyEmpty = true;
						}
						break;
    				}
				}
			}
			//FHA: in case of fckeditor, if the editor's body is empty then reset field's value and current value
			if(isFCKbodyEmpty) {
				field.value = "";
				value = "";
				} else {
					value = AF_trim(field.value);
				}
		}
		else {

			value = AF_trim(field.value);
		}
}	

		if (mandatory == 'yes' || mandatory == 'true') {
			// not checked checkbox
			mandEmpty = (field.tagName.toLowerCase() == "input") 
				 && (field.type == "checkbox") 
				 && (!field.checked);
			// empty input
			if(!mandEmpty) mandEmpty = (value == "");
			if (mandEmpty) {
				if (description == '') {
					description = msgmandatory + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}								
				
				AF_ShowInvisibleTabTab(field);
				
				alert(eval('"' + description + '"'));
				
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden') && (field.style.display != "none")) {
					field.focus();
				}
				return false;
			}
		}
		
		if (validation == '') {
			if (value != '' &&  !AF_regexMatch(value, pattern)){
				if (description == '') {
					description = msgdefault + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				
				AF_ShowInvisibleTabTab(field);
				
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'N') {
			minvalue = field.getAttribute("minvalue");
			maxvalue = field.getAttribute("maxvalue");
			if (value != '' &&  !AF_isNum(value, minvalue, maxvalue, pattern)){  //isNaN(parseInt(value))) {
				if (description == '') {
					description = msgnumeric + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				
				AF_ShowInvisibleTabTab(field);
				
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'P') {
			if (value != '' &&  !AF_isPhone(value, pattern)){
				if (description == '') {
					description = msgphone + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				
				AF_ShowInvisibleTabTab(field);
				
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'E') {
			if (value != '' &&  !AF_isEmail(value, pattern)){
				if (description == '') {
					description = msgemail + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				
				AF_ShowInvisibleTabTab(field);
				
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
		else
		if (validation == 'D') {
			if (value != '' &&  !AF_isDate(field, pattern)) { 
				if (description == '') {
					description = msgdate + '\\n' + field.getAttribute('name');
				}
				
				if (systemvalue == 'true') {
					description += addendum;
				}
				
				if (!evisible) {
					AF_showTabPage(esectionid, etabindex);
				}
				
				AF_ShowInvisibleTabTab(field);
				
				alert(eval('"' + description + '"'));
				if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
					field.focus();
				}
				return false;
			}
		}
	
		if (constraint != null && value != '') {
			if (constraint != '') {
				if (!eval(constraint)) {
					if (description == '') {
						description = msgconstraint + '\\n' + field.getAttribute('name');
					}
					
					if (systemvalue == 'true') {
						description += addendum;
					}
					
					if (!evisible) {
						AF_showTabPage(esectionid, etabindex);
					}
					
					AF_ShowInvisibleTabTab(field);
					
					alert(eval('"' + description + '"'));
					if (!field.getAttribute('disabled') && !field.getAttribute('readOnly') && (field.getAttribute('type') != 'hidden')) {
						field.focus();
					}
					return false;
				}
			}
		}
	}

//	alert('validating cardinality');
//	for each field we have a template for
	for (var f=0; f<mvFieldsNames.length; f++) {
		fieldName = mvFieldsNames[f];
		
		//get the number of non empty values we have for it
		var count = elementsMap[encodeFieldName(fieldName)];
		if(!count) count = 0;
		
		var minCardinality;
		var maxCardinality;

		var fieldNameEscaped = escapeFieldName(fieldName);
		
		//eval("minCardinality =  mvf"+fieldNameEscaped+"_"+form.id+".minCardinality")
		//eval("maxCardinality =  mvf"+fieldNameEscaped+"_"+form.id+".maxCardinality")
		//eval("allowDuplicate =  mvf"+fieldNameEscaped+"_"+form.id+".allowDuplicate")
		var mvField = form.elements['template'+mvSeparator + fieldName];
		// in case where this mv field is a multi radio box (within a fieldGroup) so we need to read these attributse from one of its childs
		if(mvField.length > 0 && mvField[0].getAttribute("type") == "radio") { 
    		minCardinality = mvField[0].getAttribute('minCardinality');
    		maxCardinality = mvField[0].getAttribute('maxCardinality');
    		allowDuplicate = mvField[0].getAttribute('allowDuplicate');
		} else {
		    minCardinality = mvField.getAttribute('minCardinality');
    		maxCardinality = mvField.getAttribute('maxCardinality');
    		allowDuplicate = mvField.getAttribute('allowDuplicate');
		}
		
		//alert(minCardinality);alert(maxCardinality);alert(allowDuplicate);
		
		
		msgMinCardinality = formatMessage('Should have at least {0} value(s) for {1}',minCardinality, fieldName)
		msgMaxCardinality = formatMessage('Can not add more than {0} value(s) for {1}',maxCardinality, fieldName)
		msgDuplicateViolation = formatMessage('Duplicated values are not allowed for {0}', fieldName);
		
		if(maxCardinality < 0){
			maxCardinality = count + 1;
		}

		if(minCardinality != null && count < minCardinality){
			alert(msgMinCardinality)
			return false;
		}
		if(maxCardinality != null && count > maxCardinality){
			alert(msgMaxCardinality)
			return false;
		}		

		if(allowDuplicate != null && !allowDuplicate){
			var values = valuesMap[encodeFieldName(fieldName)];
			if(values)
    			for( var x = 0 ; x < values.length; x++){
    				for(var y = 0 ; y < x ; y++){
    					if(numericsList[encodeFieldName(fieldName)] != null){
    						if(parseInt(values[x], 10) == parseInt(values[y], 10)){
    							alert(msgDuplicateViolation);
    							return false;
    						}	
    					}
    					if(values[x] == values[y]){
    						alert(msgDuplicateViolation);
    						return false;
    					}
    				}
    			}
		}
	}
	
	return true;
}
function validCardinalityCheck(maxCardinality, minCardinality, count, fieldName){
	
	// non data model field
	if (maxCardinality == null && minCardinality == null) return true;
		
	msgMinCardinality = formatMessage('Should have at least {0} value(s) for {1}',minCardinality, fieldName)
	msgMaxCardinality = formatMessage('Can not add more than {0} value(s) for {1}',maxCardinality, fieldName)

	if(maxCardinality < 0){
		maxCardinality = count + 1;
	}

	if(minCardinality != null && count < minCardinality){
		alert(msgMinCardinality);
		return false;
	}
	
	if(maxCardinality != null && count > maxCardinality){
		alert(msgMaxCardinality);
		return false;
	}
	return true;
}

// for multivalue fields, the names are escaped from '.' and '-' chars and used
// to construct js objects (MultiValueField and MultiValueElement)
function escapeFieldName(fieldName) {
	var fn = fieldName;

// For some wierd reason, the replace is not functioning as expected within the testing tool.
// Hence, we used the split and join functions to achieve the same result	
//	while(fn.indexOf(".") > -1) fn = fn.replace(".", "__dot__");
	fn = fn.split(".");
	fn = fn.join("__dot__"); 

//	while(fn.indexOf("-") > -1) fn = fn.replace(".", "__hyp__");
	fn = fn.split("-");
	fn = fn.join("__hyp__");
	
	return fn;
}

// validates against a regex
function AF_regexMatch(fieldValue, pattern) {
	if (pattern == '') return true;
	
	var myre = new RegExp(pattern);
	return myre.exec(fieldValue);
}


// validates the format of a Numeric
function AF_isNum(numField, minvalue, maxvalue, pattern) {
	
	// test first character
	var GoodChars = "-+1234567890.";
	if (GoodChars.indexOf(numField.charAt(0)) == -1) return false;
	
	if (numField.charAt(0) == ".") {
		// don't allow only "." 
		if(numField.length == 1) return false;
		GoodChars = "1234567890eE";
	}	
	else
	if (numField.charAt(0) == "+" || numField.charAt(0) == "-") {
		// don't allow only "+/-" 
		if(numField.length == 1) return false;
		GoodChars = "1234567890.eE";
	}
	else	
	GoodChars = "1234567890.eE";
	
	for (var i = 1; i <= numField.length -1; i++) {
		if (GoodChars.indexOf(numField.charAt(i)) == -1) return false;
		
		// don't repeat the (.) sign
		if (numField.charAt(i) == ".") GoodChars = "1234567890eE";
		
		if (numField.charAt(i) == "e" || numField.charAt(i) == "E") {
			// e|E is the last character of the number
			if (i == numField.length -1) return false;
			GoodChars = "+-1234567890";
			i++;
			if (GoodChars.indexOf(numField.charAt(i)) == -1) return false;
			GoodChars = "1234567890";
			while(++i <= numField.length -1)
			if (GoodChars.indexOf(numField.charAt(i)) == -1) return false;
		}
	}
	
	thisNum = new Number(numField);
	upper   = new Number(maxvalue);
	lower   = new Number(minvalue);
	
	if(thisNum < lower || thisNum > upper) return false;
	
	// new regex validation
	return AF_regexMatch(numField, pattern);
}


// validates the format of a phone/fax no.
function AF_isPhone(phoneField, pattern) {
	// old validation
	//var GoodChars = "+1234567890ext-()/. ";
	//for (var i = 0; i <= phoneField.length -1; i++) {
	//	if (GoodChars.indexOf(phoneField.charAt(i)) == -1) return false;
	//}
	
	// new regex validation
	var re_phone;
	if (pattern == '') {
		re_phone = /.*/;
	} else {
		re_phone = new RegExp(pattern);
	}
	return re_phone.exec(phoneField);
}


// validates the format of an email no.
function AF_isEmail(emailField, pattern) {
	// new regex validation
	var re_email;
	if (pattern == '') {
		re_email = /^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4})$/;
	} else {
		re_email = new RegExp(pattern);
	}
	return re_email.exec(emailField);
}


//validates the format of a date 
function AF_isDate(dateField, pattern) {
	var checkstr = "0123456789";
	var seperator = "/";
	
	var day = "";
	var month = "";
	var year = "";
	
	var iday = 0
	var imonth = 0;
	var iyear = 0;
	
	var leap = 0;
	var err = 0;
	
	var i;
	var dayIsFilled = false;
	var monthIsFilled = false;
	
	var DateValue = AF_trim(dateField.value);
	
	/* Delete all chars except 0..9 */
	for (i = 0; i < DateValue.length; i++) {
		if (checkstr.indexOf(DateValue.substr(i, 1)) >= 0) {
			if (!dayIsFilled) {
				day = day + DateValue.substr(i, 1);
				
			} else if (!monthIsFilled) {
				month = month + DateValue.substr(i, 1);
				
			} else {
				year = year + DateValue.substr(i, 1);
			}
			
		} 
		else 
		if(DateValue.charAt(i) != seperator) {
			return false;
		}
		else {
			if (dayIsFilled) monthIsFilled = true;
			dayIsFilled = true;
		}
	}
	/* Always change date to 8 digits - string*/
	/* only allow 1-2 day|month digits and 4 year digits */
	
	if(day.length < 1 || day.length > 2) return false;
	if(month.length < 1 || month.length > 2) return false;
	if(year.length != 4) return false;
	
	if (day.length == 1) {
		day = '0' + day;
	}
	
	if (month.length == 1) {
		month = '0' + month;
	}

	DateValue = day + month + year;
	
	if (DateValue.length != 8) {err = 19;}

	/* year is wrong if year = 0000 */
	year = DateValue.substr(4, 4);
	iyear = parseInt(year, 10);
	if (iyear == 0) {err = 20;}
	
	/* Validation of month*/
	month = DateValue.substr(2, 2);
	imonth = parseInt(month, 10);
	if ((imonth < 1) || (imonth > 12)) {err = 21;}

	/* Validation of day*/
	day = DateValue.substr(0, 2);
	iday = parseInt(day, 10);
	if ((iday < 1) || (iday > 31)) {err = 22;}

	/* Validation leap-year / february / day */
	if ((iyear % 4 == 0) || (iyear % 100 == 0) || (iyear % 400 == 0)) {leap = 1;}
	
	if ((imonth == 2) && (leap == 1) && (iday > 29)) {err = 23;}
	if ((imonth == 2) && (leap != 1) && (iday > 28)) {err = 24;}
	
	/* Validation of other months */
	if ((iday > 31) && ((month == "01") || (month == "03") || (month == "05") || (month == "07") || (month == "08") || (month == "10") || (month == "12"))) {
		err = 25;
	}

	if ((iday > 30) && ((month == "04") || (month == "06") || (month == "09") || (month == "11"))) {
		err = 26;
	}

	/* if 00 ist entered, no error, deleting the entry */
	if ((iday == 0) && (imonth == 0) && (iyear == 00)) {
		err = 1; day = ""; month = ""; year = ""; seperator = "";
	}

	/* if no error, write the completed date to Input-Field (e.g. 13/12/2001) */
	if (err == 0) {
		if(pattern != null && pattern != "") {
			re_date = new RegExp(pattern);
			if(re_date.exec(day + seperator + month + seperator + year)) {
				dateField.value = day + seperator + month + seperator + year;
				return true;			
			}
			else return false;
		}
		else {
			dateField.value = day + seperator + month + seperator + year;
			return true;
		}

	} else {
		return false;
	}
}


// Title: Timestamp picker
// Description: See the demo at url
// URL: http://us.geocities.com/tspicker/
// Script featured on: http://javascriptkit.com/script/script2/timestamp.shtml
// Version: 1.0
// Date: 12-05-2001 (mm-dd-yyyy)
// Author: Denis Gritcyuk <denis@softcomplex.com>; <tspicker@yahoo.com>
// Notes: Permission given to use this script in any kind of applications if
//    header lines are left unchanged. Feel free to contact the author
//    for feature requests and/or donations

function AF_showCalendar(formId, fieldName, selectDate) {
	var arr_months = [
		"January"
		, "February"
		, "March"
		, "April"
		, "May"
		, "June"
		, "July"
		, "August"
		, "September"
		, "October"
		, "November"
		, "December"
	];
	var week_days = [
		"Su"
		, "Mo"
		, "Tu"
		, "We"
		, "Th"
		, "Fr"
		, "Sa"
	];
	var n_weekstart = 1; // day week starts from (normally 0 or 1)
	
	var fieldObj = document.getElementById(formId).elements[fieldName];
	
	if (!AF_isDate(fieldObj)) {
		fieldObj.value = '';
	}
	str_datetime = fieldObj.value;
	
	var dt_datetime = new Date();
	if (selectDate!=null && selectDate!="") dt_datetime = AF_str2dt(selectDate);
	else if (str_datetime != null && str_datetime !="") dt_datetime = AF_str2dt(str_datetime);
	
	var dt_prev_month = new Date(dt_datetime);
	dt_prev_month.setMonth(dt_datetime.getMonth()-1);
	var dt_next_month = new Date(dt_datetime);
	dt_next_month.setMonth(dt_datetime.getMonth()+1);
	var dt_firstday = new Date(dt_datetime);
	dt_firstday.setDate(1);
	dt_firstday.setDate(1-(7+dt_firstday.getDay()-n_weekstart)%7);
	var dt_lastday = new Date(dt_next_month);
	dt_lastday.setDate(0);
	
	var dt_prev_year = new Date(dt_datetime);
	dt_prev_year.setFullYear(dt_datetime.getFullYear()-1);
	
	var dt_next_year = new Date(dt_datetime);
	dt_next_year.setFullYear(dt_datetime.getFullYear()+1);	
	
	// html generation (feel free to tune it for your particular application)
	// print calendar header
	var str_buffer = new String (
		"<html>\n"+
		"<head>\n"+
		"<title>Calendar</title>\n"+
		"</head>\n"+
		"<body bgcolor=\"White\">\n"+
		"<table class=\"clsOTable\" cellspacing=\"0\" border=\"0\" width=\"100%\">\n"+
		"<tr><td bgcolor=\"#4682B4\">\n"+
		"<table cellspacing=\"1\" cellpadding=\"3\" border=\"0\" width=\"100%\">\n"+
		"<tr>\n	<td bgcolor=\"#4682B4\">" + 
		"<font color=\"white\" face=\"tahoma, verdana\" size=\"2\"><a href=\"javascript:window.opener.AF_showCalendar('"+formId+"', '"+fieldName+"', '"+AF_dt2dtstr(dt_prev_month) +"');\"> < </a>"+
		"<a href=\"javascript:window.opener.AF_showCalendar('"+formId+"', '"+fieldName+"', '"+ AF_dt2dtstr(dt_prev_year)+"');\"> << </a></font>"+
		"</td>\n"+
		"<td bgcolor=\"#4682B4\" colspan=\"5\" align=\"center\">"+
		"<font color=\"white\" face=\"tahoma, verdana\" size=\"2\">"+
		arr_months[dt_datetime.getMonth()]+" "+dt_datetime.getFullYear()+"</font></td>\n"+
		"<td bgcolor=\"#4682B4\" align=\"right\">"+
		"<font color=\"white\" face=\"tahoma, verdana\" size=\"2\"><a href=\"javascript:window.opener.AF_showCalendar('"+formId+"', '"+fieldName+"', '"+AF_dt2dtstr(dt_next_month)+"');\"> > </a>"+
		"<a href=\"javascript:window.opener.AF_showCalendar('"+formId+"', '"+fieldName+"', '"+AF_dt2dtstr(dt_next_year)+"');\"> >> </a></font>"+
		"</td>\n</tr>\n"
	);

	var dt_current_day = new Date(dt_firstday);
	// print weekdays titles
	str_buffer += "<tr>\n";
	for (var n=0; n<7; n++)
		str_buffer += "	<td align=\"center\" width=\"14%\" bgcolor=\"#87CEFA\">"+
		"<font color=\"white\" face=\"tahoma, verdana\" size=\"2\">"+
		week_days[(n_weekstart+n)%7]+"</font></td>\n";
	// print calendar table
	str_buffer += "</tr>\n";
	while (dt_current_day.getMonth() == dt_datetime.getMonth() ||
		dt_current_day.getMonth() == dt_firstday.getMonth()) {
		// print row heder
		str_buffer += "<tr>\n";
		for (var n_current_wday=0; n_current_wday<7; n_current_wday++) {
				if (dt_current_day.getDate() == dt_datetime.getDate() &&
					dt_current_day.getMonth() == dt_datetime.getMonth())
					// print current date
					str_buffer += "	<td align=\"center\" width=\"14%\" bgcolor=\"#FFB6C1\" align=\"right\">";
				else if (dt_current_day.getDay() == 0 || dt_current_day.getDay() == 6)
					// weekend days
					str_buffer += "	<td align=\"center\" width=\"14%\" bgcolor=\"#DBEAF5\" align=\"right\">";
				else
					// print working days of current month
					str_buffer += "	<td align=\"center\" width=\"14%\" bgcolor=\"white\" align=\"right\">";

				if (dt_current_day.getMonth() == dt_datetime.getMonth())
					// print days of current month
					str_buffer += "<a href=\"javascript:window.opener.AF_setDateField('"+formId+"','"+fieldName+"','"+AF_dt2dtstr(dt_current_day)+"'); window.close();\">"+
					"<font color=\"black\" face=\"tahoma, verdana\" size=\"2\">";
				else 
					// print days of other months
					str_buffer += "<a href=\"javascript:window.opener.AF_setDateField('"+formId+"','"+fieldName+"','"+AF_dt2dtstr(dt_current_day)+"'); window.close();\">"+
					"<font color=\"gray\" face=\"tahoma, verdana\" size=\"2\">";
				str_buffer += dt_current_day.getDate()+"</font></a></td>\n";
				dt_current_day.setDate(dt_current_day.getDate()+1);
		}
		// print row footer
		str_buffer += "</tr>\n";
	}
	// print calendar footer
	str_buffer +="</table>\n" +
		"</tr>\n</td>\n</table>\n" +
		"</body>\n" +
		"</html>\n";

	var vWinCal = window.open("", "Calendar", "width=220,height=220,status=no,resizable=yes,top=200,left=200");
	vWinCal.opener = self;
	var calc_doc = vWinCal.document;
	calc_doc.write (str_buffer);
	calc_doc.close();
}


// datetime parsing and formatting routines. modify them if you wish other datetime format
function AF_str2dt (str_datetime) {
	var re_date = /^\s*(\d+)\/(\d+)\/(\d+)\s*$/;
	if (!re_date.exec(str_datetime)) return alert("Invalid Datetime format: " + str_datetime);
	return (new Date (RegExp.$3, RegExp.$2-1, RegExp.$1));
}

function AF_dt2dtstr (dt_datetime) {
	return (new String (
			dt_datetime.getDate()+"/"+(dt_datetime.getMonth()+1)+"/"+dt_datetime.getFullYear()));
}


// tested on ie and firebird
function AF_setDateField(formId, fieldName, d) {
	document.getElementById(formId).elements[fieldName].value = d;
}

/*
	Function will detetct if a URL is for an MS Office document and attempt to open the appropriate OLE
	Automation server on the client PC insetad of opening from the browser.  If the URL is not to an office
	URL, then the document will be opened normally by the browser.  Note that this only works with MSIE and
	that, to avoid security popups/messages the intranet/web site must be in the trusted sites zone and 
	permission to script ActiveX objects set to enable (not prompt as is the default).

	Ryan 2005/07/27
		
	Params:
		link - html <a> object

	Example:
		<a href="http://intranet/afdav/xyz.doc" onclick="return AF_detectMSOfficeLink(this)">Open</a>
*/
function AF_detectMSOfficeLink(link) {
			var linkhref = link.href;
			var ext = linkhref.substring(linkhref.lastIndexOf('.')+1).toLowerCase();
			var oleProgId = null;
			var openByObject = null;
			var canActivate = false;
			switch (ext) {
				case 'doc':
					oleProgId = 'Word.Application';
					openByObj = 'Documents';
					canActivate = true;
		                        break;
				case 'ppt':
					oleProgId = 'PowerPoint.Application';
					openByObj = 'Presentations';
					canActivate = true;
		                        break;
				case 'xls':
					oleProgId = 'Excel.Application';
					openByObj = 'Workbooks';
		                        break;
				case 'mpp':
					oleProgId = 'Project.Application';
					openByObj = 'Projects';	
		                        break;
			}	

			if (oleProgId != null) {
    			if(linkhref.indexOf('/autoforms/afdav/')) {
    				linkhref = linkhref.replace(/\/autoforms\/afdav/,'/afdav');
    			}
				try {
					var appObj = new ActiveXObject(oleProgId);
					if (appObj != null) {
                        appObj.visible=true;
						if (canActivate)
							appObj.Activate();
                        openByObj = eval('appObj.'+openByObj);
						openByObj.Open(linkhref);
						appObj.visible=true;
						
						return false;
					}
				} catch (e) {  
					if (appObj != null) {
						try {
							appObj.Quit();
						} catch (e) { /* Empty */ }
					}
					/* alert(oleProgId +' '+ e.message  ); */
				}
			}
		    return true;
		}


// expands an associative array to a string containing url parameters (starting with "&")
function AF_mapToUrlParameters(pRenderMap) {
	var s = "";
	for (key in pRenderMap) {
		s += "&" + key + "=" + pRenderMap[key];
	}
	return s;
}

// used in the pages functionality to select a given tab
// assumes a specific structure for the html code, which must be something like
// <div class="tabs">
//   <div class="navigation">
//     <div class="item selected" onclick="AF_selectTab(this)"><a href="#">Item one</a></div>
//     <div class="item" onclick="AF_selectTab(this)"><a href="#">Item two</a></div>
//   </div>
//   <div class="content">
//     <div class="item selected">Content one</div>
//     <div class="item">Content two</div>
//   </div>
// </div>
function AF_selectTab(ref) {

	var i, n, s, ss;

	// set the focus on the first table found.. this is dangerous business!
	try { document.getElementsByTagName("table")[0].focus(); } catch(e) {}
	
	// unselect all items in the navigation
	n = ref.parentNode.childNodes;
		for (i=0; i<n.length; i++) {
			try {
				if (n[i].className.indexOf("item")!=-1) n[i].className="item";
			} catch (e) {}
	}

	// select the given item
	ref.className='item selected';

	// find the position of the selected item in the navigation
	n = ref.parentNode.childNodes;
	s=0;
	for (i=0; i<n.length; i++) {
		try {
			if (n[i].className.indexOf("item selected")!=-1) break;
			if (n[i].className.indexOf("item")!=-1) s++;
		} catch (e) {}
	}

	// find the child with class=="content"
	n = ref.parentNode.parentNode.childNodes;
	for (i=0; i<n.length; i++) {
		try {
			if (n[i].className.indexOf("content")!=-1) {
				n = n[i].childNodes;
				break;
			}
		} catch (e) {}
	}
	
	// show the item with the selected position in the content and hide the others
	ss=0;
		for (i=0; i<n.length; i++) {
		try {
			if (n[i].className.indexOf("item")!=-1) {
				if (ss==s) {				
										n[i].className="item selected"; 	 					
				}
				else {
					n[i].className="item";
				}
				ss++;
			}
		} catch (e) {}
	}
	}
	



// -------------------- multi value -------------------
// used for listboxes
function validateMaxCardinality(obj){
	var total = 0;
	var newValues = new Array();
	var oldValues = new Array();
	var maxAccepted = obj.getAttribute('maxcardinality');
	var oldValuesString = null;

	oldValuesString = obj.getAttribute('oldvalues');
	oldValues = oldValuesString.split(',');

	for (loop=0; loop < obj.options.length; loop++){
		if(obj.options[loop].selected){
			newValues[loop] = true;
			total++;
		}else{
			newValues[loop] = false;
		}
	}	

	if(total > maxAccepted){
		for (loop=0; loop < oldValues.length; loop++){
			obj.options[loop].selected = (oldValues[loop] == "true");
			}
			alert('max exceeded');
	}else{
		obj.setAttribute('oldvalues', ''+newValues);
		oldValues = newValues;
	}
}

// used by the directory picker
// creates a 2 dimentional array based on valuesArray
// which is an array of selected options
function toArray(valuesArray){
	var returnArray = new Array();
	for(var i = 0 ; i < valuesArray.length; i++){
		var tempArray = new Array();
		tempArray[0] = valuesArray[i].value;
		tempArray[1] = valuesArray[i].text;
		returnArray[returnArray.length] = tempArray;
	}
	return returnArray;
}
/**
this method takes a parameterized message and replaces its parameters by values passed to this method
i.e. formatMessage("You are not allowed to give more than {0} values to {1}","5","fieldName") ->
	"You are not allowed to give more than 5 values to fieldName
*/
function formatMessage(message) {
	for(var i=0;i < arguments.length-1;i++) {
		message = message.replace('{' + i + '}',arguments[i+1]);
	}
	return message;
}

function AF_addEvent(obj, evType, fn, useCapture){
  if (obj.addEventListener){
    obj.addEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.attachEvent){
    var r = obj.attachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Error : Handler could not be attached");
  }
}

/////////////////////// Constrained list methods /////////////////////////////////////


// these vars will be common for all constrained list fields in this form since that
// there no meaning of opening multiple constrained list at the same time
// for now it will be 1 at a time.

var constrainedListWin = null;
var timedClosed = null;

function AF_popupConstrainedList(url, windowName, formId, fieldName, options) {
	var popupName		= options.popupName;	
	var popupHeight		= options.popupHeight;
	var popupWidth		= options.popupWidth;
	var popupProps		= options.popupProps;
	
	if (popupName == null || popupName == "null")
		popupName = "constrainedListWin";
	if (popupHeight == null || popupHeight == "null")
		popupHeight = 400;
	if (popupWidth == null || popupWidth == "null")
		popupWidth = 450;
	if (popupProps == null || popupProps == "null")
		popupProps = "scrollbars=yes,status=yes,resizable=yes";
		
	popupProps = "width=" + popupWidth + ",height=" + popupHeight + "," + popupProps;
	
	// in case of multiple constrained list opened, clear the first before opening another
	if(timedClosed != null)
		clearTimeout(timedClosed);
	constrainedListWin = window.open(url, popupName , popupProps + ',top=' + ((window.screen.availHeight - 480) / 2) + ',left=' + ((window.screen.availWidth - 700) / 2));
	if ( constrainedListWin != null) {
		if( constrainedListWin.opener == null) {
			constrainedListWin.opener = self;
		}
	}
	constrainedListWin.focus();
	checkReloadConstrainedList(url,formId,fieldName);
}
function checkReloadConstrainedList(url,formId,fieldName) {
	try {
    	if(constrainedListWin.closed) {
			AF_reloadField(formId,fieldName);
    	} else {
    		timedClosed=setTimeout("checkReloadConstrainedList('" + url + "','" + formId + "','" + fieldName + "')",500);
    	}
	} catch(e) {
		// in some cases, IE gives a "Permission denied" error on win.closed so thats why we wraps it with try/catch
		// alert(e.message); // retry in a while
		timedClosed=setTimeout("checkReloadConstrainedList('" + url + "','" + formId + "','" + fieldName + "')",500);
		
	}
}


/////////////////////// fields dependencies and reloading methods /////////////////////////////////////



// entry point for reloading a field
function AF_reloadField(formId,fieldName) {
	var field = AF_getElementByIdOrName(fieldName,formId);
	if(field) {
    	var parameters = buildDependencyParameters(formId,fieldName);
    	// for now, only select fields are reloadable
    	if(field.tagName == "SELECT") {
    		AF_reloadList(formId,fieldName,parameters);
    	} else {
    		alert("AF_reloadField: Unsupported field type " + field.tagName);
    	}
    	clearDependenciesTree(formId,fieldName,false);
	} else {
		// perhaps we are reloading a multi-value field (for example reloading a multiple select boxes)
		var form = document.getElementById(formId);
		for( i = 0; i < form.length; i++){	
			var fname = form.elements[i].name;
			if(fname != null) {
				if(fname.indexOf(fieldName + mvSeparator) >= 0 || fname.indexOf("template" + mvSeparator + fieldName) >=0){ 
					// there's a field which name starts with fieldName: or template:fieldName
					// so reload it
					if(form.elements[i].tagName == "SELECT")
						AF_reloadField(formId,fname);
					
				}
			}
		}
		//alert('the following fieldname does not exist in form:' + fieldName);
	}
}

// returns comma separated string of field names impacting on the param fieldName
function getParentDependencies(fieldName,formId) {
	var eln = AF_getElementByIdOrName(fieldName,formId);
	if(eln)
		return eln.getAttribute("parentdependencies");
	else
		return null;
}

// returns comma separated string of field names depending on the param fieldName
function getChildDependencies(fieldName,formId) {
	var eln = AF_getElementByIdOrName(fieldName,formId);
	if(eln)
		return eln.getAttribute("childdependencies");
	else
		return null;
}
// utility function to clear a field
function AF_clearField(formId,fieldName) {
	//alert('clearing field ' + fieldName);
	var eln = AF_getElementByIdOrName(fieldName,formId);
	if(eln.tagName == "SELECT") {
		//alert('clearing select field ' + eln.name);
		eln.options.length = 0;
	} else if(eln.tagName == "INPUT") {
		//alert('clearing input field ' + eln.name);
		eln.value = "";
	}
}
// when reloading a field, we should clear fields which depends on it.
function clearDependenciesTree(formId,fieldName,clearMe) {
//alert('clear child dependencies for ' + fieldName);alert('clear field?' + fieldName + " [" + clearMe + "]");
	if(clearMe)
		AF_clearField(formId,fieldName);
	var eln = AF_getElementByIdOrName(fieldName,formId);
	var dependencies = getChildDependencies(fieldName,formId);
	if(dependencies != null && dependencies != "") {
    	var dependenciesArray = dependencies.split(',');
    	for(var d in dependenciesArray) {
        	var dEln = AF_getElementByIdOrName(dependenciesArray[d],formId);
        	if(dEln != null) {
        		clearDependenciesTree(formId,dependenciesArray[d],true);

			}
    	}	
	}
}

// formId and fieldName are the the select coordinates
// this is the main function which will do the list reloading
function AF_reloadList(formId,fieldName,parameters) {
	var formName = document.getElementById(formId).elements['AF_formName'].value;
	var url = "/autoforms" + "/form/" + formName;
	new Ajax.Request(
		url, Object.extend({
			parameters:   parameters,
			method:	      'get',
			onLoading:    null,
			onComplete:   function(transport) {AF_renderList(formId,fieldName,transport.responseText)} ,
			onFailure:    null,
			asynchronous: true
		}));
}


// build parameters which will be sent as request params to the view generating the list box
function buildDependencyParameters(formId,fieldName) {
	var parameters = "AF_renderField=" + fieldName; // this is mandatory common field for reloading lists
	var dependencies = getParentDependencies(fieldName,formId);
	if(dependencies != null) {
		var dependenciesArray = dependencies.split(',');
		// add each dependency to the xml http request as param
		for(var d in dependenciesArray) {
			var dEln = AF_getElementByIdOrName(dependenciesArray[d],formId);
			// add it in case it is not null and its not empty string
			if(dEln != null && dEln.value != "")
				parameters += "&" + dependenciesArray[d] + "=" + dEln.value;
		}
	}
	return parameters;
}
// this methos inserts the new options in an exising select box (remove existing options)
function AF_renderList(formId,fieldName,newOptions)  {
	//alert(newOptions);
	if(newOptions != null && newOptions != "") {
    	var tmpDivId = "tmp_" + fieldName + "_div";
    	var tmpSelId = tmpDivId + "_sel";
    	var tmpSelContent = "<select style='display:none' disabled id='" + tmpSelId + "'>" + newOptions + "</select>";
    	// we need to attach the <select/> to div and then attach the div to the document in order to traverse it as dom tree
    	attachNewDivTo(tmpDivId,tmpSelContent,document.body); // mozilla does not allow to append childs to document [document.appendChild]
    	AF_replaceOptions(fieldName,tmpSelId,formId);
	} else {
		AF_clearField(formId,fieldName);
	}
}
// attach a new div element with divContent to a parentElement 
function attachNewDivTo(divId,divContent,parentEln) {
	//TODO: remove child from where it is not just the parentEln but for now it okay
	// since the current usage make us sure that it is located in the parentEln
	var existingDiv = document.getElementById(divId);
	if(existingDiv != null) {
		parentEln.removeChild(existingDiv);
	}
	var hiddenDivEln = document.createElement("div");
	hiddenDivEln.id = divId;
	hiddenDivEln.innerHTML = divContent;
	parentEln.appendChild(hiddenDivEln);
	return hiddenDivEln;
}
// formId is used if one parameters is a form element (not an id)
// replace one select's options with other select's options
// source and destination could be either select's id or field name
// if both are ids then no need for formId, you can call this method with 2 params
function AF_replaceOptions(source,destination,formId) {
	var o1 = AF_getElementByIdOrName(source,formId);
	var o2 = AF_getElementByIdOrName(destination,formId);
	//alert("source=" + source);alert("destination=" + destination);alert("formid="+formId);alert(o1.innerHTML);alert(o2.innerHTML);
    o1.length=0; // clear the original select
    var len=o2.length;
    for(var i=0;i<len;i++) {
        var txt=o2.options[i].text;
        var val=o2.options[i].value;
        o1.options[o1.length]=new Option(txt,val);
    }
}
// this cross-browser function tries to find an element by 2 ways: 1) as a form element 2) as an id
function AF_getElementByIdOrName(idOrName,formId) {
	var form = document.getElementById(formId);
	var obj = null;
	if(form != null) {
		obj = document.getElementById(formId).elements[idOrName];
		if(obj == null)
			obj = document.getElementById(idOrName);
	} else {
		obj = document.getElementById(idOrName);
	}
		
	return obj;
}

////////////////////// end

function AF_showDavPicker(formid, fieldname, url, options) {
	var popupName		= options.popupName;	
	var popupHeight		= options.popupHeight;
	var popupWidth		= options.popupWidth;
	var popupProps		= options.popupProps;
	
	davFieldname = fieldname;
	davFormId = formid;
	var rand = Math.round(10000*Math.random())

	if (popupName == null || popupName == "null")
		popupName = "Dav_picker_" + rand;
	if (popupHeight == null || popupHeight == "null")
		popupHeight = 320;
	if (popupWidth == null || popupWidth == "null")
		popupWidth = 420;
	if (popupProps == null || popupProps == "null")
		popupProps = "status=no,resizable=yes,top=200,left=200,scrollbars=yes";
		
	popupProps = "width=" + popupWidth + ",height=" + popupHeight + "," + popupProps;
	
	var winDavPicker = window.open(url + "&cfn=" + fieldname + "&cfi=" + formid, popupName , popupProps);
	
	winDavPicker.opener = self;
}

function AF_setDavUrlValue(formid,fieldname,path) {
	//alert('AF_setDavUrlValue=' + fieldname);
	if(formid != null && formid != "" && fieldname != null && fieldname != "" && path != null && path != "") {
		var eln = document.getElementById(formid).elements[fieldname];
		if(eln != null)
			eln.value = path;
	}
}

function AF_showMusePicker(formid, fieldname, url, options) {
	var popupName		= options.popupName;	
	var popupHeight		= options.popupHeight;
	var popupWidth		= options.popupWidth;
	var popupProps		= options.popupProps;
	
	var rand = Math.round(10000*Math.random())
	var eltId = document.getElementById(formid).elements[fieldname].getAttribute("id");
	url = url +"&openerId="+eltId;
	
	if (popupName == null || popupName == "null")
		popupName = "Muse_picker_" + rand;
	if (popupHeight == null || popupHeight == "null")
		popupHeight = 400;
	if (popupWidth == null || popupWidth == "null")
		popupWidth = 400;
	if (popupProps == null || popupProps == "null")
		popupProps = "status=no,resizable=yes,top=200,left=200,scrollbars=yes";
		
	popupProps = "width=" + popupWidth + ",height=" + popupHeight + "," + popupProps;
	
	var musePicker = window.open(url + "&cfn=" + fieldname + "&cfi=" + formid, popupName , popupProps);
	
	musePicker.opener = self;
}

function AF_showPicker(formid, fieldname, url, options) {
	var popupName		= options.popupName;	
	var popupHeight		= options.popupHeight;
	var popupWidth		= options.popupWidth;
	var popupProps		= options.popupProps;
	
	var rand = Math.round(10000*Math.random())
	var eltId = document.getElementById(formid).elements[fieldname].getAttribute("id");
	url = url +"&openerId="+eltId;
	
	if (popupName == null || popupName == "null")
		popupName = "AF_picker_" + rand;
	if (popupHeight == null || popupHeight == "null")
		popupHeight = 400;
	if (popupWidth == null || popupWidth == "null")
		popupWidth = 400;
	if (popupProps == null || popupProps == "null")
		popupProps = "status=no,resizable=yes,top=200,left=200,scrollbars=yes";
		
	popupProps = "width=" + popupWidth + ",height=" + popupHeight + "," + popupProps;
	
	var picker = window.open(url + "&cfn=" + fieldname + "&cfi=" + formid, popupName , popupProps);
	
	picker.opener = self;
}

// this method is used since that arrays in IE js could contain properties having the same name as a field name so it breaks thats why 
// we are "encoding" in order to make sure its unique and does not conflict with existing properties ( like zip)
function encodeFieldName(fieldName) {
	return "@" + fieldName + "@";
}



function AF_compareDates(after, before, strict) {
	//alert("date after = [" + after + "] date before = [" + before + "] strict = [" + strict + "]");
	var currentDateTime = new Date();
	/* we cant use new Date() directly since it contains hh/mm/ss so it wont be equal to inDate while input dates are dd/mm/yyyy */
	var today = new Date(currentDateTime.getFullYear(),currentDateTime.getMonth(),currentDateTime.getDate());
	
	// do not validate if the document alrea
	var dtAfter = today;
	if(after != null && AF_trim(after) != '')
		dtAfter =  AF_parseDate(after);
	var dtBefore = today;
	if(before != null && AF_trim(before) != '')
		dtBefore =  AF_parseDate(before);
	var ok = strict ? dtAfter.getTime() > dtBefore.getTime() : dtAfter.getTime() >= dtBefore.getTime();
	//alert('result of comparison = dtAfter = [' + dtAfter + '] dtAfter.getTime() [' + dtAfter.getTime() + '] dtBefore [' + dtBefore + '] dtBefore.getTime() [' + dtBefore.getTime() + ']');
	return ok;
}
















function captchaImageReload(captchaId, url, captchaIdCounter){
	document.getElementById('div-'+captchaId).innerHTML = '<img class="captcha-img" src="'+url+'&counter='+(captchaIdCounter)+'"/>';
}
/*
Here you can put any javascript specifc to any project without having
to create a new js file and include it in every template you need it in.
any code you place here will automatically be placed inside the autoforms.js
*/
function AF_initTextareaMaxLengthChecker() {
	var x = document.getElementsByTagName('textarea');
	for (var i=0;i<x.length;i++) {
		AF_attachTextAreaChecker(x[i]);
	}
}

function AF_getTextAreaMaxLength(textarea) {
	return textarea.getAttribute('maxlength');
}

function AF_getTextAreaCurrentLength(textarea) {
	return textarea.value.length;
}

function AF_getTextAreaRegexPattern(textarea) {
	return textarea.getAttribute('pattern');
}

function AF_checkTextareaMaxLength() {
	var maxLength = AF_getTextAreaMaxLength(this);
	var currentLength = AF_getTextAreaCurrentLength(this);
	var pattern = AF_getTextAreaRegexPattern(this);
	//alert(currentLength);alert(maxLength);
	if (currentLength > maxLength) {
		//this.relatedElement.className = 'toomuch';
        //alert('too much');
        this.value = this.value.substr(0,maxLength);
        currentLength = maxLength;
    } else if((pattern == null) || (pattern != null && AF_regexMatch(this.value,pattern)) ) {
    	// alert('pattern is undefined or defined but the value is matching it so go ahead...');
    	//this.relatedElement.className = '';
	} else {
		//alert('pattern is defined but the value does not match it so we have to rollback');
		currentLength--;
		this.value = this.value.substr(0,currentLength);
		// yes believe it or not even we are substracting 1 character its like not removing anything if its a CRLF so we have to remove them both at the same time
		if(this.value.length == currentLength+1) {
			//alert ('we are dealing with CRLF so we have to remove them both at the same time so remove last 2 chars CR and LF');
			currentLength--;
			this.value = this.value.substr(0,currentLength);
		}
    }
	this.relatedElement.firstChild.nodeValue = currentLength;
	// not innerHTML
}

var AF_textAreaCounter = document.createElement('div');
AF_textAreaCounter.className = 'counter';

function AF_attachTextAreaChecker(textarea) {
	var taName = textarea.getAttribute('name');
	var isTemplateField = AF_isTemplateField(taName);
	//alert('name=[' + taName + ']');alert('maxlen=[' + textarea.getAttribute('maxlength') + ']');alert('hasChecker=[' + textarea.hasChecker + ']');alert('[isTemplateField=[' + isTemplateField + ']');
	
	if (allowedToAttachChecker(textarea)) {
		var counterClone = AF_textAreaCounter.cloneNode(true);
		counterClone.relatedElement = textarea;
		counterClone.innerHTML = '<span>0</span>/'+textarea.getAttribute('maxlength');
		textarea.parentNode.insertBefore(counterClone,textarea.nextSibling);
		textarea.relatedElement = counterClone.getElementsByTagName('span')[0];
		textarea.hasChecker = true;
	
		textarea.onkeyup = textarea.onchange = AF_checkTextareaMaxLength;
		textarea.onkeyup();
	}
}

function allowedToAttachChecker(textarea) {
	var taName = textarea.getAttribute('name');
	var allowed = true;
	allowed = allowed && textarea.getAttribute('maxlength');
	allowed = allowed && !AF_isTemplateField(taName);
	allowed = allowed && !textarea.hasChecker;
	return allowed;
}
