/**
 * formweaver.js
 * @Dependancy:
 *	Uses functions.js for various functions
 *	Uses prototype.js for various functions
 * 	References wysiwygBasePath global variable
 
 * Copyright (c) 2007 University of Central Florida.
 * 
 * Licensed under the Educational Community License, Version 1.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 * 
 *      http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 
 */


//*********************************************** fw_form ******************************************************
/**
 * @class fw_form
 */
function fw_form(instanceName, name){
	// form informations
	this.name = name;	// name of the form
	this.instanceName = instanceName;	// name of the instance of fw_form class
	
	this.label = '';	// title of the form
	this.description = '';	// description of the form
	
	this.enctype = 'application/x-www-form-urlencoded';	// content type of form application/x-www-form-urlencoded or multipart/form-data
		
	// TODO: keep track of names?
	// list of used names
	// this.nameLists = new Array();
		
	// array of fields or form groups in the form
	this.formElements = new Array();
}	
// END fw_form()


/**
 * @function addField
 * @desc adds field to the form object
 * @return void
 */
fw_form.prototype.addField = function(fieldObject){
	this.formElements[this.formElements.length] = fieldObject;
	
	// TODO: keep track of names?
	//this.nameLists[this.nameLists.length] = fieldObject.name;
}


/**
 * @function deleteField
 * @desc deletes field from the form object
 * @return boolean
 */
fw_form.prototype.deleteField = function(index){
	if(index >= 0 && index < this.formElements.length){
		var deletedElement = this.formElements.splice(index,1);
		// ? TODO: implement undo functionality?
		// var deleteElementIndex = index;	
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function moveFieldUp
 * @desc moves the field up
 * @return boolean
 */
fw_form.prototype.moveFieldUp = function(index){
	if(index > 0 && index < this.formElements.length){
		var elementToMoveUp = this.formElements[index];
		this.formElements[index] = this.formElements[(index-1)];
		this.formElements[(index-1)] = elementToMoveUp;
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function moveFieldDown
 * @desc moves the field up
 * @return boolean
 */
fw_form.prototype.moveFieldDown = function(index){
	if(index >= 0 && index < (this.formElements.length-1)){
		var elementToMoveUp = this.formElements[(index+1)];
		this.formElements[(index+1)] = this.formElements[index];
		this.formElements[index] = elementToMoveUp;
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function toString
 * @desc outputs html form, NOTE! Set all necessary variables before calling this function
 * @return string
 */
fw_form.prototype.toString = function(){
	// variable to hold output
	var htmlOutput = '';
	
	htmlOutput = "\n" + '<form name="' + this.name.escapeHTML().gsub(' ', '') + '" action="#" method="post" onsubmit="return false;">';
	
	htmlOutput += "\n\t" + '<h2 id="fw_formTitle">' + this.label.escapeHTML() + '</h2>';
	htmlOutput += "\n\t" + '<p id="fw_formDesc">' + this.description.escapeHTML() + '</p>';
	
	for(var i=0; i<this.formElements.length; i++){
		htmlOutput += this.formElements[i];
	}
	
	htmlOutput += "\n\t" + '<input type="submit" value="Submit" class="submitButton" />'; 
	htmlOutput += ' <input type="reset" value="Reset" class="resetButton" />';
	
	htmlOutput += "\n" + '</form>' + "\n";
	
	return htmlOutput;
}



/**
 * @function toHtmlTable
 * @desc outputs html table format, NOTE! Set all necessary variables before calling this function
 * @return string
 */
fw_form.prototype.toHtmlTable = function(){
	// variable to hold output
	var htmlOutput = '';
	
	htmlOutput = "\n" + '<form name="' + this.name.escapeHTML().gsub(' ', '') + '" action="#" method="post" onsubmit="return false;">';
	
	htmlOutput += "\n\t" + '<h2 id="fw_formTitle">' + this.label.escapeHTML() + '</h2>';
	htmlOutput += "\n\t" + '<p id="fw_formDesc">' + this.description.escapeHTML() + '</p>';
	
	var tableOpened = false;
	// htmlOutput += "\n\t" + '<table>';
	
	for(var i=0; i<this.formElements.length; i++){
		if(this.formElements[i].type == 'group'){
			if(tableOpened){
				htmlOutput += "\n\t" + '</table>';
				tableOpened = false;
			}
			htmlOutput += this.formElements[i].toHtmlTable();
		}
		else{
			if(!tableOpened){
				htmlOutput += "\n\t" + '<table>';
				tableOpened = true;
			}
			htmlOutput += this.formElements[i].toHtmlTable();
		}
	}
	
	// close any table
	if(tableOpened){
		htmlOutput += "\n\t" + '</table>';
	}
	
	// htmlOutput += "\n\t" + '</table>';
	
	htmlOutput += "\n\t" + '<input type="submit" value="Submit" class="submitButton" />'; 
	htmlOutput += ' <input type="reset" value="Reset" class="resetButton" />';
	
	htmlOutput += "\n" + '</form>' + "\n";
	
	return htmlOutput;
}



/**
 * @function toXsd
 * @desc outputs to Xsd, NOTE! Set all necessary variables before calling this function
 * @return string
 */
fw_form.prototype.toXsd = function(){
	
	// variable to hold output
	var htmlOutput = '';
	
	htmlOutput = '<?xml version="1.0" encoding="UTF-8"?>';
	htmlOutput += "\n" + '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">';
	htmlOutput += "\n\t" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '">';
	htmlOutput += "\n\t\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '</xs:annotation>';
	htmlOutput += "\n\t\t" + '<xs:complexType>';
	htmlOutput += "\n\t\t\t" + '<xs:sequence>';
	
	for(var i=0; i<this.formElements.length; i++){
		htmlOutput += this.formElements[i].toXsd();
	}
	
	htmlOutput += "\n\t\t\t" + '</xs:sequence>';
   	htmlOutput += "\n\t\t" + '</xs:complexType>';
    htmlOutput += "\n\t" + '</xs:element>';
	htmlOutput += "\n" + '</xs:schema>';
	
	return htmlOutput;
}


/**
 * @function importXsd
 * @desc imports XSD and create javascript objects for editing that xsd
 * @return boolean
 */
fw_form.prototype.importXsd = function(XsdText, oOptions){
	
	// get optional arguments
	var oOptions = augment({firstElement: true, appendElement: false}, oOptions);
	
	// first parse string into an object using xparse.js
	// not using xparse
	// var parsedObj = Xparse(XsdText);
	
	// using just DOM
	// code for IE
	if (window.ActiveXObject){
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		doc.async="false";
		doc.loadXML(XsdText);
	}
	// code for Mozilla, Firefox, Opera, etc.
	else {
		var parser=new DOMParser();
		var doc=parser.parseFromString(XsdText,"text/xml");
	}
	
	var parsedObj=doc.documentElement;
	
	/*
	As it currently works(until DOM support is added), it is simply a function that takes a string and parses it, extracting the tags and their attributes. It collects the tags and all of the textual information into some objects, then returns the object as one main "root" object.
	For now, the API is extreemly simple and only for read-only purposes. There are four types of objects, listed here with each of their properties:

    * An Element: <tag>
          o type - "element"
          o name - string containing the elements name
          o attributes - array containing name=value pairs for each attribute
          o contents - array containing objects for everything within the tag 
    * PI's - Processing Instructions: <? some data ?>
          o type - "pi"
          o value - string containing the data in the pi for the target 
    * Comments: <!-- my comment -->
          o type - "comment"
          o value - string containing the text in the comment 
    * Textual Information
          o type - "chardata"
          o value - string containing the text 

	*/
	
	// now recreate a form element
	// TODO: write some recursive function
	/* not using xparse
	try {
		var rootIndex = -1;
		// find root element xs:schema
		for(var i=0; i<parsedObj.contents.length; i++) {
			if(parsedObj.contents[i].type && parsedObj.contents[i].type == "element" && parsedObj.contents[i].name && parsedObj.contents[i].name.toLowerCase() == "xs:schema"){
				// FOUND! now go through its contents to find its first element and process that
	                        // currently only support one root element and doesnt support templates
				for(var j=0; j<parsedObj.contents[i].contents.length; j++){
					if(parsedObj.contents[i].contents[j].type && parsedObj.contents[i].contents[j].type == "element" && parsedObj.contents[i].contents[j].name && parsedObj.contents[i].contents[j].name.toLowerCase() == "xs:element"){
       		                	       	return this.handleParse(parsedObj.contents[i].contents[j],{firstElement: oOptions.firstElement, appendElement: oOptions.appendElement});
                        		}  
				}
				// false if it got to this position
				return false;
			}
		}

		// error! can't find such element
		alert('Error! in getting the root element, no such element was found!');
		return false;

	}
	catch(e){
		alert('Error! in getting the root element! ' + e);
		return false;
	}
	*/
	
	try {
		var rootIndex = -1;
		// find root element xs:schema
		for(var i=0; i<parsedObj.childNodes.length; i++) {
			// alert(parsedObj.childNodes[i].nodeType + " : " + parsedObj.childNodes[i].nodeName.toLowerCase());
			if(parsedObj.childNodes[i].nodeType == 1 && parsedObj.childNodes[i].nodeName.toLowerCase() == "xs:element"){
				// FOUND! now go through its contents to find its first element and process that
	                        // currently only support one root element and doesnt support templates
				//rootIndex = i;
				/*
				for(var j=0; j<parsedObj.childNodes[i].childNodes.length; j++){
					if(parsedObj.childNodes[i].childNodes[j].nodeType == 1 && parsedObj.childNodes[i].childNodes[j].nodeName.toLowerCase() == "xs:element"){
       		                	       	return this.handleParse(parsedObj.childNodes[i].childNodes[j],{firstElement: oOptions.firstElement, appendElement: oOptions.appendElement});
                        		}
				}
				*/
				return this.handleParse(parsedObj.childNodes[i],{firstElement: oOptions.firstElement, appendElement: oOptions.appendElement});
				// false if it got to this position
				// alert('Error! no xs:element was found!');
				// return false;
			}
		}

		// error! can't find such element
		alert('Error! in getting the root element, no such element was found!');
		return false;

	}
	catch(e){
		alert('Error! in getting the root element! ' + e);
		return false;
	}
}


/**
 * @function handleParse
 * @desc creates appropriate form element objects recursively and returns the finished object
 * @return object ??? or false
 */
// using DOM now
 fw_form.prototype.handleParse = function(parseThis, oOptions) {
	// catch error
	try {
		// get optional arguments
		var oOptions = augment({firstElement: false, appendElement: false}, oOptions );
		// alert('Inside! ' + parseThis.noteType + ' : ' + parseThis.nodeName.toLowerCase());
		// check if type exists and its element type as well as have the name of xs:element
		if(parseThis.nodeType == 1 && parseThis.nodeName.toLowerCase() == "xs:element"){

			// create generic Object and initialize common variables
			var genericObject = new Object();
			genericObject.name = '';
			genericObject.label = '';
			genericObject.description = '';
			genericObject.type = "text" // default to text for now
			
			// go through its attribute first
			var attrValue = null;
			
			// name attribute
			attrValue = parseThis.getAttribute('name');
			if(attrValue != null)
			{
				genericObject.name = attrValue.unescapeHTML();
			}
			
			// minoccurs attribute
			attrValue = parseThis.getAttribute('minOccurs');
			if(attrValue != null)
			{
				if(attrValue == 0){
					genericObject.required = false;
				}
				else if(attrValue == 1){
					genericObject.required = true;
				}
				else {
					// TODO: handle min occur case?
					genericObject.required = true;
				}
			}
			
			// maxoccurs attribute
			attrValue = parseThis.getAttribute('maxOccurs');
			if(attrValue != null)
			{
				if(attrValue.toLowerCase() == "unbounded"){
					genericObject.repeated = true;
				}
				else {
					genericObject.repeated = false;
				}
			}
			
			// type attribute
			attrValue = parseThis.getAttribute('type');
			if(attrValue != null)
			{
				if(attrValue.toLowerCase() == "xs:boolean"){
					genericObject.type = "boolean";
				}	// not sure where these types will be defined so i just check for it here as well as simpletype base attribute
				else if(attrValue.toLowerCase() == "ospi:richtext"){
					genericObject.type = "wysiwyg";
				}
				else if(attrValue.toLowerCase() == "xs:anyuri"){
					genericObject.type = "file";
				}
			}
			// end going through attributes

			// go through child nodes
			for(var k=0; k<parseThis.childNodes.length; k++){
				if(parseThis.childNodes[k].nodeType == 1) {
					// handle supported names	
					switch(parseThis.childNodes[k].nodeName.toLowerCase()){
						case 'xs:annotation':
							//alert('annotation!');
							// look for xs:documentation element
							for(var m=0; m<parseThis.childNodes[k].childNodes.length; m++) {
								if(parseThis.childNodes[k].childNodes[m].nodeType == 1 && parseThis.childNodes[k].childNodes[m].nodeName.toLowerCase() == "xs:documentation"){
									 //alert('documentations!');
									// found xs:documentation element, now look through its attribute
									
									// source attribute
									var attrValue = parseThis.childNodes[k].childNodes[m].getAttribute('source');
									if(attrValue != null){
										// handle supported attributes
										switch(attrValue.toLowerCase()){
											case 'ospi.label':
												//alert('label ' + parseThis.childNodes[k].childNodes[m].childNodes[0].nodeValue);
												//genericObject.label = parseThis.childNodes[k].childNodes[m].firstChild.nodeValue;
												// get text node
												var textValue = '';
												// alert(parseThis.childNodes[k].childNodes[m].childNodes.length);
												for(var n=0; n<parseThis.childNodes[k].childNodes[m].childNodes.length; n++){
													// filter text node
													//alert(n + ' ' + parseThis.childNodes[k].childNodes[m].childNodes[n].nodeType);
													if(parseThis.childNodes[k].childNodes[m].childNodes[n].nodeType == 3){
														textValue += parseThis.childNodes[k].childNodes[m].childNodes[n].data;
													}
												}
												// alert('Label is ' + textValue);
												genericObject.label = textValue.unescapeHTML();
												//alert('After: Label is ' + genericObject.label);
												break;
											case 'ospi.description':
												//alert('description ' + parseThis.childNodes[k].childNodes[m].childNodes[0].nodeValue);
												//genericObject.description = parseThis.childNodes[k].childNodes[m].firstChild.nodeValue;
												//alert('After: description is ' + genericObject.description);
												var textValue = '';
												//alert(parseThis.childNodes[k].childNodes.length);
												for(var n=0; n<parseThis.childNodes[k].childNodes[m].childNodes.length; n++){
													// filter text node
													if(parseThis.childNodes[k].childNodes[m].childNodes[n].nodeType == 3){
														textValue += parseThis.childNodes[k].childNodes[m].childNodes[n].data;
													}
												}
												// alert('Label is ' + textValue);
												genericObject.description = textValue.unescapeHTML();
												break;
											case 'ospi.isrichtext':
												//alert('description ' + parseThis.childNodes[k].childNodes[m].childNodes[0].nodeValue);
												//genericObject.description = parseThis.childNodes[k].childNodes[m].firstChild.nodeValue;
												//alert('After: description is ' + genericObject.description);
												var textValue = '';
												//alert(parseThis.childNodes[k].childNodes.length);
												for(var n=0; n<parseThis.childNodes[k].childNodes[m].childNodes.length; n++){
													// filter text node
													if(parseThis.childNodes[k].childNodes[m].childNodes[n].nodeType == 3){
														textValue += parseThis.childNodes[k].childNodes[m].childNodes[n].data;
													}
												}
												// alert('Label is ' + textValue);
												if(textValue.toLowerCase() == 'true'){
													//alert('It is wysiwyg! 1');
													genericObject.type = "wysiwyg"
												}
												//alert('Is it?');
												
												break;
											default: 
												// not supported attribute
											break;
										}
									}
								}									
							}
							break;
						case 'xs:complextype':
							// alert('complexType!');
							// this is group type but could be the root form element
							genericObject.type = "group";
							// look for xs:sequence element
							// only support 1 sequence! element
							for(var m=0; m<parseThis.childNodes[k].childNodes.length; m++) {
								if(parseThis.childNodes[k].childNodes[m].nodeType == 1 && parseThis.childNodes[k].childNodes[m].nodeName.toLowerCase() == "xs:sequence"){
									// alert('sequence!');
									// initialize the content array
									genericObject.formElements = new Array();
									
									// loop through contents to find elements and recursively call it
									for(var n=0; n<parseThis.childNodes[k].childNodes[m].childNodes.length; n++){
										if(parseThis.childNodes[k].childNodes[m].childNodes[n].nodeType == 1 && parseThis.childNodes[k].childNodes[m].childNodes[n].nodeName.toLowerCase() == "xs:element"){
											var resultObj = this.handleParse(parseThis.childNodes[k].childNodes[m].childNodes[n]);
											
											if(isObject(resultObj)) {
												genericObject.formElements[genericObject.formElements.length] = resultObj;
												//document.getElementById("debug").innerHTML += "<p>Adding result element " + resultObj.name + "(" + resultObj.label + " | " + resultObj.description + ") to " + genericObject.name + "</p>";	
											}
											else {
												//alert("Failed to add result element!");
											}
										}
									}
									break;
								}
							}
							break;
						case 'xs:simpletype':
							//alert('simpleType!');
							// look for xs:restriction element
							// only support 1 restriction! element
							for(var m=0; m<parseThis.childNodes[k].childNodes.length; m++) {
								if(parseThis.childNodes[k].childNodes[m].nodeType == 1 && parseThis.childNodes[k].childNodes[m].nodeName.toLowerCase() == "xs:restriction"){
									// alert('restriction!');
									
									// loop through attributes 
									// base attribute
									var attrValue = parseThis.childNodes[k].childNodes[m].getAttribute('base');
									if(attrValue != null){
										// handle supported attributes
										switch(attrValue.toLowerCase()){
											case 'xs:string':
												//genericObject.type = "text";
												// dont do anything since default is text
												break;
											case 'xs:normalizedstring':
												// basically same as xs:string but this is definitely a text input field.. but why have duplicates?
												//genericObject.type = "text";
												// dont do anything since default is text
												break;
											case 'xs:integer':
												//genericObject.type ="text"
												genericObject.number = true;
												break;
											case 'xs:anyuri':
												genericObject.type = "file"
												break;
											case 'ospi:richtext':
												genericObject.type = "wysiwyg"
												break;
											case 'xs:date':
												genericObject.type = "date"
												break;
											case 'xs:boolean':
												genericObject.type = "boolean"
												break;
											default: 
												// not supported attribute
											break;
										}
									}
									// end looping through attributes
									
									// loop through contents to find elements and get restrictions
									for(var n=0; n<parseThis.childNodes[k].childNodes[m].childNodes.length; n++){
										if(parseThis.childNodes[k].childNodes[m].childNodes[n].nodeType == 1){
											// handle supported restrictions
											switch(parseThis.childNodes[k].childNodes[m].childNodes[n].nodeName.toLowerCase()){
												case "xs:maxlength":
													// loop through its attribute to find a value attribute
													var attrValue = parseThis.childNodes[k].childNodes[m].childNodes[n].getAttribute('value');
													if(attrValue != null){
														genericObject.maxLength = parseInt(attrValue);
														if(genericObject.maxLength > 99) {
															genericObject.type = "textarea";
														}
													}
													// end looping through attributes
													break;
												case "xs:mininclusive":
													// loop through its attribute to find a value attribute
													var attrValue = parseThis.childNodes[k].childNodes[m].childNodes[n].getAttribute('value');
													if(attrValue != null){
														if(attrValue.type == "date"){ 
															genericObject.minDate = attrValue.unescapeHTML();
														}
														else{
															genericObject.minNumber = parseInt(attrValue);
														}
													}
													// end looping through attributes
													break;
												case "xs:maxinclusive":
													// loop through its attribute to find a value attribute
													var attrValue = parseThis.childNodes[k].childNodes[m].childNodes[n].getAttribute('value');
													if(attrValue != null){
														if(genericObject.type == "date"){ 
															genericObject.maxDate = attrValue.unescapeHTML();
														}
														else{
															genericObject.maxNumber = parseInt(attrValue);
														}
													}
													// end looping through attributes
													break;
												case "xs:enumeration":
													//alert('Enumeration!');
													// set the type 
													genericObject.type = "select";
													
													//alert("element " + genericObject.name + " has type of " + genericObject.type);
													// initialize the selections if not initialized
													if(!genericObject.selections){
														genericObject.selections = new Array();
													}
													
													// loop through its attribute to find a value attribute
													var attrValue = parseThis.childNodes[k].childNodes[m].childNodes[n].getAttribute('value');
													if(attrValue != null){
														// create selection object and insert into a selection array
														// TODO: it doesn't support separate label for now
														attrValue = attrValue.unescapeHTML();
														genericObject.selections[genericObject.selections.length] = new fw_selection(attrValue, attrValue);
													}
													// end looping through attributes
													break;
												case "xs:pattern":
													// loop through its attribute to find a value attribute
													var attrValue = parseThis.childNodes[k].childNodes[m].childNodes[n].getAttribute('value');
													if(attrValue != null){
														if(attrValue == '^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$'){
															genericObject.email = true;
														}
														else if(attrValue == '^[2-9]\d{2}-\d{3}-\d{4}$'){
															genericObject.phone = true;
														}
													}
													// end looping through attributes
													break;
												default: 
													// unsupported element
													break;
											}
										}
									}
									// end looping through contents
									break;
								}
							}
							break;
						default: 
							// not supported element
							break;
					}
				}
			}
			// end going through contents
				
			// if first element, then assign value to form element
			if(oOptions.firstElement) {
				if(typeof genericObject.name != "undefined") {
					this.name = genericObject.name;
				}
				if(typeof genericObject.label != "undefined") {
					this.label = genericObject.label;
				}
				if(typeof genericObject.description != "undefined") {
					this.description = genericObject.description;
				}
				if(typeof genericObject.formElements != "undefined") {
					// alert('assigned form elements ' + genericObject.formElements.length);
					if(oOptions.appendElement){
						// alert('Appending ' + genericObject.formElements.length + ' elements ' + this.formElements.length);
						this.formElements = this.formElements.concat(genericObject.formElements);
						// alert('After appending ' + this.formElements.length);
					}
					else{
						// alert('Replacing elements');
						this.formElements = genericObject.formElements;
					}
				}
				return true;
			}
			else {	// if not create a suitable form element then return that object
				var newElement = false;
				// check what type of element it is
				switch(genericObject.type) {
					case 'group':
						// create new group object
						newElement = new fw_group('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						if(typeof genericObject.formElements != "undefined") {
							newElement.formElements = genericObject.formElements;
						}
						
						break;
					case 'file':
						// create new boolean object
						newElement = new fw_fileField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						
						break;
					case 'wysiwyg':
						// create new boolean object
						newElement = new fw_wysiwygField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						
						break;
					case 'text':
						// create new text object
						newElement = new fw_textField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							//document.getElementById("debug").innerHTML += "<p>" + newElement.label + " vs Generic " + genericObject.label + "</p>"; 
							newElement.label = genericObject.label;
							//document.getElementById("debug").innerHTML += "<p>" + newElement.label + " vs Generic " + genericObject.label + "</p>"; 
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.maxLength != "undefined") {
							newElement.maxLength = genericObject.maxLength;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						if(typeof genericObject.email != "undefined") {
							newElement.email = genericObject.email;
						}
						if(typeof genericObject.phone != "undefined") {
							newElement.phone = genericObject.phone;
						}
						if(typeof genericObject.number != "undefined") {
							newElement.number = genericObject.number;
						}
						if(typeof genericObject.minNumber != "undefined") {
							newElement.minNumber = genericObject.minNumber;
						}
						if(typeof genericObject.maxNumber != "undefined") {
							newElement.maxNumber = genericObject.maxNumber;
						}
						
						break;
					case 'date':
						// create new date object
						newElement = new fw_dateField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.maxLength != "undefined") {
							newElement.maxLength = genericObject.maxLength;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						if(typeof genericObject.minDate != "undefined") {
							newElement.minDate = genericObject.minDate;
						}
						if(typeof genericObject.maxDate != "undefined") {
							newElement.maxDate = genericObject.maxDate;
						}
						
						break;
					case 'boolean':
						// create new boolean object
						newElement = new fw_booleanField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						
						break;
					case 'textarea':
						// create new textarea object
						newElement = new fw_textareaField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.maxlength != "undefined") {
							newElement.maxlength = genericObject.maxlength;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						
						break;
					case 'select':
						// create new select object
						newElement = new fw_selectField('','','');
						
						if(typeof genericObject.name != "undefined") {
							newElement.name = genericObject.name;
						}
						if(typeof genericObject.label != "undefined") {
							newElement.label = genericObject.label;
						}
						if(typeof genericObject.description != "undefined") {
							newElement.description = genericObject.description;
						}
						if(typeof genericObject.selections != "undefined") {
							newElement.selections = genericObject.selections;
						}
						if(typeof genericObject.required != "undefined") {
							newElement.required = genericObject.required;
						}
						if(typeof genericObject.repeated != "undefined") {
							newElement.repeated = genericObject.repeated;
						}
						
						break;
					default:
						// unsupported type
						break;
				}
				
				// return the element
				//if(isObject(newElement)){
					//document.getElementById("debug").innerHTML += "<p>Generic element " + genericObject.name + "( " + genericObject.label + " | " + genericObject.description + " | " + genericObject.type + ")</p>";
					//document.getElementById("debug").innerHTML += "<p>Returning element " + newElement.name + "( " + newElement.label + " | " + newElement.description + " | " + newElement.type + ")</p>";
				//}
				return newElement;
			}
			
			// shouldn't get here
			return false;
			
		}
		else {
			// i can't parse non element types! :(
			alert("Error! Can't parse " + parseThis.nodeType + " - " + parseThis.nodeName);
			return false;
		}

	}
	catch(e) {
		alert('Error:' + e);
		return false;
	}
}



/**
 * @function reset
 * @desc resets form object and clears all stored form elements
 * @return true or false
 */
fw_form.prototype.reset = function(){
	// reset form informations
	this.name = '';	// name of the form
	this.label = '';	// title of the form
	this.description = '';	// description of the form
	this.enctype = 'application/x-www-form-urlencoded';
				
	// reset form elements
	this.formElements = new Array();
}


//***********************************************************************************************************


//*********************************************** fw_group ******************************************************
/**
 * @class fw_group
 */
function fw_group(name, label, description){
	// form informations
	this.name = name;	// name of the form
	this.type = "group";
	
	this.label = label;	// title of the form
	this.description = description;	// description of the form
	
	this.required = true;
	this.repeated = false;
		
	// TODO: keep track of names?
	// list of used names
	// this.nameLists = new Array();
		
	// array of fields in the group
	this.formElements = new Array();
}	
// END fw_form()


/**
 * @function addMember
 * @desc adds field to the form object
 * @return void
 */
fw_group.prototype.addMember = function(fieldObject){
	this.formElements[this.formElements.length] = fieldObject;
	
	// TODO: keep track of names?
	//this.nameLists[this.nameLists.length] = fieldObject.name;
}


/**
 * @function deleteMember
 * @desc deletes field from the form object
 * @return boolean
 */
fw_group.prototype.deleteMember = function(index){
	if(index >= 0 && index < this.formElements.length){
		var deletedElement = this.formElements.splice(index,1);
		// ? TODO: implement undo functionality?
		// var deleteElementIndex = index;	
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function moveMemberUp
 * @desc moves the field up
 * @return boolean
 */
fw_group.prototype.moveMemberUp = function(index){
	if(index > 0 && index < this.formElements.length){
		var elementToMoveUp = this.formElements[index];
		this.formElements[index] = this.formElements[(index-1)];
		this.formElements[(index-1)] = elementToMoveUp;
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function moveMemberDown
 * @desc moves the field up
 * @return boolean
 */
fw_group.prototype.moveMemberDown = function(index){
	if(index >= 0 && index < (this.members.length-1)){
		var elementToMoveUp = this.formElements[(index+1)];
		this.formElements[(index+1)] = this.formElements[index];
		this.formElements[index] = elementToMoveUp;
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function toString
 * @desc outputs html form, NOTE! Set all necessary variables before calling this function
 * @return string
 */
fw_group.prototype.toString = function(){
	// variable to hold output
	var htmlOutput = '';
	
	htmlOutput += "\n\t" + '<h3 class="fw_formTitle">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput += ' </h3>';
	htmlOutput += "\n\t" + '<p class="fw_formDesc">' + this.description.escapeHTML() + '</p>';
	htmlOutput += "\n\t" + '<div class="group">';
	
	if(this.formElements.length == 0){
		htmlOutput += "\n\t\t" + '<p>No group fields exist</p>';
	}
	else{ 
		for(var i=0; i<this.formElements.length; i++){
			htmlOutput += this.formElements[i];
		}
	}
	htmlOutput += "\n\t" + '</div>';
	
	return htmlOutput;
}



/**
 * @function toHtmlTable
 * @desc outputs html table form, NOTE! Set all necessary variables before calling this function
 * @return string
 */
fw_group.prototype.toHtmlTable = function(){
	// variable to hold output
	var htmlOutput = '';
	
	// close previous table
	// htmlOutput += "\n\t" + '</table>';
	
	htmlOutput += "\n\t" + '<div class="group">';
	htmlOutput += "\n\t" + '<h3 class="fw_formTitle">';
	
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput += ' </h3>';
	htmlOutput += "\n\t" + '<p class="fw_formDesc">' + this.description.escapeHTML() + '</p>';
	
	
	if(this.formElements.length == 0){
		htmlOutput += "\n\t\t" + '<p>No group fields exist</p>';
	}
	else{ 
		// htmlOutput += "\n\t" + '<table>';
	        var tableOpened = false;
		for(var i=0; i<this.formElements.length; i++){
			if(this.formElements[i].type == 'group'){
				if(tableOpened){
					htmlOutput += "\n\t" + '</table>';
					tableOpened = false;
				}
				htmlOutput += this.formElements[i].toHtmlTable();
			}
			else {
				if(!tableOpened){
					htmlOutput += "\n\t" + '<table>';
					tableOpened = true;
				}
				htmlOutput += this.formElements[i].toHtmlTable();
			}
		}
		
		// close any tables opened
		if(tableOpened){
			htmlOutput += "\n\t" + '</table>';
		}
		
		// htmlOutput += "\n\t" + '</table>';
	}
	htmlOutput += "\n\t" + '</div>';
	
	// resume table
	// htmlOutput += "\n\t" + '<table>';
	
	return htmlOutput;
}



/**
 * @function toXsd
 * @desc outputs to Xsd, NOTE! Set all necessary variables before calling this function
 * @return string
 */
fw_group.prototype.toXsd = function(){
	// variable to hold output
	var htmlOutput = '';
	
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	htmlOutput += '>';
    htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n\t" + '<xs:complexType>';
    htmlOutput += "\n\t\t" + '<xs:sequence>';
	
	for(var i=0; i<this.formElements.length; i++){
		htmlOutput += this.formElements[i].toXsd();
	}
	
	htmlOutput += "\n\t\t" + '</xs:sequence>';
    htmlOutput += "\n\t" + '</xs:complexType>';
    htmlOutput += "\n" + '</xs:element>';
	
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_wysiwygField ******************************************************

/**
 * @class fw_wysiwygField
 * @desc used by both radio and checkbox fields
 */
function fw_wysiwygField(name, label, description){
	this.label = label;	// label of choice
	this.name = name;	// name of field
	this.description = description;
	
	this.required = true;
	this.repeated = false;
	
	this.type = "wysiwyg";
}


/**
 * @function toString
 * @desc return html output for file field
 * @return string
 * TODO: convert this into rich text area
 */
fw_wysiwygField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t\t" + '<br /><textarea name="' + this.name.escapeHTML().gsub(' ', '') + '" rows="10" cols="80"';
	
	// optional attributes
	/*
	if(!isNaN(this.columns) && Math.floor(this.columns) > 0){
		htmlOutput += ' cols="' + Math.floor(this.columns) + '"';
	}
	if(!isNaN(this.rows) && Math.floor(this.rows) > 0){		
		htmlOutput += ' rows="' + Math.floor(this.rows) + '"';
	}
	*/
	
	htmlOutput += '>';
	
	htmlOutput += '</textarea>';
	htmlOutput += "\n\t" + '</label>';
	htmlOutput += "\n\t" + '<br /><p id="' + this.name.escapeHTML().gsub(' ', '') + 'WysiwygLink">Note: Wysiwyg editor will appear in place of text box in sakai. <a href="#" onclick="convertToWysiwyg(\'' + this.name.escapeHTML() + '\'); hidethis(\'' + this.name.escapeHTML() + 'WysiwygLink\'); return false;">View Wysiwyg editor</a></p>';
	
	return htmlOutput;
}



/**
 * @function toHtmlTable
 * @desc return html table output for file field
 * @return string
 * TODO: convert this into rich text area
 */
fw_wysiwygField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t\t\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  '</span></label>';
	htmlOutput +=  "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	htmlOutput += "\n\t\t\t" + '<textarea name="' + this.name.escapeHTML().gsub(' ', '') + '" rows="10" cols="80"';
	
	// optional attributes
	/*
	if(!isNaN(this.columns) && Math.floor(this.columns) > 0){
		htmlOutput += ' cols="' + Math.floor(this.columns) + '"';
	}
	if(!isNaN(this.rows) && Math.floor(this.rows) > 0){		
		htmlOutput += ' rows="' + Math.floor(this.rows) + '"';
	}
	*/
	
	htmlOutput += '>';
	
	htmlOutput += '</textarea>';
	htmlOutput += "\n\t" + '<br /><p id="' + this.name.escapeHTML().gsub(' ', '') + 'WysiwygLink">Note: Wysiwyg editor will appear in place of text box in sakai. <a href="#" onclick="convertToWysiwyg(\'' + this.name.escapeHTML().gsub(' ', '') + '\'); hidethis(\'' + this.name.escapeHTML().gsub(' ', '') + 'WysiwygLink\'); return false;">View Wysiwyg editor</a></p>';
	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" + '</tr>';
	
	return htmlOutput;
}



/**
 * @function toXsd
 * @desc return html output for file field using table
 * @return string
 */
fw_wysiwygField.prototype.toXsd = function(){
	var htmlOutput = '';
	
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	
	htmlOutput += '>';
  	htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.isRichText">true</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n\t" + '<xs:simpleType>';
	htmlOutput += "\n\t\t" + '<xs:restriction base="ospi:richText">';
    htmlOutput += "\n\t\t" + '</xs:restriction>';
    htmlOutput += "\n\t" + '</xs:simpleType>';
	htmlOutput += "\n" + '</xs:element>';
	
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_fileField ******************************************************

/**
 * @class fw_fileField
 * @desc used by both radio and checkbox fields
 */
function fw_fileField(name, label, description){
	this.label = label;	// label of choice
	this.name = name;	// name of field
	this.description = description;
	
	this.required = true;
	this.repeated = false;
	
	this.type = "file";
}


/**
 * @function toString
 * @desc return html output for file field
 * @return string
 */
fw_fileField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n\t" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	
	htmlOutput += '<input type="file" name="' + this.name.escapeHTML().gsub(' ', '') + '"/>';
	htmlOutput += '</label>';
	htmlOutput += "\n\t" +'<br />';
	return htmlOutput;
}



/**
 * @function toHtmlTable
 * @desc return html output for file field
 * @return string
 */
fw_fileField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span></label>';
	htmlOutput += "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	// htmlOutput += "\n\t" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	
	htmlOutput += '<input type="file" name="' + this.name.escapeHTML().gsub(' ', '') + '" />'

	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" +'</tr>';
	return htmlOutput;
}



/**
 * @function toXsd
 * @desc return html output for file field using table
 * @return string
 */
fw_fileField.prototype.toXsd = function(){
	var htmlOutput = '';
	
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	
	htmlOutput += '>';
  	htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n\t" + '<xs:simpleType>';
	htmlOutput += "\n\t\t" + '<xs:restriction base="xs:anyURI">';
    htmlOutput += "\n\t\t" + '</xs:restriction>';
    htmlOutput += "\n\t" + '</xs:simpleType>';
	htmlOutput += "\n" + '</xs:element>';
	
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_textField ******************************************************
 
/**
 * @class fw_textField
 */
function fw_textField(name,label,description){
	// text/password field info
	this.name = name;	// name of the field
	this.label = label;	// label of the field
	this.description = description;	// description of the field
	this.maxLength = -1;	// max length of the field
	this.required = true;	// required = false sets minOccur to 0, required = true sets minOccurs to 1
	this.repeated = false;
	this.email = false;
	this.phone = false;
	this.number = false;
	this.minNumber = false;
	this.maxNumber = false;
	this.type = "text";
}


/**
 *	@function toString
 *	@desc outputs HTML for text/password field
 *	@return string
 */
fw_textField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n\t" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += '<input type="text" name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	
	// optional attributes
	if(!isNaN(this.maxLength) && Math.floor(this.maxLength) > 0){
		htmlOutput += ' maxlength="' + Math.floor(this.maxLength) + '" size="' + Math.floor(this.maxLength) + '"';
	}
	
	htmlOutput += ' />';
	htmlOutput += '</label>';
	htmlOutput += "\n\t" + '<br />';
	return htmlOutput;
}


/**
 *	@function toHtmlTable
 *	@desc outputs HTML for text/password field
 *	@return string
 */
fw_textField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span></label>';
	htmlOutput += "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	// htmlOutput += "\n\t" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += '<input type="text" name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	
	// optional attributes
	if(!isNaN(this.maxLength) && Math.floor(this.maxLength) > 0){
		htmlOutput += ' maxlength="' + Math.floor(this.maxLength) + '" size="' + Math.floor(this.maxLength) + '"';
	}
	
	htmlOutput += ' />';
	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" + '</tr>';
	return htmlOutput;
}


/**
 *	@function toXsd
 *	@desc outputs to Xsd
 *	@return string
 */
fw_textField.prototype.toXsd = function(){
	var htmlOutput = '';
	var maxlength = 32;	// default max length
	
	if(!isNaN(this.maxLength) && Math.floor(this.maxLength) > 0){
		maxlength = Math.floor(this.maxLength);
	}
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	htmlOutput += '>';
  	htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n\t" + '<xs:simpleType>';
	if(this.number) {
    	htmlOutput += "\n\t\t" + '<xs:restriction base="xs:integer">';
	}
	else{
		htmlOutput += "\n\t\t" + '<xs:restriction base="xs:string">';
	}
    htmlOutput += "\n\t\t\t" + '<xs:maxLength value="' + maxlength + '"/>';
	if(this.email){
		htmlOutput += "\n\t\t\t" + '<xs:pattern value="^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$"/>';
	}
	if(this.phone){
		htmlOutput += "\n\t\t\t" + '<xs:pattern value="^[2-9]\d{2}-\d{3}-\d{4}$"/>';
	}
	if(this.minNumber != false){
		htmlOutput += "\n\t\t\t" + '<xs:minExclusive value="' + this.minNumber + '"/>';
	}
	if(this.maxNumber != false){
		htmlOutput += "\n\t\t\t" + '<xs:maxInclusive value="' + this.maxNumber + '"/>';
	}
    htmlOutput += "\n\t\t" + '</xs:restriction>';
    htmlOutput += "\n\t" + '</xs:simpleType>';
	htmlOutput += "\n" + '</xs:element>';
	
	return htmlOutput;
}


//***********************************************************************************************************



//*********************************************** fw_dateField ******************************************************
 
/**
 * @class fw_dateField
 */
function fw_dateField(name,label,description){
	// text/password field info
	this.name = name;	// name of the field
	this.label = label;	// label of the field
	this.description = description;	// description of the field
	this.required = true;
	this.repeated = false;
	this.minDate = false;
	this.maxDate = false;
	this.type = "date";
}


/**
 *	@function toString
 *	@desc outputs HTML for date field
 *	@return string
 */
fw_dateField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n\t" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	//htmlOutput += '<input type="text" name="' + htmlEntities(this.name) + '" id="' + htmlEntities(this.name) + '" />';
	htmlOutput += "\n\t\t" + '<select name="' + this.name.escapeHTML().gsub(' ', '') + '_month" id="' + this.name.escapeHTML().gsub(' ', '') + '_month">';
	htmlOutput += "\n\t\t\t" + '<option value="1">JAN</option>';
	htmlOutput += "\n\t\t\t" + '<option value="2">FEB</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="3">MAR</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="4">APR</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="5">MAY</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="6">JUN</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="7">JUL</option>';         
	htmlOutput += "\n\t\t\t" + '<option value="8">AUG</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="9">SEP</option>';        
	htmlOutput += "\n\t\t\t" + '<option value="10">OCT</option>';        
	htmlOutput += "\n\t\t\t" + '<option value="11">NOV</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="12">DEC</option>'; 
	htmlOutput += "\n\t\t" + '</select>';
	htmlOutput += "\n\t\t" +'<select name="' + this.name.escapeHTML().gsub(' ', '') + '_day" id="' + this.name.escapeHTML().gsub(' ', '') + '_day">';
	for(var i=1; i<32; i++){
		htmlOutput += "\n\t\t\t" +'<option value="' + i + '">' + i + '</option>';
	}
	htmlOutput += "\n\t\t" +'</select>';
	htmlOutput += "\n\t\t" +'<select name="' + this.name.escapeHTML().gsub(' ', '') + '_year" id="' + this.name.escapeHTML().gsub(' ', '') + '_year">';
	for(var i=1996; i<2016; i++){
		htmlOutput += "\n\t\t\t" +'<option value="' + i + '">' + i + '</option>';
	}
	htmlOutput += "\n\t\t" +'</select>';
	htmlOutput += "\n\t" +'</label>' + "\n";
	//htmlOutput += "\n\t" +'<a href="#" onclick="return false;">Pick a date</a>';
	htmlOutput += "\n\t" +'<br />';
	return htmlOutput;
}



/**
 *	@function toHtmlTable
 *	@desc outputs HTML for date field
 *	@return string
 */
fw_dateField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span></label>';
	htmlOutput += "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	// htmlOutput += "\n\t" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	//htmlOutput += '<input type="text" name="' + htmlEntities(this.name) + '" id="' + htmlEntities(this.name) + '" />';
	htmlOutput += "\n\t\t" + '<select name="' + this.name.escapeHTML().gsub(' ', '') + '_month" id="' + this.name.escapeHTML().gsub(' ', '') + '_month">';
	htmlOutput += "\n\t\t\t" + '<option value="1">JAN</option>';
	htmlOutput += "\n\t\t\t" + '<option value="2">FEB</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="3">MAR</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="4">APR</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="5">MAY</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="6">JUN</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="7">JUL</option>';         
	htmlOutput += "\n\t\t\t" + '<option value="8">AUG</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="9">SEP</option>';        
	htmlOutput += "\n\t\t\t" + '<option value="10">OCT</option>';        
	htmlOutput += "\n\t\t\t" + '<option value="11">NOV</option>';       
	htmlOutput += "\n\t\t\t" + '<option value="12">DEC</option>'; 
	htmlOutput += "\n\t\t" + '</select>';
	htmlOutput += "\n\t\t" +'<select name="' + this.name.escapeHTML().gsub(' ', '') + '_day" id="' + this.name.escapeHTML().gsub(' ', '') + '_day">';
	for(var i=1; i<32; i++){
		htmlOutput += "\n\t\t\t" +'<option value="' + i + '">' + i + '</option>';
	}
	htmlOutput += "\n\t\t" +'</select>';
	htmlOutput += "\n\t\t" +'<select name="' + this.name.escapeHTML().gsub(' ', '') + '_year" id="' + this.name.escapeHTML().gsub(' ', '') + '_year">';
	for(var i=1996; i<2016; i++){
		htmlOutput += "\n\t\t\t" +'<option value="' + i + '">' + i + '</option>';
	}
	htmlOutput += "\n\t\t" +'</select>';
	
	//htmlOutput += "\n\t" +'<a href="#" onclick="return false;">Pick a date</a>';
	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" +'</tr>';
	return htmlOutput;
}



/**
 *	@function toXsd
 *	@desc outputs to Xsd
 *	@return string
 */
fw_dateField.prototype.toXsd = function(){
	var htmlOutput = '';
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	htmlOutput += '>';
  	htmlOutput += "\n\t" + '<xs:annotation>';
	htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
	htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
	htmlOutput += "\n\t" + '</xs:annotation>';
	htmlOutput += "\n\t" + '<xs:simpleType>';
	htmlOutput += "\n\t\t" + '<xs:restriction base="xs:date">';
	if(this.minDate != false) {
		htmlOutput += "\n\t\t\t" + '<xs:minInclusive value="' + this.minDate + '"/>';
	}
	if(this.maxDate != false) {
		htmlOutput += "\n\t\t\t" + '<xs:maxInclusive value="' + this.maxDate + '"/>';
	}
	htmlOutput += "\n\t\t" + '</xs:restriction>';
	htmlOutput += "\n\t" + '</xs:simpleType>';
	htmlOutput += "\n" + '</xs:element>';
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_booleanField ******************************************************

/**
 * @class fw_booleanField
 * @desc used for boolean type
 */
function fw_booleanField(name, label, description){
	// boolean field info
	this.name = name;	// name of the field
	this.label = label;	// label of the field
	this.description = description;	// description of the field
	this.required = true;
	this.repeated = false;
	this.type = "boolean";
}


/**
 * @function toString
 * @desc outputs HTML for radio/checkbox field
 * @return string
 */
fw_booleanField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label>';
	//htmlOutput += "\n\t" + '<input type="checkbox" value="yes" name="' + htmlEntities(this.name) + '" /> <span class="label">';
	htmlOutput += "\n\t" + '<span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t" + '<input type="checkbox" value="yes" name="' + this.name.escapeHTML().gsub(' ', '') + '" /></label>';
	htmlOutput += "\n\t" +'<br />';
	return htmlOutput;
}



/**
 * @function toHtmlTable
 * @desc outputs HTML for radio/checkbox field
 * @return string
 */
fw_booleanField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t" + '<label>';
	//htmlOutput += "\n\t" + '<input type="checkbox" value="yes" name="' + htmlEntities(this.name) + '" /> <span class="label">';
	htmlOutput += "\n\t" + '<span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span></label>';
	htmlOutput += "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t" + '<input type="checkbox" value="yes" name="' + this.name.escapeHTML().gsub(' ', '') + '" />';
	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" +'</tr>';
	return htmlOutput;
}



/**
 * @function toTable
 * @desc outputs HTML for radio/checkbox field using table
 * @return string
 */
fw_booleanField.prototype.toXsd = function(){
	var htmlOutput = '';
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	htmlOutput += '>';
    htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
  	htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n\t" + '<xs:simpleType>';
    htmlOutput += "\n\t\t" + '<xs:restriction base="xs:boolean">';
    htmlOutput += "\n\t\t" + '</xs:restriction>';
    htmlOutput += "\n\t" + '</xs:simpleType>';
	htmlOutput += "\n" + '</xs:element>';
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_textareaField ******************************************************

/**
 * @class fw_textareaField
 */
function fw_textareaField(name, label, description){
	// textarea field info
	this.name = name;	// name of the field
	this.label = label;	// label of the field
	this.description = description;	// description of the field
	this.maxlength = -1;
	this.required = true;
	this.repeated = false;
	this.type = "textarea";
}


/**
 * @function toString
 * @desc outputs HTML string
 * @return string
 */
fw_textareaField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t\t" +'<textarea name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	
	// optional attributes
	/*
	if(!isNaN(this.columns) && Math.floor(this.columns) > 0){
		htmlOutput += ' cols="' + Math.floor(this.columns) + '"';
	}
	if(!isNaN(this.rows) && Math.floor(this.rows) > 0){		
		htmlOutput += ' rows="' + Math.floor(this.rows) + '"';
	}
	*/
	
	htmlOutput += '>';
	
	htmlOutput += "\n\t\t" +'</textarea>';
	htmlOutput += "\n\t" +'</label>' + "\n";
	htmlOutput += "\n\t" +'<br />';
	return htmlOutput;
}


/**
 * @function toHtmlTable
 * @desc outputs HTML string
 * @return string
 */
fw_textareaField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span></labe>';
	htmlOutput += "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t\t" +'<textarea name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	
	// optional attributes
	/*
	if(!isNaN(this.columns) && Math.floor(this.columns) > 0){
		htmlOutput += ' cols="' + Math.floor(this.columns) + '"';
	}
	if(!isNaN(this.rows) && Math.floor(this.rows) > 0){		
		htmlOutput += ' rows="' + Math.floor(this.rows) + '"';
	}
	*/
	
	htmlOutput += '>';
	
	htmlOutput += "\n\t\t" +'</textarea>';
	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" +'</tr>';
	return htmlOutput;
}


/**
 * @function toXsd
 * @desc outputs to Xsd
 * @return string
 */
fw_textareaField.prototype.toXsd = function(){
	var htmlOutput = '';
	
	var maxlength = 100;	// default max length
	
	if(!isNaN(this.maxLength) && Math.floor(this.maxLength) > 100){
		maxlength = Math.floor(this.maxLength);
	}
	
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	htmlOutput += '>';
    htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';   
    htmlOutput += "\n\t" + '<xs:simpleType>';
    htmlOutput += "\n\t\t" + '<xs:restriction base="xs:string">';
    htmlOutput += "\n\t\t\t" + '<xs:maxLength value="' + maxlength + '"/>';
    htmlOutput += "\n\t\t" + '</xs:restriction>';
    htmlOutput += "\n\t" + '</xs:simpleType>';
	htmlOutput += "\n" + '</xs:element>';
	
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_selection ******************************************************

/**
 * @class fw_selection
 */
function fw_selection(label, value){
	// select item info
	this.label = label;	// label of selection
	this.value = value;	// value of selection
}


/**
 * @function toString
 * @desc output HTML for section
 * @return string
 */
fw_selection.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t\t\t" + '<option value="' + this.value.escapeHTML() + '">' + this.label.escapeHTML() + '</option>' + "\n";
	return htmlOutput;
}


/**
 * @function toXsd
 * @desc output XSD for section
 * @return string
 */
fw_selection.prototype.toXsd = function(){
	var htmlOutput = '';
	htmlOutput += "\n" + '<xs:enumeration value="' + this.value.escapeHTML() + '">';
    htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation>' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n" + '</xs:enumeration>';
	return htmlOutput;
}


//***********************************************************************************************************


//*********************************************** fw_selectField ******************************************************

/**
 * @class fw_selectField 
 */
function fw_selectField(name, label, description){
	// select field info
	this.name = name;	// name of the field
	this.label = label;	// label of the field
	this.description = description;	// description of the field
	// selections or selection groups
	this.selections = new Array();
	this.required = true;
	this.repeated = false;
	this.type = "select";
}


/**
 * @function addOption
 * @desc adds option to the select field
 * @return boolean
 */
fw_selectField.prototype.addOption = function(label, value, selected, id){
	// check if arguments are valid
	if(!isEmpty(label) && !isEmpty(value)){
		this.selections[this.selections.length] = new fw_selection(label, value);
		if(!isEmpty(id)){
			this.selections[(this.selections.length-1)].id = id;
		}
		if(isBoolean(selected)){
			this.selections[(this.selections.length-1)].selected = selected;
		}
		return true;
	}
	else{
		return false;
	}
}


/**
 * @function toString
 * @desc output HTML for section field
 * @return string
 */
fw_selectField.prototype.toString = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t\t" + '<select name="' + this.name.escapeHTML().gsub(' ', '') + '">';
	
	for(var i=0; i<this.selections.length; i++){
		htmlOutput += this.selections[i];
	}
	
	htmlOutput += "\n\t\t" + '</select>';
	htmlOutput += "\n\t" + '</label>';
	htmlOutput += "\n\t" +'<br />';
	return htmlOutput;
}



/**
 * @function toHtmlTable
 * @desc output HTML for section field
 * @return string
 */
fw_selectField.prototype.toHtmlTable = function(){
	var htmlOutput = '';
	htmlOutput += "\n\t" + '<tr>';
	htmlOutput += "\n\t\t" + '<th scope="row">';
	htmlOutput += "\n\t" + '<label><span class="label">';
	if(this.required) {
		htmlOutput +=  '<span class="required">* </span>';
	}
	htmlOutput += this.label.escapeHTML();
	if(this.repeated) {
		htmlOutput +=  '<span class="repeated"> [<a href="#" onclick="return false;">+</a>]</span>';
	}
	htmlOutput +=  ' </span></label>';
	htmlOutput += "\n\t\t" + '</th>';
	htmlOutput += "\n\t\t" + '<td>';
	// htmlOutput += "\n" + '<p class="desc">' + htmlEntities(this.description) + '</p>';
	htmlOutput += "\n\t\t" + '<select name="' + this.name.escapeHTML().gsub(' ', '') + '">';
	
	for(var i=0; i<this.selections.length; i++){
		htmlOutput += this.selections[i];
	}
	
	htmlOutput += "\n\t\t" + '</select>';
	htmlOutput += "\n\t\t" + '</td>';
	htmlOutput += "\n\t" +'</tr>';
	return htmlOutput;
}



/**
 * @function toXsd
 * @desc output Xsd for section field
 * @return string
 */
fw_selectField.prototype.toXsd = function(){
	var htmlOutput = '';
	
	htmlOutput += "\n" + '<xs:element name="' + this.name.escapeHTML().gsub(' ', '') + '"';
	if(!this.required) {
		htmlOutput += ' minOccurs="0"';
	}
	else {
		htmlOutput += ' minOccurs="1"';
	}
	if(this.repeated){
		htmlOutput += ' maxOccurs="unbounded"';
	}
	htmlOutput += '>';
    htmlOutput += "\n\t" + '<xs:annotation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.label">' + this.label.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t\t" + '<xs:documentation source="ospi.description">' + this.description.escapeHTML() + '</xs:documentation>';
    htmlOutput += "\n\t" + '</xs:annotation>';
    htmlOutput += "\n\t" + '<xs:simpleType>';
    htmlOutput += "\n\t\t" + '<xs:restriction base="xs:string">'
    for(var i=0; i<this.selections.length; i++){
		htmlOutput += this.selections[i].toXsd();
	}
    htmlOutput += "\n\t\t" + '</xs:restriction>';
    htmlOutput += "\n\t" + '</xs:simpleType>';
    htmlOutput += "\n" + '</xs:element>';
	return htmlOutput;
}


//***********************************************************************************************************
