function ServiceForm (EventManager, serviceXMLObj, serviceMap) {
	var ServiceFormId = 'ServiceForm-';
	var FormTableId = ServiceFormId+serviceXMLObj.getAttribute('title');

	this.clearForm = function () {
		var rootElement = document.getElementById(FormTableId);
		if(rootElement && rootElement.parentNode) {
			var p = rootElement.parentNode;
			p.removeChild(rootElement);
		}
	}
	
	function createInputArray(tableId) {
		var table = document.getElementById(tableId);
		var inputs = table.getElementsByTagName('input');
		var inputValues = new Array();
		for(var i = 0; i < inputs.length; i++) {
			inputValues[inputs[i].name] = inputs[i].value;
		}
		inputs = null;

		var selects = table.getElementsByTagName('select');
		for(var i = 0; i < selects.length; i++) {
			var options = selects[i].getElementsByTagName('option');
			inputValues[selects[i].name] = options[selects[i].selectedIndex].value;
			options = null; 
		}
		selects = null;
		table = null;

		return inputValues;
	}

	this.setParent = function(p) {
		var divId = serviceXMLObj.getAttribute('div');
		if(divId) {
			this.renderFromDiv(p, divId);
		} else {
			this.renderDefault(p);
		}
	}

	this.renderFromDiv = function(p,divId) {
		var parent = document.getElementById(p);
		var div = document.getElementById(divId);
		parent.innerHTML = div.innerHTML;
		
		var inputs = parent.getElementsByTagName('input');
		var searchButton = null, cancelButton = null;
		for(var i = 0; i < inputs.length; i++) {
			var inputType = new String(inputs[i].type);
			if(inputType.match(/submit/)) {
				inputs[i].onclick = function () {
					var values = createInputArray(p);
					var submitInputs = serviceXMLObj.getElementsByTagName('input');
					for(var i = 0; i < submitInputs.length; i++) {
						submitInputs[i].setAttribute('value', values[submitInputs[i].getAttribute('name')]); 
					}
					if(this.getAttribute("url")) {
						var urls = serviceXMLObj.getElementsByTagName('url');
						if(urls.length > 0) {
							urls[0].firstChild.nodeValue = this.getAttribute('url');
						} else {
							var parentDoc = serviceXMLObj.parentNode;
							while(parentDoc.parentNode) {
								parentDoc = parentDoc.parentNode;
							}
							var newUrl = parentDoc.createElement('url');
							newUrl.appendChild(parentDoc.createTextNode(this.getAttribute('url')));
							serviceXMLObj.appendChild(newUrl);
						}
							
					}
					EventManager.triggerEvent('callservice', serviceXMLObj);
				}
				searchButton = i;

			} else if(inputType.match(/reset/)) {
				inputs[i].onclick = function() {
					EventManager.triggerEvent('cancelservice', serviceXMLObj);
				}
			} else if(inputType.match(/text/)) {
				inputs[i].onkeyup = function(event) {
					if(window.event) { event= window.event; }
					if(event.keyCode == 13) {
						if(searchButton) {
							inputs[searchButton].onclick();
						}
					} else if(event.keyCode == 27) {
						if(cancelButton) {
							inputs[cancelButton].onclick();
						}
					}
				}
			}
		}

	}

	this.renderDefault = function(p) {
		document.getElementById(p).innerHTML = '';
		var formTable = document.createElement('table');
		document.getElementById(p).appendChild(formTable);
		formTable.setAttribute('id', FormTableId);
		
		var formBody = document.createElement('tbody');
		formTable.appendChild(formBody);

		var inputs = serviceXMLObj.getElementsByTagName('input');
		var userInputs = 0;
		var invalids = 0;

		for(var i = 0; i < inputs.length; i++) {
			var newInputRow = document.createElement('tr');
			var inputTitleCell = document.createElement('th');
			inputTitleCell.className = 'InputTitleCell';
			if(inputs[i].getAttribute('title') && inputs[i].getAttribute('title') != 'null') {
				inputTitleCell.appendChild(document.createTextNode(inputs[i].getAttribute('title')));
			}
			formBody.appendChild(newInputRow);
			var newInputCell = document.createElement('td');
			newInputRow.appendChild(inputTitleCell);
			newInputRow.appendChild(newInputCell);

			var inputType = new String(inputs[i].getAttribute('type')).toLowerCase();
			if(inputType == 'user') {
				var inputObj = document.createElement('input');
				if(inputs[i].getAttribute('value') && inputs[i].getAttribute('value') != 'null') {
					inputObj.value = inputs[i].getAttribute('value');
				}

				inputObj.name = inputs[i].getAttribute('name');
				var validateType = inputs[i].getAttribute('validate');
				if(validateType && validateType != 'null') {
					validateType = validateType.toUpperCase();
					if(VALIDATE_FUNCTIONS[validateType]) {
						inputObj.onchange = function (event) {
							if(window.event) { event = window.event; }
							var value = !eval(VALIDATE_FUNCTIONS[validateType]+'("'+this.value+'")');
							if(value) {
								this.style.background = SERVICES_INVALID_BACKGROUND;
								this.setAttribute('invalid', 'true');
								invalids++;
							} else if(this.getAttribute('invalid')) {
								this.style.background = '';
								invalids--;
							}
							return preventDefaultEvent(event);
						}
					}
				}

				newInputCell.appendChild(inputObj);
				userInputs++;
			} else if(inputType == 'select') {
				var selectObj = document.createElement('select');
				selectObj.name = inputs[i].getAttribute('name');
				var options = inputs[i].getElementsByTagName('option');
				for(var o = 0; o < options.length; o++ ) {
					var option = document.createElement('option');
					option.value = options[o].getAttribute('value');
					option.appendChild(document.createTextNode(options[o].firstChild.nodeValue));
					selectObj.appendChild(option);
				}
				newInputCell.appendChild(selectObj);
				userInputs++;
			} else if(inputType == 'hidden') {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = inputs[i].getAttribute('value');
				/*inputsTable.appendChild(newInput);*/
				formBody.appendChild(newInput);
			} else if(inputType == 'checkbox') {
				var newInput = document.createElement('input');
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = inputs[i].getAttribute('value');
				newInput.type = 'checkbox';
				newInputCell.appendChild(newInput);
				newInput.checked = parseBoolean(inputs[i].getAttribute('checked'));
				userInputs++;
			} else if(inputType == 'radio') {
				var hiddenValue = document.createElement('input');
				hiddenValue.type = 'hidden';
				hiddenValue.name = inputs[i].getAttribute('name');
				formBody.appendChild(hiddenValue);

				var options = inputs[i].getElementsByTagName('option');
				var checked = true;
				for(var o = 0; o < options.length; o++) {
					var radio = document.createElement('input');
					radio.name = inputs[i].getAttribute('name');
					radio.value = options[o].getAttribute('value');
					radio.type = 'radio';
					radio.checked = checked;
					newInputCell.appendChild(radio);
					newInputCell.appendChild(document.createTextNode(options[o].firstChild.nodeValue));
					newInputCell.appendChild(document.createElement('br'));
					if(checked) {
						hiddenValue.value = radio.value;
					}

					radio.onclick = function () {
						hiddenValue.value = this.value;
					}
					checked = false;
				}
			} else if(inputType == 'file') {
				var name = inputs[i].getAttribute('name');
				var value = inputs[i].getAttribute('value');
				if(!(value.substring(0,1) == '/' || value.match(/[A-Za-z]\:/))) {
					value = MAPSERVER_MAPFILE_ROOT + value;
				}
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputType == 'layers') {
				var layers = serviceMap.getLayerURLs();
				var name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				var value = '';
				for(var layer in layers) {
					value = value + serviceMap.preprocessURL('image',layer) + delim;
				}
				value = value.substring(0,value.length-1);
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputType == 'legends') {
				var layers = serviceMap.getLayerURLs();
				var name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				var value = '';
				for(var layer in layers) {
					value = value + serviceMap.preprocessURL('legend',layer) + delim;
				}
				value = value.substring(0,value.length-1);
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputType == 'opacity') {
				var layers = serviceMap.getLayerURLs();
				var name = inputs[i].getAttribute('name');
				var delim = inputs[i].getAttribute('delim');
				var value = '';
				for(var layer in layers) {
					value = value + serviceMap.getLayerOpacity(layer) + delim;
				}
				value = value.substring(0,value.length-1);
				var newInput = document.createElement('input');
				newInput.name = name;
				newInput.value = escape(value);
				newInput.type = 'hidden';
				formBody.appendChild(newInput);
			} else if(inputType == 'extent') {
				var newInput = document.createElement('input');
				var delim = inputs[i].getAttribute('delim');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.value = serviceMap.getExtent(delim);
				formBody.appendChild(newInput);
			} else if(inputType == 'layertitles' || inputType == 'visiblelayers') {
				var newInput = document.createElement('input');
				var delim = inputs[i].getAttribute('delim');
				var allLayers = (inputType != 'visiblelayers');
				
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				var layers = serviceMap.getLayerTitles();
				var str = '';
				for(var l = 0; l < layers.length; l++) {
					if(allLayers || serviceMap.isLayerVisible(layers[l])) {
						if(delim == " ") {
							layers[l] = layers[l].replace(/ /g,'');
						}
						str += delim+layers[l];
					}
				}
				str = str.substring(1);
				newInput.value = str;
				formBody.appendChild(newInput);
			} else if(inputType == 'title') {
				var newInput = document.createElement('input');
				newInput.name = inputs[i].getAttribute('name');
				var v = inputs[i].getAttribute('value');
				if(v && v != 'null') {
					newInput.value = v;
				}
				newInputCell.appendChild(newInput);
				newInput.onchange = function (event) {
					if(window.event) { event = window.event; }
					if(!this.value) { alert ('Drawing objects must have a name!'); }
					var layers = serviceMap.getLayerTitles();
					for(var l = 0; l < layers.length; l++) {
						if(this.value == layers[l]) {
							alert('Already a layer named '+this.value);
							this.value = '';
							return preventDefaultEvent(event);
						}
					}
					return true;
				}
				userInput++;
				newInput = null;
			} else if(inputType == 'selection') {
				var delim = inputs[i].getAttribute('delim');
				var layerName = inputs[i].getAttribute('layer');
				var name = inputs[i].getAttribute('name');
				if(layerName) {
					var newInput = document.createElement('input');
					newInput.name = name;
					var selections = serviceMap.getLayerSelection(layerName);
					if(selections && selections.length > 0) {
						var str = selections[0];
						for(var sel = 1; sel < selections.length; sel++) {
							str += delim+selections[sel];
						}
						newInput.value = str;
					}
					newInput.type = 'hidden';
					newInputCell.appendChild(newInput);
				} else {
					var selectObj = document.createElement('select');
					selectObj.name = name;
					var layers = serviceMap.getLayerTitles();
					var found = false;
					newInputCell.appendChild(selectObj);
					for(var layer = 0; layer < layers.length; layer++) {
						var selections = serviceMap.getLayerSelection(layers[layer]);
						if(selections && selections.length > 0) {
							found = true;
							var opt = document.createElement('option');
							selectObj.appendChild(opt);
							var str = selections[0];
							for(var sel = 1; sel < selections.length; sel++) {
								str += delim+selections[sel];
							}
							opt.appendChild(document.createTextNode(layers[layer]));
							opt.value = str;
						}
					}
					if(!found) {
						selectObj.disabled = true;
						var opt = document.createElement('option');
						selectObj.appendChild(opt);
						opt.appendChild(document.createTextNode('No Layers Selected!'));
					}
					userInputs++;
				}
			} else if(inputType == 'area') {
				var selectObj = document.createElement('select');
				selectObj.name = inputs[i].getAttribute('name');
				var areas = serviceMap.getDrawingAreas();
				for(var area in areas) {
					var option = document.createElement('option');
					option.value = areas[area];
					option.appendChild(document.createTextNode(area));
					selectObj.appendChild(option);
				}
				newInputCell.appendChild(selectObj);
				userInputs++;
			} else if(inputType == 'visiblelayer') {
				var layers = serviceMap.getLayerTitles();
				var selectObj = document.createElement('select');
				selectObj.name = inputs[i].getAttribute('name');
				selectObj.style.width = '150px';
				newInputCell.appendChild(selectObj);

				var nullOpt = document.createElement('option');
				selectObj.appendChild(nullOpt);
				nullOpt = null;

				for(var layer = 0; layer < layers.length; layer++) {
					var option = document.createElement('option');
					option.value = layers[layer];
					selectObj.appendChild(option);
					option.appendChild(document.createTextNode(layers[layer]));
					option = null;
				}

				selectObj = null;
				userInputs++;
			} else if(inputType == 'length') {
				var newInput = document.createElement('input');
				newInput.type = 'hidden';
				newInput.name = inputs[i].getAttribute('name');
				newInput.setAttribute('id','ServiceForm-'+newInput.name);
				
				var userInput = document.createElement('input');
				userInput.setAttribute('id', newInput.getAttribute('id')+'-Source');
				userInput.targetId = newInput.getAttribute('id');
				userInput.sourceId = userInput.getAttribute('id');

				var v = inputs[i].getAttribute('value');
				if(!v || v == 'null') {
					userInput.setAttribute('value', '0');
				} else {
					userInput.setAttribute('value', v);
				}
				newInput.setAttribute('value', userInput.getAttribute('value'));
			
				newInputCell.appendChild(newInput);
				newInputCell.appendChild(userInput);
				newInputCell.appendChild(document.createElement('br'));

				var groundUnits = document.createElement('select');
				groundUnits.setAttribute('id',newInput.getAttribute('id')+'-Units');

				newInputCell.appendChild(groundUnits);
				populateUnitConversionSelection('length', groundUnits);
				
				groundUnits.targetId = newInput.getAttribute('id');
				groundUnits.sourceId = userInput.getAttribute('id');
				groundUnits.unitId = groundUnits.getAttribute('id');
				userInput.unitId = groundUnits.getAttribute('id');
				groundUnits.onchange = function () {
					var src = document.getElementById(this.sourceId);
					var target = document.getElementById(this.targetId);
					var units = document.getElementById(this.unitId);
					var opts = units.getElementsByTagName('option');
					var v = src.value;
					if(units.selectedIndex >= 0) { 
						var selOpt = opts[units.selectedIndex];
						var unitCheck = new String(selOpt.value);
						v = Math.round(v*UNIT_CONVERSION[selOpt.value][MAP_GROUND_UNITS]);
						target.value = v;
					}
				}
				userInput.onchange = groundUnits.onchange;

				groundUnits = null;
				newInput = null;
				userInput = null;
				userInputs++;
			}
			newInputCell = null;
			newInputRow = null;
		}

		var cancelSubmitRow = document.createElement('tr');

		var cancelButton = document.createElement('button');
		cancelButton.appendChild(document.createTextNode('Cancel'));
		var searchButton = document.createElement('button');
		searchButton.appendChild(document.createTextNode('OK'));

		var cancelCell = document.createElement('td');
		var searchCell = document.createElement('td');
		searchCell.style.textAlign = 'right';
		cancelCell.appendChild(cancelButton);
		searchCell.appendChild(searchButton);
		cancelSubmitRow.appendChild(cancelCell);
		cancelSubmitRow.appendChild(searchCell);
		formBody.appendChild(cancelSubmitRow);
		cancelButton.onclick = function() {
			EventManager.triggerEvent('cancelservice', serviceXMLObj);
		}


		searchButton.onclick = function () {
			if(invalids <= 0) {
				var values = createInputArray(FormTableId);
				var inputs = serviceXMLObj.getElementsByTagName('input');
				for(var i = 0; i < inputs.length; i++) {
					inputs[i].setAttribute('value', values[inputs[i].getAttribute('name')]); 
				}
				EventManager.triggerEvent('callservice', serviceXMLObj);
			} else {
				/* OUCH DO NOT DO THAT */
				alert(SERVICES_INVALID_MESSAGE);
			}
		}

		formBody.searchButton = searchButton;
		formBody.onkeyup = function (event) {
			if(window.event) { event = window.event; }
			if(event.keyCode == 13) {
				/*
				 * This prevents the service from making an annoying alert message
				 * when the validation function has already made an alert message
				 */
				if(invalids <= 0) {
					this.searchButton.onclick();
				}
			}
		}

		if(userInputs <= 0) {
			searchButton.onclick();
		}
		searchButton = null;
		cancelButton = null;
		cancelCell = null;
		searchCell = null;
		cancelSubmitRow = null;
		formBody = null;
		formTable = null;

	}
}
