
// API_AJAX is created using source from the SpryData.js at:
// http://labs.adobe.com/technologies/spry/includes/SpryData.js
// Therefore the following copyright notice is retained.

// Copyright (c) 2006. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Adobe Systems Incorporated nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.


window.AJAX = new Object();
AJAX._business = true;
AJAX.busy	= false;
AJAX._busy	= {tmr:0,queue:[],func:null,count:0};

try {
	AJAX._business = (top != window);
} catch (e) { }

AJAX.checkEnabled = function() {
	var req = null;
	this.isEnabled = false;

	try {
		if (window.XMLHttpRequest) {
			try { req = new XMLHttpRequest(); }
			catch (e) { req = null; }
		} else if (window.ActiveXObject) {
			try { req = new ActiveXObject("Msxml2.XMLHTTP"); }
			catch (e) {
				try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
				catch (e) { req = null; }
			}
		}
		this.isEnabled = req && (typeof (req) == 'object');
		req = null;
	}
	catch (e) { }

	return this.isEnabled;
};
AJAX.setBusy = function(busy) {
	if (!this._business) return;
	if (busy) {
		if (this._busy.count == 0) {
			try { top.FD.Lock.Set(); } catch (e) { }
		}
		this._busy.count++;
	} else {
		this._busy.count--;
		if (this._busy.count == 0) {
			try { top.FD.Lock.Release(); } catch (e) { }
		}
	}
	if (this._busy.count < 0) this._busy.count = 0;
	this.busy = this._busy.count > 0;
}
AJAX.checkBusy = function(func) {
	if (!this._business) return false;
	window.clearTimeout(this._busy.tmr);
	if (!this.busy) {
		if (arguments.length == 0 && this._busy.queue.length > 0) {
			func = this._busy.queue.pop();
			if (typeof func == 'string') eval(func);
			else func();
			if (this._busy.queue.length > 0) {
				this._busy.tmr = window.setTimeout('AJAX.checkBusy()', 200);
			}
		}
		return !this.busy;
	}
	if (func) this._busy.queue.push(func);
	//alert('busy: ' + AJAX.busy);
	this._busy.tmr = window.setTimeout('AJAX.checkBusy()', 500);
	return false;
}
AJAX.cancelBusy = function(func) {
	if (!this._business) return;
	window.clearTimeout(this._busy.tmr);
	this.busy = false;
	this._busy.count = 0;
	try { top.FD.Lock.Cancel(); } catch (e) { }
	if (arguments.length > 0) {
		if (typeof func == 'string') eval(func);
		else func();
	}
}

AJAX.createRequest = function() {
	var req = null;

	try {
		if (window.XMLHttpRequest) {
			try { req = new XMLHttpRequest(); }
			catch (e) {
				this.Debug.reportError("createRequest", "Failed to create an XMLHttpRequest object!");
				return null;
			}
		}
		else if (window.ActiveXObject) {
			try { req = new ActiveXObject("Msxml2.XMLHTTP"); }
			catch (e) {
				try { req = new ActiveXObject("Microsoft.XMLHTTP"); }
				catch (e) {
					this.Debug.reportError("createRequest", "Failed to invoke an MS XMLHTTP ActiveXObject!");
					return null;
				}
			}
		} else {
			this.Debug.reportError("createRequest", "Browser does not support the XMLHttpRequest object!");
			return null;
		}
	} catch (e) { this.Debug.reportError("createRequest", "Exception caught: " + (typeof e.message != 'undefined' ? e.message : e)); }

	return req;
}

AJAX.sendRequest = function(data) { // url, data, async, callback ) {
	var req = null;
	try {
		if (!data || !data.url || (data.async && !data.callback)) {
			this.Debug.reportError("sendRequest", "Not enough parameters!");
			return;
		}
		// check some parameters
		data.verb = data.verb || 'GET';
		data.async = data.async || false; // be sure it is a boolean
		if (typeof data.count == 'undefined') data.count = 0;
		data.count++;

		req = this.createRequest();
	} catch (e) {
		alert('1' + e.message);
	}
	AJAX.setBusy(true);
	try {
		// if the call is asynchronous then set the callback function
		if (data.async) {
			req.onreadystatechange = function() {
				if (req.readyState == AJAX.Constants.readyState.COMPLETED && req.status == AJAX.Constants.Status.READY) {
					AJAX.setBusy(false);
				}
				if (req.readyState == AJAX.Constants.readyState.COMPLETED && req.status == 12031 && data.count < 2)
					AJAX.sendRequest(data);
				else
					data.callback(req, data);
			};
		}
		req.open(data.verb, data.url, data.async);
		if (data.verb == 'POST') {
			req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			req.setRequestHeader("Content-Length", data.content.length);
			req.setRequestHeader("Connection", "close");
			req.send(data.content || '');
		} else {
			if (req.readyState != AJAX.Constants.readyState.LOADING) {
				var state = req.readyState;
				req.abort();
				req = null;
				throw "sendRequest::send : Error sending request, readyState=" + state;
			}
			req.send(null);
		}
		if (!data.async && data.callback) { data.callback(req, data); }

		if (!data.async) {
			AJAX.setBusy(false);
		}

	} catch (e) { fdOnError((typeof e.message != 'undefined' ? e.message : e), '', 132); }

	return req;
};

AJAX.stringToXMLDoc = function(str) {
	var xmlDoc = null;

	if (window.ActiveXObject) {
		// Attempt to parse the string using the IE method.
		try {
			var xmlDOMObj = new ActiveXObject("Microsoft.XMLDOM");
			xmlDOMObj.async = false;
			xmlDOMObj.loadXML(str);
			xmlDoc = xmlDOMObj;
		} catch (e) {
			if (window.XMLHttpRequest) {
				try {
					if (!FD.Cookie.Exist('XMLDOM_failed')) {
						FD.Cookie.Set('XMLDOM_failed', 'true', 60);
						fdFinalOnError(window, 'AJAX.stringToXMLDoc failed: ' + (typeof e.message != 'undefined' ? e.message : e), '', 153, '', true);
					}

					var xmlDOMobj = new XMLHttpRequest();
					try { xmlDOMobj = this.sendRequest({ url: '/string2xml.asp', content: 'data=' + encodeURIComponent(str), verb: 'POST' }); } catch (e1) { alert('opening request failed'); throw e1; }
					xmlDoc = xmlDOMobj.responseXML;
					e = null;
				} catch (e1) { e = e1; }
			}
			if (e) {
				this.Debug.reportError("stringToXMLDoc", "Exception caught: " + (typeof e.message != 'undefined' ? e.message : e) + '\r\n\r\nString:\r\n' + str);
				xmlDoc = null;
			}
		}
	} else if (window.DOMParser) {
		try {
			var domParser = new DOMParser();
			xmlDoc = domParser.parseFromString(str, 'text/xml');
		}
		catch (e) {
			this.Debug.reportError("stringToXMLDoc", "Exception caught: " + (typeof e.message != 'undefined' ? e.message : e) + '\r\n\r\nString:\r\n' + str);
			xmlDoc = null;
		}
	} else {
		this.Debug.reportError("stringToXMLDoc", "Browser does not support the DOMParser object!");
	}

	return xmlDoc;
};

AJAX.getNodesByFunc = function(root, func) {
	var nodeStack = new Array;
	var resultArr = new XMLNodeList;
	var node = root;

	while (node) {
		if (func(node))
			resultArr.push(node);

		if (node.hasChildNodes()) {
			nodeStack.push(node);
			node = node.firstChild;
		} else {
			if (node == root)
				node = null;
			else
				try { node = node.nextSibling; } catch (e) { node = null; };
		}

		while (!node && nodeStack.length > 0) {
			node = nodeStack.pop();
			if (node == root)
				node = null;
			else
				try { node = node.nextSibling; } catch (e) { node = null; }
		}
	}

	if (nodeStack && nodeStack.length > 0)
		this.Debug.trace("getNodesByFunc", "WARNING: Failed to traverse all nodes!");

	return resultArr;
};

AJAX.getNodeValue = function(node, nodeName, alt, type) {
	if (!node) alert('Node is not an object. (' + node + ', ' + nodeName + ', ' + alt + ', ' + type + ')(stack: ' + FD.Stack.Format(',') + ')');
	try {
		var sel = (nodeName ? node.selectSingleNode(nodeName) : node);
	} catch (e) {
		this.Debug.reportError("getNodeValue", "Could not select value from node (" + node + ") with nodeName '" + nodeName + "'!");
		throw e;
	}
	var val = (alt || '');
	if (sel && sel.firstChild) val = sel.firstChild.nodeValue;
	if (type) return this.Utils.stringToXMLValue(val, type);
	else return val;
}


// AJAX Constants
AJAX.Constants = new Object();
// XMLHTTPRequest readyState constants
/**
* The state of the request.
*
* Possible values:
*   0 UNINITIALIZED open() has not been called yet.
*   1 LOADING       send() has not been called yet.
*   2 LOADED        send() has been called, headers and status are available.
*   3 INTERACTIVE   Downloading, responseText holds the partial data.
*   4 COMPLETED     Finished with all operations.
*/
AJAX.Constants.readyState = {
	UNINITIALIZED: 0,
	LOADING: 1,
	LOADED: 2,
	INTERACTIVE: 3,
	COMPLETED: 4
};
AJAX.Constants.Status = {
	READY: 200
};
AJAX.Constants.nodeTypes = {
	ELEMENT_NODE: 1,
	ATTRIBUTE_NODE: 2,
	TEXT_NODE: 3,
	CDATA_SECTION_NODE: 4,
	ENTITY_REFERENCE_NODE: 5,
	ENTITY_NODE: 6,
	PROCESSING_INSTRUCTION_NODE: 7,
	COMMENT_NODE: 8,
	DOCUMENT_NODE: 9,
	DOCUMENT_TYPE_NODE: 10,
	DOCUMENT_FRAGMENT_NODE: 11,
	NOTATION_NODE: 12
};
AJAX.Constants.errorCodes = {
	TASK_SAVE_CONDITION: -1
};

// AJAX Utility methods
AJAX.Utils = new Object();

AJAX.Utils.encodeEntities = function(str) {
	if (str) {
		str = str.replace(/&/g, "&amp;");
		str = str.replace(/</g, "&lt;");
		str = str.replace(/>/g, "&gt;");
		str = str.replace(/"/g, "&quot;");
	}
	return str
};

AJAX.Utils.toXML = function(node) {
	if (node.innerXML) {
		return node.innerXML;
	} else if (node.xml) {
		return node.xml;
	} else if (typeof XMLSerializer != "undefined") {
		return (new XMLSerializer()).serializeToString(node);
	} else
		return '';
}

AJAX.Utils.toObject = function(node, obj_to) {
	var obj = obj_to || {}, child, i, type;
	if (!node.childNodes || node.childNodes.length == 0) alert('AJAX.Utils.toObject: Invalid node!');
	for (i = 0; i < node.childNodes.length; i++) {
		if (node.childNodes[i].nodeType == 1 /* ELEMENT_NODE */) {
			child = node.childNodes[i];
			obj[child.nodeName] = AJAX.getNodeValue(child, '', '', child.getAttribute('datatype') || '');
		}
	}
	return obj;
}

AJAX.Utils.decodeEntities = function(str) {
	var d = AJAX.Utils.decodeEntities.div;
	if (!d) {
		d = document.createElement('div');
		AJAX.Utils.decodeEntities.div = d;
		if (!d) return str;
	}
	d.innerHTML = str;
	if (d.childNodes.length == 1 && d.firstChild.nodeType == 3 /* Node.TEXT_NODE */ && d.firstChild.nextSibling == null)
		str = d.firstChild.data;
	else {
		// Hmmm, innerHTML processing of str produced content
		// we weren't expecting, so just replace entities we
		// expect folks will use in node attributes that contain
		// JavaScript.
		str = str.replace(/&lt;/, "<");
		str = str.replace(/&gt;/, ">");
		str = str.replace(/&quot;/, "\"");
		str = str.replace(/&amp;/, "&");
	}
	return str;
};

/** collectFormData - Compiles a string of form data. - brad@xkr.us - 2004-10-20 **/
AJAX.Utils.collectFormData = function(form, full_checkbox) {

	var cRetval = '', cTemp = '', cCTName = '', cCType = '', arrElts = [], oCurrent = null;
	for (var i = form.elements.length - 1; i >= 0; i--) {
		oCurrent = form.elements[i];
		/* successful elements must have a name and must not be disabled */
		if (oCurrent.name && !oCurrent.disabled) arrElts.push(oCurrent);
	}

	/* sort elements so same names will be adjacent to each other */
	arrElts.sort(function(a, b) { return ((a.name < b.name) ? 1 : (a.name == b.name) ? 0 : -1); });

	while (oCurrent = arrElts.pop()) {
		cCTName = oCurrent.tagName.toLowerCase();
		cCType = ((oCurrent.type) ? oCurrent.type.toLowerCase() : '');

		/* handle input[type="radio|checkbox"] */
		if (cCTName == "input" && /^(radio|checkbox)$/.test(cCType)) {
			do {
				if (oCurrent.checked || oCurrent.selected)
					cRetval = cRetval.append(URLEncode(oCurrent.name) + '=' + URLEncode(oCurrent.value), '&');
				else if (full_checkbox && cCType == 'checkbox')
					cRetval = cRetval.append(URLEncode(oCurrent.name) + '=off', '&');
			} while (arrElts.length > 0 && arrElts[arrElts.length - 1].name == oCurrent.name && (oCurrent = arrElts.pop()));
		}

		/* handle select[multiple] */
		if (cCTName == "select" && oCurrent.multiple && oCurrent.options) {
			for (i = 0, len = oCurrent.options.length, cTemp = ''; i < len; i++) {
				if (oCurrent.options[i].selected)
					cTemp = cTemp.append(URLEncode(oCurrent.options[i].value), ',');
			}
			cRetval = cRetval.append(URLEncode(oCurrent.name) + '=' + sTemp, '&');
		}
		/* any other element */
		else if ((cCTName == "input" && /^(text|password|hidden)$/.test(cCType)) || /^(select|textarea)$/.test(cCTName)) {
			cRetval = cRetval.append(URLEncode(oCurrent.name) + '=' + URLEncode(oCurrent.value), '&');
		}
	}
	return cRetval;
}
String.prototype.append = function(add, sep) {
	return this + ((this.length) ? sep : '') + add;
}

/**
Creates an object to emulate a form from the elements string.
Elements can be a comma delimited string. You can optionally assign name-mappings using the syntax: <element-name>|<mapping-name>.
This mapping is used when this object is part of a AJAX.Form lookup to map a form-element with a node-name in the xml result.
**/
AJAX.Utils.createForm = function(elements) {
	var form = { elements: new Array(), mapping: new Object() }, aEl = (arguments.length > 0 ? elements.split(',') : new Array()), el;
	for (var i = 0; i < aEl.length; i++) {
		aEl[i] = aEl[i].split('|');
		el = document.getElementsByName(aEl[i][0]);
		if (el.length > 0) {
			if (aEl[i].length == 2) form.mapping[aEl[i][0]] = aEl[i][1];
			for (var j = 0; j < el.length; j++) {
				if (inList(el[j].tagName.toLowerCase(), 'input', 'select', 'textarea'))
					form.elements.push(el[j]);
			}
		}
	}
	return form;
}

/** fillFormData - fills a form with the values of node.childNodes **/
AJAX.Utils.fillFormData = function(node) {
	var child, el, val, i, tagname;
	child = node.firstChild;
	while (child) {
		if (child.nodeType == AJAX.Constants.nodeTypes.ELEMENT_NODE) {
			el = document.getElementsByName(child.getAttribute('prefix') + child.nodeName);
			if (el) {
				val = child.firstChild ? child.firstChild.nodeValue : '';
				if (el.length == 1) {
					tagname = el[0].tagName.toLowerCase();
					if (tagname == 'select') {
						el[0].selectedIndex = -1;
						for (var i = 0; i < el[0].length; i++) {
							if (el[0].options[i].value == val || el[0].options[i].text.toLowerCase() == val.toLowerCase()) {
								el[0].selectedIndex = i;
								break;
							}
						}
					} else if (tagname == 'input' && /^(radio|checkbox)$/.test((el[0].type ? el[0].type.toLowerCase() : ''))) {
						el[0].checked = val == 1 || AJAX.Utils.stringToXMLValue(val, 'boolean');
					} else {
						el[0].value = val;
					}
				} else { // it's an checkbox- or radiogroup
					val = ',' + val + ',';
					for (i = 0; i < el.length; i++) {
						el[i].checked = new RegExp(',' + AJAX.Utils.stringToXMLValue(el[i].value, child.getAttribute('datatype')) + ',').test(val);
					}
				}
			}
		}
		child = child.nextSibling;
	}
}

AJAX.Utils.stringToXMLValue = function(str, datatype) {
	switch (datatype) {
		case 'string': return str; break;
		case 'boolean': return (str == '1' || str.toLowerCase() == 'on' || str.toLowerCase() == '.t.' ? 1 : 0); break;
		case 'number': return String.create(str).float(); break;
		case 'integer': return Number.int(str); break;
	}
	return str;
}

AJAX.Utils.stringToRegExp = function(str) {
	var re = new RegExp('([\\\(\\\[\\\{])', 'gm');
	str = str.replace(re, '\\\$1');
	re = new RegExp('([\\\)\\\]\\\}\\\?])', 'gm');
	return str.replace(re, '\\\$1');
}

// AJAX Form methods specific for Formdesk form customization
AJAX.Form = new Object();
AJAX.Form.progress = false;
AJAX.Form.progress_counter = 0;

/**
Creates a data object to hold the information needed for a specific lookup block.
This object or it's identifier is passed to the AJAX.Form.getData function.
This object must be populated with the following information:
- data.url_part
Url to call (the search string will be appended to this url)
- data.selector
An object which holds the listbox en optional the fieldname in the xml result when this name differs from the element-name
- data.form
An object created with AJAX.Utils.createForm. This object holds the fields and optional mappings to populate with the result data.
**/
AJAX.Form.create = function(identifier, url_part, form_element_list, selector) {
	var data = {
		ready: false,
		error: false,
		errorCode: 0,
		identifier: identifier,
		progress: typeof oProgress != 'undefined', // if the progressbar is available then use it
		form: null,
		search: '',
		reset: function() {
			this.ready = this.error = false; this.errorCode = 0;
		}
	}
	if (url_part) data.url_part = url_part;
	if (form_element_list) data.form = AJAX.Utils.createForm(form_element_list);
	if (selector) {
		if (typeof selector == 'object') data.selector = selector;
		else {
			aEl = selector.split('|');
			data.selector = { element: $(aEl[0]) };
			if (aEl.length > 1) data.selector.mapping = aEl[1];
			if (aEl.length > 2) data.selector.empty_option = aEl[2] == 'true'
		}
	}
	this[identifier] = data;
	return data;
}

AJAX.Form.Task = {
	timerID: 0, data: null, req: null, ready: false,
	create: function(action, id_attribute) {
		var data = AJAX.Form.create(action, 'data/' + action + '/' + (typeof (top.Global.Id_Form) != 'undefined' ? top.Global.Id_Form : 0) + '/' + (id_attribute ? id_attribute + '/' : ''));
		data.async = data.lock = data.silent = true /* don't empty the fields */;
		return data;
	},
	clearTimer: function() {
		if (this.timerID > 0) {
			window.clearTimeout(this.timerID);
			this.timerID = 0;
		}
	},
	call: function() {
		this.req = null;
		if (this.data) {
			this.req = AJAX.Form.getData(this.data, null);
		}
		return this.data;
	},
	save: function(data, async, form, single, compare, sync) {
		if (typeof data == 'string') {
			// data parameter contains the name, so get the object
			data = AJAX.Form[data];
		}
		//this.clearTimer();
		data.ready = data.getonly = this.ready = false;
		if (!form) form = document.builder;
		if (!async && !sync) {
			top.AJAX.Form.Task.save(data, true, AJAX.Utils.collectFormData(form, true), single, compare);
			return;
		}
		/*
		the call is asynchrone. this means that there can be simultaneous calls to save data.
		to prevent simultaneous use of the data object we clone the object
		*/
		var clone = {
			identifier: data.identifier,
			url_part: data.url_part,
			form: null,
			form_org: form,
			content: (typeof form == 'string' ? form : AJAX.Utils.collectFormData(form, true)),
			verb: 'POST',
			silent: true,
			reset: function() {
				this.ready = this.error = false; this.errorCode = 0;
			},
			async: async,
			search: 'save' + (single ? '?single=true&' : '?') + 'silent=' + (top.Global.Silent ? true : false)
		}
		data.content = clone.content;
		if (compare && clone.content == compare) {
			return;
		}
		if (async || sync) {
			this.data = clone;
			clone.callback = function(req, data) {
				AJAX.Form.setData(req, data);
				if (data.ready) {
					AJAX.Form.Task.ready = true;
					if (FDEvents) FDEvents.FireEvent('AJAXAfterTaskSave', { req: req, data: data });
				}
			};
			this.call();
		} else {
			clone.callback = function(req, data) {
				AJAX.Form.setData(req, data);
				if (data.ready) {
					AJAX.Form.Task.ready = true;
					if (FDEvents) FDEvents.FireEvent('AJAXAfterTaskSave', { req: req, data: data });
				}
			};
			top.AJAX.Form.getData(clone, null);
		}
	},
	single: function(data, el) {
		var form = { elements: new Array(el) };
		this.save(data, true, form, true);
	},
	get: function(data, async, url_params, getonly) {
		if (typeof data == 'string') {
			// data parameter contains the name, so get the object
			var str = data;
			data = AJAX.Form[data];
			if (!data) data = AJAX.Form.Task.create(str);
		}
		//this.clearTimer();
		data.ready = this.ready = false;
		async = (arguments.length == 1 || async);
		data.verb = 'GET';
		data.search = 'get';
		data.async = async;
		if (url_params) data.search += ('?' + url_params);
		data.getonly = getonly || false;
		data.callback = function(req, data) {
			AJAX.Form.setData(req, data);
			if (data.ready) {
				AJAX.Form.Task.ready = true;
				if (FDEvents) FDEvents.FireEvent('AJAXAfterTaskGet', { req: req, data: data });
				top.showLoad(true);
			}
		};
		this.data = data;
		data.showLoad = top.showLoad;
		if (data.async) {
			this.timerID = window.setTimeout('AJAX.Form.Task.call()', 0);
		} else {
			return this.call();
		}
		/*
		var req = AJAX.Form.getData( data, null ) ;
		if ( !async ) {
		if ( req && req.readyState == AJAX.Constants.readyState.COMPLETED && req.status == 200 ) return req ;
		else return null ;
		}
		*/
	},
	check: function(data, value, url_params, return_type) {
		AJAX.setBusy(true);

		if (typeof data == 'string') {
			// data parameter contains the name, so get the object
			var str = data;
			data = AJAX.Form[data];
			if (!data) data = AJAX.Form.Task.create(str);
		}
		this.clearTimer();
		data.ready = this.ready = false;
		data.verb = 'GET';
		data.search = 'check?value=' + value;
		data.async = false;
		data.callback = AJAX.Form.setData;
		if (url_params) data.search += ('&' + url_params);
		data.getonly = true;
		this.data = data;
		this.call();
		if (data.ready && data.recordset.length == 1) {
			AJAX.setBusy(false);
			return AJAX.getNodeValue(data.recordset.item(0), 'value', '', return_type || 'boolean');
		}
		AJAX.setBusy(false);

		return true;
	}
}

/**
Lookup function
**/
AJAX.Form.getData = function(data, search, initValue) {

	if (typeof data == 'string') {
		// data parameter contains the name, so get the object
		data = this[data];
	}

	data.reset();

	if (typeof initValue == 'boolean' && initValue) {
		data.lock = true;
		initValue = null;
	}

	data.initValue = (arguments.length >= 3 ? initValue : null);
	if (data.initValue && data.initValue.match(/^\[[^\[\]]*\]$/))
		data.initValue = null;

	if ((this.progress || data.progress) && (search || search == null)) { // progressbar is defined and there is something to lookup
		if (AJAX.Form.progress_counter <= 0) {
			// initialise and show the progressbar
			oProgress.init(50, '', '', AJAX.Texts.get('progress', 'Requesting data . . .'));
			oProgress.setStyle();
			oProgress.loop(10);
		}
		AJAX.Form.progress_counter++;
	} else if (data.showLoad) { data.showLoad(); }

	if (data.selector) { // there is listbox selection element defined
		// set the listbox to zero length
		data.selector.element.length = 0;
		if (typeof data.selector.empty_option == 'undefined' || data.selector.empty_option) {
			// add a first option
			addOption(document, data.selector.element, '', AJAX.Texts.get('selector_option', '-> Select an option <-'));
		}
	}

	if (!data.lock && data.initValue == null) {
		// empty the fields which will be populated with data
		AJAX.Form.fillData(data.form, null);
	}

	data.recordset = null; // clear previous recordset
	data.results = null; // clear previous results
	if (search != null) {
		data.search = (isArray(search) ? search : URLEncode(search || '')); // save the search string/array
	}

	// call the registerd events with which you can do some more preparing work
	if (FDEvents) FDEvents.FireEvent('AJAXBeforeRequest', { data: data });

	var req = null;
	if (search || search == null) {
		// compose the url with the url_part + the search string
		if (isArray(data.search)) {
			var params = '';
			for (var i = 0; i < data.search.length; i = i + 2) {
				params = params + '&' + data.search[i] + '=' + URLEncode(data.search[i + 1]);
			}
			data.url = data.url_part + '?' + params.substr(1);
		} else {
			data.url = data.url_part + data.search;
		}
		data.url += (data.url.search(/\?/) >= 0 ? '&' : '?') + (!data.cache ? 'v' + FD.Utils.Random() : top.Global.Version.all + '&cache=true');
		if (!data.callback) { // if the callback function is not yet set
			data.async = true; // the call will be asynchrone (something else can be done in meanwhile)
			data.callback = AJAX.Form.setData; // set the callback function
		}
		//alert(data.url);
		try { req = AJAX.sendRequest(data); /* send the request */ } catch (e) { alert(e.message ? e.message : e); };
		if (!req) { // the request is unsuccessful
			data.error = true;
			if (data.progress) {
				AJAX.Form.progress_counter--;
				if (AJAX.Form.progress_counter <= 0) oProgress.finish(0); // clear the progressbar
			} else if (data.showLoad) { data.showLoad(true); }
			// alert the user
			alert(AJAX.Texts.get('error_send_request', 'Error sending request!'));
		}

	}

	return req;

}

/**
Callback funtion.
This function is called when the request status changes.
**/
AJAX.Form.setData = function(req, data) {

	data.ready = req.readyState == AJAX.Constants.readyState.COMPLETED && req.status == 200;
	if (data.ready) {

		if (typeof FD.Form != 'undefined') {
			var prevLoading = FD.Form.isLoading;
			FD.Form.isLoading = false;
		}
		//alert(req.responseText);
		data.text = req.responseText;
		data.results = req.responseXML; // store the xml results in the data object

		if ((req.responseXML.parseError && req.responseXML.parseError.errorCode != 0) || req.responseXML.getElementsByTagName('parsererror').length > 0) {
			var str = req.responseText.replace(/&/g, '&amp;');
			data.results = AJAX.stringToXMLDoc(str);
			if ((data.results.parseError && data.results.parseError.errorCode != 0) || data.results.getElementsByTagName('parsererror').length > 0) {
				data.error = true;
				if (data.results.parseError)
					alert('XML Parse Error: ' + data.results.parseError.reason);
				else {
					alert(data.results.getElementsByTagName('parsererror').item(0).childNodes.item(0).nodeValue);
				}
			}
		}

		// select the Recordset node from the xml
		var root = data.root = data.results.selectSingleNode('//Recordset');
		if (!root) root = data.root = data.results.selectSingleNode('//result');
		var recordset = data.recordset = (root ? root : data.results).selectNodes('//Record'); //.item( 0 ) ;
		if (typeof (root) == 'object' && root.nodeName == 'result' && (data.errorCode = Number.int(AJAX.getNodeValue(root, 'code'))) != 0) {
			data.error = true;
			data.recordset = null;
			var msg = AJAX.getNodeValue(root, 'error_message_extended');
			if (!msg) msg = AJAX.getNodeValue(root, 'error_message');
			alert(msg);
		} else if (!recordset || !recordset.length) { // there is no data; .hasChildNodes()

			data.recordset = null;
			if (data.selector) {
				// disable the selection listbox
				setDisabled(null, data.selector.element, true, true, true);
			}
			if (!data.silent) {
				// alert the user
				alert(AJAX.Texts.get('alert_nodata', 'No data found!'));
			}

		} else if (data.selector) { // there is a data selection listbox

			/*
			This data selection listbox will be populated first.
			After the user selects an option from the listbox the AJAX.Form.selectData function will populate the given fields.
			*/

			// enable the listbox
			setDisabled(null, data.selector.element, false, true, true);

			// use the mapping field-name if defined. the elements name will be used otherwise.
			var field = (data.selector.mapping ? data.selector.mapping : data.selector.element.name).toLowerCase()
			AJAX.Form.fillSelector(data.selector.element, recordset, field);

			/*
			add the identifier of the data object to the listbox property
			the AJAX.Form.selectData is then able to identify the corresponding data object to select the results
			*/
			data.selector.element.setAttribute('identifier', data.identifier);
			if (data.selector.element.onchange && !data.selector.element.prevOnchange) {
				// save the already defined onchange event
				data.selector.element.prevOnchange = data.selector.element.onchange;
			}
			//if (data.form) {
				// set the onchange event to call the AJAX.Form.selectData funtion
				data.selector.element.onchange = function() { AJAX.Form.selectData(this); };
			//}
			if (data.initValue != null && data.initValue) {

				data.selector.element.value = data.initValue;
				if (data.selector.element.selectedIndex == -1) data.selector.element.selectedIndex = 0;

			} else if (data.selector.element.length == 2 || (typeof data.selector.empty_option != 'undefined' && !data.selector.empty_option)) {
				/*
				if there is one result (first option + one result option)
				then select this option in the listbox and populate the fields right away
				*/
				data.selector.element.selectedIndex = (typeof data.selector.empty_option == 'undefined' || data.selector.empty_option ? 1 : 0);
				AJAX.Form.selectData(data.selector.element);
			}

		} else if (!data.getonly) {

			// no selection listbox, so populate the fields
			AJAX.Form.fillData(data.form, recordset.item(0)); //.firstChild

			if (!data.silent && Number.int(recordset.item(0).getAttribute('empty')) > 0) {
				// alert the user
				alert(AJAX.Texts.get('alert_nodata', 'No data found!'));
			}

		}

		if (FDEvents) FDEvents.FireEvent('AJAXAfterSetData', { data: data });

		if (this.progress || data.progress) {
			AJAX.Form.progress_counter--;
			if (AJAX.Form.progress_counter <= 0) oProgress.finish(0); // clear the progressbar
		} else if (data.showLoad) { data.showLoad(true); }

		if (Number.int(root.getAttribute('hasmsg')) > 1) {
			try {
				top.Global.hasMessage = true;
				top.Global.showMessage(top);
			} catch (e) { }
		}

		if (typeof FD.Form != 'undefined') FD.Form.isLoading = prevLoading;

	} else if (req.readyState == AJAX.Constants.readyState.COMPLETED) {

		data.error = true;

		/*
		the request is ready but there something wrong with the status
		*/

		if (FDEvents) FDEvents.FireEvent('AJAXAfterSetData', { data: data });

		if (this.progress || data.progress) {
			AJAX.Form.progress_counter--;
			if (AJAX.Form.progress_counter <= 0) oProgress.finish(0); // clear the progressbar
		} else if (data.showLoad) { data.showLoad(true); }
		// alert the user
		alert(AJAX.Texts.get('error_request', 'Error requesting data!') + '(' + req.status + ',' + data.url + ')');

	}

	data.init = false;

}

/**
This function populates a listbox with values from xml records
**/
AJAX.Form.fillSelector = function(selector, recordset, field, empty, first_option, value) {

	if (empty) selector.length = 0;
	if (first_option) addOption(document, selector, '', AJAX.Texts.get('selector_option', '-> Select an option <-'));

	var val;
	for (var i = 0; i < recordset.length; i++) { // skip through the records (nodes)

		val = AJAX.getNodeValue(recordset.item(i), field); // get the value from the record
		// add an option the the listbox with this value
		addOption(document, selector, val, val);

	}

	if (arguments.length >= 6 && value) {
		selector.value = value;
		if (selector.selectedIndex == -1) selector.selectedIndex = 0;
	} else selector.selectedIndex = 0;

}

/**
This function populate the fields corresponding to the selected option from the listbox
Selector parameter is the listbox itself
**/
AJAX.Form.selectData = function(selector) {
	var self = AJAX.Form, ident = selector.getAttribute('identifier'), data = self[ident]; // data object
	if (data && data.recordset != null) {
		// get the recordset
		//var recordset = data.results.selectNodes('//Recordset/Record'); //.item( 0 ) ;
		try {
			// try to populate the fields
			AJAX.Form.fillData(data.form, data.recordset.item(selector.selectedIndex - (typeof data.selector.empty_option == 'undefined' || data.selector.empty_option ? 1 : 0)));
		} catch (e) {
			AJAX.Debug.sendError('AJAX.Form.selectData', '(1,' + ident + ',value=' + (selector ? selector.value : '<no selector>') + ') ' + (typeof e.message != 'undefined' ? e.message : e));
			// alert the user
			alert(AJAX.Texts.get('error_selector', 'Error selecting data!\nData not found.'));
		}
		// call the registered events
		if (FDEvents) FDEvents.FireEvent('AJAXAfterSelectorChange', { data: data, selector: selector });
	} else {
		AJAX.Debug.sendError(
			'AJAX.Form.selectData',
			'(2,' + ident + ',value=' + (selector ? selector.value : '<no selector>') + ') data: ' + typeof (data) + ', form: ' + (typeof (data) != 'undefined' ? typeof (data.form) : '<none>') + ', rec: ' + (typeof (data) != 'undefined' ? typeof (data.recordset) + '(isnull:' + (data.recordset == null) + ')' : '<none>')
		);
	}
	if (self.prevOnchange) {
		// call the saved previous onchange event
		self.prevOnchange();
	}
}

/**
This function populates the fields
**/
AJAX.Form.fillData = function(form, node) {
	var fld, el, child;
	//	alert(node.xml);
	if (!form) { // fills the data based on the childNodes collection
		if (!node) return;
		for (var i = 0; i < node.childNodes.length; i++) {
			if (node.childNodes[i].nodeType == 1 /* ELEMENT_NODE */) {
				child = node.childNodes[i];
				this.traverseData(node, child, '');
				/*
				if (child.getAttribute('prefix')) {
					name = child.getAttribute('prefix') + child.nodeName.charAt(0).toUpperCase() + child.nodeName.substr(1);
				} else {
					name = child.nodeName;
				}
				els = document.getElementsByName(name); // returns a collection/array with element(s)
				el = [];
				if (els) {
					for (var j = 0; j < els.length; j++) {
						if (inList(els[j].tagName.toLowerCase(), 'input', 'textarea', 'select')) {
							el.push(els[j]);
						}
					}
					els = null;
				}
				if (el.length == 1) {
					this.fillField(el[0], node, child.nodeName);
				} else if (el.length > 0) { // checkbox-/radiogroup
					this.fillField(el, node, child.nodeName);
				}
				*/
			}
		}
	} else { // fills the data based on the form parameter object (AJAX.Form.create)
		for (var i = 0; i < form.elements.length; i++) {
			//			alert(form.elements[i].name + ', ' + form.mapping[ form.elements[i].name ] ) ;
			fld = (typeof form.mapping[form.elements[i].name] != 'undefined' ? form.mapping[form.elements[i].name] : form.elements[i].name).toLowerCase();
			this.fillField(form.elements[i], node, fld);
		}
	}
}
AJAX.Form.traverseData = function(parent, node, nodeName) {
	nodeName = nodeName + (nodeName != '' ? '/' : '') + node.nodeName;
	//alert(nodeName);
	var items = node.selectNodes('./*');
	if (items.length > 0) {
		for (var i = 0; i < items.length; i++) {
			this.traverseData(node, items[i], nodeName);
		}
	} else {
		if (node.getAttribute('prefix')) {
			name = node.getAttribute('prefix') + node.nodeName.charAt(0).toUpperCase() + node.nodeName.substr(1);
		} else {
			name = nodeName;
		}
		els = document.getElementsByName(name); // returns a collection/array with element(s)
		el = [];
		if (els) {
			for (var j = 0; j < els.length; j++) {
				if (inList(els[j].tagName.toLowerCase(), 'input', 'textarea', 'select')) {
					el.push(els[j]);
				}
			}
			els = null;
		}
		if (el.length == 1) {
			this.fillField(el[0], parent, node.nodeName);
		} else if (el.length > 0) { // checkbox-/radiogroup
			this.fillField(el, parent, node.nodeName);
		}
	}
}
AJAX.Form.fillField = function(el, node, fld) {
	var type = (typeof el.type != 'undefined' ? el.type.toLowerCase() : ''), value = '';
	if ((typeof el.tagName == 'undefined' || el.tagName.toLowerCase() != 'select') && el.length > 0 && typeof el[0] != 'undefined') {
		var values = ',' + (node ? AJAX.getNodeValue(node, fld) : '') + ',', el_values = [], alt, altstr = '';
		values = values.replaceAll('<=>', ',');
		for (var i = 0; i < el.length; i++) {
			value = AJAX.Utils.stringToRegExp(el[i].value);
			el_values.push(el[i].value);
			el[i].checked = values.search(',' + value + ',') > -1;
			if (el[i].checked) values = values.replace(',' + el[i].value, '');
		}
		if ((alt = $('alt' + el[el.length - 1].id)) && values != ',') {
			// an alternative option is available. check if there is a value which belongs to this option
			values = values.substr(1, values.length - 2);
			if (values) {
				el[el.length - 1].checked = true;
				setDisabled(null, alt, false, true);
			}
			alt.value = values;
		}
	} else if (el.tagName.toLowerCase() == "input" && /^(radio|checkbox)$/.test(type)) {
		//if (el.name=='required') alert(el.value + ', nodeVal: '+fld+',' + AJAX.getNodeValue( node, fld ) );
		el.checked = (node && (node && el.value == AJAX.getNodeValue(node, fld) || (el.value.toLowerCase() == 'on' && AJAX.getNodeValue(node, fld).toLowerCase() == 'true')));
	} else if (el.tagName.toLowerCase() == 'select') {
		var val = (node ? AJAX.getNodeValue(node, fld) : '');
		el.selectedIndex = -1;
		for (var j = 0; j < el.length; j++) {
			//alert(form.elements[i].options[j].value+', '+form.elements[i].options[j].text);
			if (el.options[j].value == val || el.options[j].text.toLowerCase() == val.toLowerCase()) {
				el.selectedIndex = j;
				break;
			}
		}
	} else {
		el.value = (node ? AJAX.getNodeValue(node, fld) : '');
	}
	if (typeof ev != 'undefined') ev((typeof el[0] != 'undefined' ? el[0].name : el.name), null);
}

AJAX.Form.getList = function(list, url_part, initValue) {
	aEl = list.split('|');
	var data = this.create(aEl[0]);
	data.url_part = url_part;
	data.search = '';
	data.selector = { element: getElement(document, aEl[0]) };
	if (aEl.length > 1) data.selector.mapping = aEl[1];
	this.getData(data, null, initValue);
}

// AJAX Text
AJAX.Texts = new Object();
AJAX.Texts.texts = new Object();
AJAX.Texts.defaults = new Object();
AJAX.Texts.defaults['NL'] = {
	progress: 'Ophalen gegevens. . .',
	selector_option: '-> Maak een keuze <-',
	error_send_request: 'Er is een fout opgetreden bij het opvragen van de gegevens!',
	error_request: 'Er kunnen geen gegevens opgehaalt worden!',
	alert_nodata: 'Er zijn geen gegevens gevonden!',
	error_selector: 'Er is een fout opgetreden bij het selecteren van de gegevens!\nDe gegevens kunnen niet worden gevonden.'
}
AJAX.Texts.set = function(texts) {
	for (key in texts) {
		this.texts[key] = texts[key];
	}
}
AJAX.Texts.get = function(key, alt) {
	return (this.texts[key] ? this.texts[key] : (alt ? alt : ''));
}


// AJAX Debug methods
AJAX.Debug = new Object();
AJAX.Debug.enableTrace = true;
AJAX.Debug.debugWindow = null;

AJAX.Debug.createDebugWindow = function() {
	if (!AJAX.Debug.enableTrace || AJAX.Debug.debugWindow)
		return;
	try {

		var div = AJAX.Debug.debugWindow = document.createElement("DIV");
		div.style.fontSize = "12px";
		div.style.fontFamily = "console";
		div.style.position = "absolute";
		div.style.width = "400px";
		div.style.height = "300px";
		div.style.overflow = "auto";
		div.style.border = "solid 1px black";
		div.style.backgroundColor = "white";
		div.style.color = "black";
		div.style.bottom = "0px";
		div.style.right = "0px";
		// div.style.opacity = "0.5";
		// div.style.filter = "alpha(opacity=50)";
		div.setAttribute("id", "SpryDebugWindow");
		document.body.appendChild(AJAX.Debug.debugWindow);
	} catch (e) { }
};

AJAX.Debug.debugOut = function(method, str, bgColor) {
	if (!AJAX.Debug.debugWindow) {
		AJAX.Debug.createDebugWindow();
		if (!AJAX.Debug.debugWindow)
			return;
	}

	var d = document.createElement("DIV");
	if (bgColor)
		d.style.backgroundColor = bgColor;
	d.innerHTML = method + ' -> ' + str;
	AJAX.Debug.debugWindow.appendChild(d);
};

AJAX.Debug.trace = function(method, str) {
	AJAX.Debug.debugOut(method, str);
};

AJAX.Debug.reportError = function(method, str) {
	alert(method + ', ' + str);
	AJAX.Debug.debugOut(method, str, "red");
};

AJAX.Debug.sendError = function(method, str) {
	try {
		AJAX.sendRequest({ url: '/error.asp?xml=true&method=' + method + '&msg=' + str, async: true, callback: function(req, data) { ; } });
	} catch (e) { }
}

function XMLDummyNode(value) {
	this.value = value;
}
XMLDummyNode.prototype.selectSingleNode = function(name) { return { firstChild: { nodeValue: this.value } }; }

/* XMLNodeList; emulates a collection object */

function XMLNodeList(i) {
	this.length = i;
}
XMLNodeList.prototype = new Array(0);
/** Inherit the Array constructor */
XMLNodeList.prototype.constructor = Array;
/**
* Returns the node at the specified index or null if the given index
* is greater than the list size or less than zero
* Note that in ECMAScript you can also use the square-bracket
* array notation instead of calling <code>item</code>
* @argument i the index of the member to return
* @returns the member corresponding to the given index
*/
XMLNodeList.prototype.item = function(i) {
	return (i < 0 || i >= this.length) ? null : this[i];
};
/**
* Emulate IE's expr property
* (Here the XMLNodeList object is given as the result of selectNodes).</p>
* @returns the XPath expression passed to selectNodes that resulted in
* this XMLNodeList
*/
XMLNodeList.prototype.expr = "";


/* XPath IE emulation */
if (document.implementation && document.implementation.hasFeature("XPath", "3.0")) {


	/** dummy, used to accept IE's stuff without throwing errors */
	if (window.XMLDocument && (!XMLDocument.prototype.setProperty)) {
		XMLDocument.prototype.setProperty = function(x, y) { };
	};

	XMLDocument.prototype.selectNodes = function(cXPathString, xNode) {
		if (!xNode) { xNode = this; }

		var oNSResolver = this.createNSResolver(this.documentElement);
		var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
		var oNodeList = new XMLNodeList(aItems.snapshotLength);
		for (var i = 0; i < aItems.snapshotLength; i++) {
			oNodeList[i] = aItems.snapshotItem(i);
		}

		return oNodeList;
	}
	XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode) {
		if (!xNode) { xNode = this; }

		var oNodeList = this.selectNodes(cXPathString, xNode);
		if (oNodeList.length > 0) {
			return oNodeList.item(0);
		} else {
			return null;
		}
	}

	Element.prototype.selectNodes = function(cXPathString) {
		if (this.ownerDocument.selectNodes) {
			return this.ownerDocument.selectNodes(cXPathString, this);
		} else { throw "For XML Elements Only"; }
	}

	Element.prototype.selectSingleNode = function(cXPathString) {
		if (this.ownerDocument.selectSingleNode) {
			return this.ownerDocument.selectSingleNode(cXPathString, this);
		} else { throw "For XML Elements Only"; }
	}

}