/**
 * Contains the class Sitebox.Framework.Ajax.
 *
 * @copyright		2000-2006 CARE Internet Services B.V. All Rights Reserved
 * @internal		$Workfile: Ajax.js$
 * @internal		$Date: 03-08-07 08:19:15 AM$
 * @author			$Author: robert$
 * @version			$Revision: 1$
 *
 * $NoKeyword$
 */

/**
 * Class for creating handling an AJAX request
 */
Sitebox.Framework.Ajax = function()
{
	this._url;
	this._async = new Sitebox.Framework.Asynchronous();
	this._json = new Array();
	this._cacheExists = (typeof(Sitebox.Framework.Ajax.Cache) == 'undefined') ? false : true;
}; // class Sitebox.Framework.Ajax

/**
 * Array of JSONs
 */
Sitebox.Framework.Ajax.json = new Array();

/**
 * Send the AJAX request.
 *
 * Available options:
 * - method
 * - content
 * - callbacks
 *   - before
 *   - loading
 *   - loaded
 *   - interactive
 *   - complete
 *   - success
 *   - notfound
 *   - failure
 *   - after
 * - showProgressIndicator
 * - progressIndicatorText
 *
 * @param	string	url
 * @param	object	options
 */
Sitebox.Framework.Ajax.prototype.call = function(url, options)
{
	/* save URL for caching */
	this._url = url;

	/* Parse options. */
	var method = 'GET';
	var content = null;
	if (options)
	{
		if (options.showProgressIndicator)
		{
			var text = 'Please wait...';
			if (options.progressIndicatorText)
			{
				text = options.progressIndicatorText
			}
			this._async.addCallbackBefore(function(e) { Sitebox.showProgressIndicator(text); });
			this._async.addCallbackComplete(Sitebox.hideProgressIndicator);
		}
		if (options.method)
		{
			method = options.method;
		}
		if (options.content)
		{
			content = options.content;
		}
	}

	/* Check if the request is cached. Not for POST requests. */
	if (this._cacheExists && method != 'POST')
	{
		var eTag = Sitebox.Framework.Ajax.Cache.getETag(this._url);
		if (eTag != null)
		{
			Sitebox.logDebug('Sitebox.Framework.Ajax.call(): Request was cached, doing a request to see if it is still valid.');
			this._async.addHeader('If-None-Match', eTag);

			/* Add the 'not modified' callback for caching */
			this._async.addCallbackNotModified(bind(this.parseFromCache, this));
		}
	}

	/* Always add the parse callback. */
	this._async.addCallbackSuccess(bind(this.parseResponse, this));

	/* Add callbacks from the options. */
	if (options && options.callbacks)
	{
		if (options.callbacks.before)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.before))
			{
				options.callbacks.before = new Array(options.callbacks.before);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.before)
			{
				var func = options.callbacks.before[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackBefore(func);
			}
		}
		if (options.callbacks.loading)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.loading))
			{
				options.callbacks.loading = new Array(options.callbacks.loading);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.loading)
			{
				var func = options.callbacks.loading[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackLoading(func);
			}
		}
		if (options.callbacks.loaded)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.loaded))
			{
				options.callbacks.loaded = new Array(options.callbacks.loaded);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.loaded)
			{
				var func = options.callbacks.loaded[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackLoaded(func);
			}
		}
		if (options.callbacks.interactive)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.interactive))
			{
				options.callbacks.interactive = new Array(options.callbacks.interactive);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.interactive)
			{
				var func = options.callbacks.interactive[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackInteractive(func);
			}
		}
		if (options.callbacks.complete)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.complete))
			{
				options.callbacks.complete = new Array(options.callbacks.complete);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.complete)
			{
				var func = options.callbacks.complete[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackComplete(func);
			}
		}
		if (options.callbacks.success)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.success))
			{
				options.callbacks.success = new Array(options.callbacks.success);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.success)
			{
				var func = options.callbacks.success[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackSuccess(func);
			}
		}
		if (options.callbacks.notfound)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.notfound))
			{
				options.callbacks.notfound = new Array(options.callbacks.notfound);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.notfound)
			{
				var func = options.callbacks.notfound[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackNotFound(func);
			}
		}
		if (options.callbacks.failure)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.failure))
			{
				options.callbacks.failure = new Array(options.callbacks.failure);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.failure)
			{
				var func = options.callbacks.failure[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackFailure(func);
			}
		}
		if (options.callbacks.after)
		{
			/* make sure the callbacks are an array */
			if (!isArrayLike(options.callbacks.after))
			{
				options.callbacks.after = new Array(options.callbacks.after);
			}
			/* loop through the callback and add them */
			for (i in options.callbacks.after)
			{
				var func = options.callbacks.after[i];
				/* if callback is a string, eval it */
				if (typeof(func) == 'string')
				{
					eval('var func = ' + func);
				}
				this._async.addCallbackAfter(func);
			}
		}
	}

	/* Do request. */
	this._async.call(this._url, method, content);
}; // function Sitebox.Framework.Ajax::call

/**
 * Loads a AJAX/JSON document.
 *
 * @param	string	url
 * @param	object	options	See Sitebox.Framework.Ajax::call for the options.
 */
Sitebox.Framework.Ajax.prototype.loadJson = function(url, options)
{
	this.call(url, options);
}; // function Sitebox.Framework.Ajax::loadJson

/**
 * Handles the XML response, parses it, and possibly add it to the Ajax cache.
 *
 * @param	XMLHttpRequest 	res
 * @param	boolean			useCache
 */
Sitebox.Framework.Ajax.prototype.parseResponse = function(res, useCache)
{
	/* call the internal parse function to parse the response XML */
	if (this.parse(res.responseXML))
	{
		/* if parsing succeeded, add this response to the cache  */
		if (this._cacheExists && (typeof(useCache) == 'undefined' || useCache == true))
		{
			var eTag = res.getResponseHeader('ETag');
			if (eTag != null)
			{
				Sitebox.logDebug('Sitebox.Framework.Ajax.parseResponse(): adding the response to the cache.');
				Sitebox.Framework.Ajax.Cache.addRequest(this._url, eTag, res);
			}
		}
	}
	else
	{
		/* if parsing failed, add the response text to the debug panel */
		Sitebox.logDebug(res.responseText);
	}
}; // function Sitebox.Framework.Ajax::parseResponse

/**
 * Parses an Ajax XML document.
 *
 * @param	XMLDocument 	xml
 */
Sitebox.Framework.Ajax.prototype.parse = function(xml)
{
	/* Check if it is a valid XMLDocument. */
	if (!xml)
	{
		Sitebox.logError('Sitebox.Framework.Ajax.parse(): no valid XML.');
		return false;
	}
	if (xml.childNodes && xml.childNodes[0] && xml.childNodes[0].nodeName == 'ajax')
	{
		var root = xml.childNodes[0];
	}
	else if (xml.childNodes && xml.childNodes[0] && xml.childNodes[0].nodeName == 'xml' && xml.childNodes[1] && xml.childNodes[1].nodeName == 'ajax')
	{
		var root = xml.childNodes[1];
	}
	else
	{
		Sitebox.logError('Sitebox.Framework.Ajax.parse(): missing <ajax></ajax> tag');
		return false;
	}

	/* variable to check if an error occured */
	var hasError = false;

	/* Loop through the Ajax nodes. */
	for (var nodeKey = 0; nodeKey < root.childNodes.length; nodeKey++)
	{
		try
		{
			var node = root.childNodes[nodeKey];
			switch (node.nodeName)
			{
				case 'title':
					var value = node.childNodes[0].nodeValue;
					document.title = value;
					break;
				case 'set':
					var elementId = getNodeAttribute(node, 'element');
					var attribute = getNodeAttribute(node, 'attribute');
					var value = node.childNodes[0].nodeValue;
					if (value == 'true' || value == 'false')
					{
						eval('$(elementId).'+attribute+'='+value+';');
					}
					else
					{
						eval('$(elementId).'+attribute+'= value;');
					}
					break;
				case 'prepend':
					var elementId = getNodeAttribute(node, 'element');
					var attribute = getNodeAttribute(node, 'attribute');
					var value = node.childNodes[0].nodeValue;
					var oldValue = getNodeAttribute(elementId, attribute);
					setNodeAttribute(elementId, attribute, value + oldValue);
					break;
				case 'append':
					var elementId = getNodeAttribute(node, 'element');
					var attribute = getNodeAttribute(node, 'attribute');
					var value = node.childNodes[0].nodeValue;
					var oldValue = getNodeAttribute(elementId, attribute);
					setNodeAttribute(elementId, attribute, oldValue + value);
					break;
				case 'replace':
					var elementId = getNodeAttribute(node, 'element');
					var attribute = getNodeAttribute(node, 'attribute');
					var oldValue = getNodeAttribute(elementId, attribute);
					eval('var searchReplace = ' + node.childNodes[0].childNodes[0].nodeValue);
					var newValue = oldValue.replace(searchReplace.search, searchReplace.replace);
					setNodeAttribute(elementId, attribute, newValue);
					break;
				case 'create-element':
					var parentId = getNodeAttribute(node, 'parent');
					var tag = getNodeAttribute(node, 'tag');
					var id = getNodeAttribute(node, 'id');
					appendChildNodes(parentId, createDOM(tag, {'id': id}));
					break;
				case 'set-element-content':
					var elementId = getNodeAttribute(node, 'element');
					var content = node.childNodes[0].nodeValue;
					eval('$(elementId).innerHTML = content;');
					break;
				case 'add-element-content':
					var elementId = getNodeAttribute(node, 'element');
					var position = getNodeAttribute(node, 'position');
					var content = node.childNodes[0].nodeValue;

					/* No ideal solution, but now it at least works. */
					var temp = DIV();
					temp.innerHTML = content;
					if (position == 'before' && $(elementId).childNodes.length > 0)
					{
						$(elementId).insertBefore(temp, $(elementId).childNodes[0]);
					}
					else
					{
						appendChildNodes($(elementId), temp);
					}

					/* Not working, when the innerHTML is replaced, all events are lost. */
					/*if (position == 'before' && $(elementId).childNodes.length > 0)
					{
						eval('$(elementId).innerHTML = content + $(elementId).innerHTML;');
					}
					else
					{
						eval('$(elementId).innerHTML = $(elementId).innerHTML + content;');
					}*/

					break;
				case 'set-body-content':
					var content = node.childNodes[0].nodeValue;
					eval('currentDocument().body.innerHTML = content;');
					break;
				case 'add-body-content':
					var position = getNodeAttribute(node, 'position');
					var content = node.childNodes[0].nodeValue;

					/* No ideal solution, but now it at least works. */
					var temp = DIV();
					temp.innerHTML = content;
					if (position == 'before' && currentDocument().body.childNodes.length > 0)
					{
						currentDocument().body.insertBefore(temp, currentDocument().body.childNodes[0]);
					}
					else
					{
						appendChildNodes(currentDocument().body, temp);
					}
					break;
				case 'remove-element':
					var elementId = getNodeAttribute(node, 'element');
					removeElement(elementId);
					break;
				case 'replace-element':
					var targetId = getNodeAttribute(node, 'target');
					var sourceId = getNodeAttribute(node, 'source');
					if (getNodeAttribute(node, 'keep-original'))
					{
						var sourceElement = $(sourceId);
						swapDOM($(targetId), $(sourceId).cloneNode(true));
					}
					else
					{
						swapDOM($(targetId), $(sourceId));
					}
					break;
				case 'copy-element':
					var sourceId = getNodeAttribute(node, 'source');
					var destinationId = getNodeAttribute(node, 'destination');
					var sourceElement = $(sourceId);
					appendChildNodes($(destinationId), sourceElement.cloneNode(true));
					break;
				case 'show-element':
					var elementId = getNodeAttribute(node, 'element');
					setNodeAttribute(elementId, 'style', {'visibility': 'visible'});
					//showElement(elementId);
					break;
				case 'hide-element':
					var elementId = getNodeAttribute(node, 'element');
					setNodeAttribute(elementId, 'style', {'visibility': 'hidden'});
					//hideElement(elementId);
					break;
				case 'set-element-position':
					var elementId = getNodeAttribute(node, 'element');
					var e = getElement(elementId);
					eval('var positioningOptions = ' + node.childNodes[0].childNodes[0].nodeValue);
					if (positioningOptions.center && positioningOptions.center == true)
					{
						var bodyDim = getViewportDimensions();
						var eDim = elementDimensions(e);
						var x = Math.round((bodyDim.w - eDim.w) / 2);
						var y = Math.round((bodyDim.h - eDim.h) / 2);
					}
					else
					{
						var x = (positioningOptions.x) ? positioningOptions.x : 0;
						var y = (positioningOptions.y) ? positioningOptions.y : 0;
					}
					setElementPosition(e, {'x': x, 'y': y});
					break;
				case 'set-element-size':
					var elementId = getNodeAttribute(node, 'element');
					var width = getNodeAttribute(node, 'width');
					var height = getNodeAttribute(node, 'height');
					updateNodeAttributes(getElement(elementId), {'style': {'width': width + 'px'}});
					updateNodeAttributes(getElement(elementId), {'style': {'height': height + 'px'}});
					break;
				case 'insert-before':
					var targetId = getNodeAttribute(node, 'target');
					var tag = getNodeAttribute(node, 'tag');
					var id = getNodeAttribute(node, 'id');
					$(targetId).parentNode.insertBefore(createDOM(tag, {'id': id}), $(targetId));
					break;
				case 'insert-after':
					var targetId = getNodeAttribute(node, 'target');
					var tag = getNodeAttribute(node, 'tag');
					var id = getNodeAttribute(node, 'id');
					$(targetId).parentNode.insertBefore(createDOM(tag, {'id': id}), $(targetId).nextSibling);
					break;
				case 'attach-handler':
					var elementId = getNodeAttribute(node, 'element');
					var event = getNodeAttribute(node, 'event');
					eval('var handler = ' + getNodeAttribute(node, 'handler'));
					connect(elementId, event, handler);
					break;
				case 'remove-handler':
					var elementId = getNodeAttribute(node, 'element');
					var event = getNodeAttribute(node, 'event');
					disconnectAll(elementId, event);
					break;
				case 'javascript':
					eval(node.childNodes[0].nodeValue);
					break;
				case 'json':
					var id = getNodeAttribute(node, 'id');
					eval('Sitebox.Framework.Ajax.json["' + id + '"] = ' + node.childNodes[0].nodeValue);
					break;
				case 'css':
					var styles = createDOM('STYLE', {'type': 'text/css'}, node.childNodes[0].nodeValue);
					appendChildNodes(document.getElementsByTagName('head')[0], styles);
					break;
				case 'call':
					var func = getNodeAttribute(node, 'function');
					eval('var attr = ' + node.childNodes[0].childNodes[0].nodeValue);
					eval(func + "('" + attr.join("', '") + "')");
					break;
				case 'alert':
					alert(node.childNodes[0].nodeValue);
					break;
				case 'include-javascript':
					Sitebox.includeJavascriptOnce(node.childNodes[0].nodeValue);
					break;
				case 'include-css':
					Sitebox.includeStylesheetOnce(node.childNodes[0].nodeValue);
					break;
				case 'log':
					if (sbDebugPanel && node.childNodes[0])
					{
						var level = getNodeAttribute(node, 'level');
						var date = getNodeAttribute(node, 'date');
						sbDebugPanel.log(node.childNodes[0].nodeValue, level, date);
					}
					break;
				default:
					Sitebox.logError('Sitebox.Framework.Ajax.parse(): unknown Ajax tag: <' + node.nodeName + '>');
					break;
			}
		}
		catch (e)
		{
			var logMessage = 'Sitebox.Framework.Ajax.parse(): parsing failed: ' + e.name + ': ' + e.message;
			if (e.fileName)
			{
				logMessage += ' [' + e.fileName;
				if (e.lineNumber)
				{
					logMessage += ':' + e.lineNumber;
				}
				logMessage += ']';
			}
			Sitebox.logError(logMessage);

			/* Logging the XML node that went wrong. The try-catch is added for
			 * safety, because this can go wrong too. */
			try
			{
				Sitebox.logError(toHTML(root.childNodes[nodeKey]).replace('></', '>' + root.childNodes[nodeKey].childNodes[0].nodeValue + '</'));
			}
			catch (e) { /* Nothing */ }

			hasError = true;
		}
	}

	/* return false on error, otherwise true */
	return !hasError;
}; // function Sitebox.Framework.Ajax::parse

/**
 * When getting a 304: not modified header, we have to pase the cached request.
 *
 * @param	XMLHttpRequest	res
 */
Sitebox.Framework.Ajax.prototype.parseFromCache = function(res)
{
	Sitebox.logDebug('Sitebox.Framework.Ajax.parseFromCache(): 304 Not Modified. Parsing cached response.');

	var cachedRes = Sitebox.Framework.Ajax.Cache.getRequest(this._url);
	if (cachedRes != null)
	{
		/* is only parsed, other callbacks are ignored */
		this.parse(cachedRes.responseXML);
	}
}; // function Sitebox.Framework.Ajax::parseFromCache

/**
 * Returns a JSON object.
 *
 * @param string id
 * @return mixed
 */
Sitebox.Framework.Ajax.prototype.getJson = function(id)
{
	if (Sitebox.Framework.Ajax.json[id])
	{
		return Sitebox.Framework.Ajax.json[id];
	}
	Sitebox.logError("Sitebox.Framework.Ajax.getJson(): No JSON string '" + id + "' available.");
}; // function Sitebox.Framework.Ajax::getJson

/**
 * Posts a form via AJAX.
 *
 * @param 	string 	formId
 * @param	array	options
 * @param 	bool 	submitDisabledFields
 */
Sitebox.Framework.Ajax.prototype.postForm = function(formId, options, submitDisabledFields)
{
	var form = $(formId);

	/* Save all WYSIWYG HTML editors. */
	this._saveHtmlEditors(formId);

	var content = '';
	for (var i = 0; i < form.elements.length; i++)
	{
		/* name is required */
		if (!form.elements[i].name)
		{
			continue;
		}
		/* only checked radiobuttons and checkboxes which are checked should be posted */
		if (form.elements[i].type && (form.elements[i].type == 'radio' || form.elements[i].type == 'checkbox') && form.elements[i].checked == false)
		{
			continue;
		}
		/* disabled fields should not be posted, unless explicitly set */
		if (!submitDisabledFields && form.elements[i].disabled && form.elements[i].disabled == true)
		{
			continue;
		}
		/* file upload fields cannot be uploaded using Ajax. uploadFile() offers a solution */
		if (form.elements[i].type && form.elements[i].type == 'file')
		{
			Sitebox.logInfo("Sitebox.Framework.Ajax.portForm(): Form submit: field '" + form.elements[i].name + "' could not be posted, because it is a file-upload field.");
			continue;
		}
		if (content != '')
		{
			content += '&';
		}
		/* for multiple selects, add all selected options */
		if (form.elements[i].nodeName == 'SELECT' && form.elements[i].multiple)
		{
			for (var o = 0; o < form.elements[i].options.length; o++)
			{
				if (form.elements[i].options[o].selected)
				{
					if (o > 0)content += '&';
					content += form.elements[i].name + '=' + encodeURIComponent(form.elements[i].options[o].value);
				}
			}
		}
		else
		{
			content += form.elements[i].name + '=' + encodeURIComponent(form.elements[i].value);
		}
	}

	/* Set (or overwrite) some required options. */
	if (!options)
	{
		options = {};
	}
	options.method = 'POST';
	options.content = content;

	/* do an Ajax call */
	this.call(form.action, options);
}; // function Sitebox.Framework.Ajax::postForm

/**
 * Posts a form via an inline frame (IFRAME).
 *
 * Available options:
 * - bool	showProgressIndicator
 * - string	progressIndicatorText
 *
 * @param 	string 	formId
 * @param	array	options
 */
Sitebox.Framework.Ajax.prototype.postFormIframe = function(formId, options)
{
	/* for element */
	var form = $(formId);

	/* Save all WYSIWYG HTML editors. */
	this._saveHtmlEditors(formId);

	/* check if the iframe already exists, and create it if not */
	var iframe = $(formId + '_iframe');
	if (!iframe)
	{
		/* create a new iframe for posting to */
		iframe = createDOM('IFRAME', {'id': formId + '_iframe', 'name': formId + '_iframe', 'style': {'display': 'none'}});

		/* add the iframe to the DOM */
		form.appendChild(iframe);
	}

	/* set the form target to this frame */
	form.target = formId + '_iframe';

	/* parse the available options */
	if (options && options.showProgressIndicator)
	{
		var text = 'Please wait...';
		if (options.progressIndicatorText)
		{
			text = options.progressIndicatorText
		}
		/* show the progress indicator */
		Sitebox.showProgressIndicator(text);
		/* add callback function to hide the progress indicator when the response is sent */
		connect(iframe, 'onload', function(e) { Sitebox.hideProgressIndicator(); });
	}

	/* when the response is sent, the inline frame will contain
	 * an Ajax response XML file. This XML file needs to be
	 * parsed, so we connect the parse function to the onload
	 * event of the iframe.
	 * To get the XMLDocument (needed for parsing) from the
	 * iframe, we call the _getIframeXmlDocument method which
	 * offers a cross-browser solution for finding this object.
	 */
	var self = this;
	connect(iframe, 'onload', function(e) { self.parse(self._getIframeXmlDocument(e.src().id)); });

	/* submit the form */
	form.submit();
}; // function Sitebox.Framework.Ajax::postFormIframe

/**
 * Returns the XMLDocument which loaded in the iframe.
 *
 * @param 	string 	iframeId
 * @return	XML document
 */
Sitebox.Framework.Ajax.prototype._getIframeXmlDocument = function(iframeId)
{
	/* we need an XmlDocument to parse through the Ajax parser.
	 * This document can only be found when using the 'frames'
	 * array. If you access the 'document' property of a frame,
	 * you will get the entire loaded document (xml file).
	 */
	var xmlDocument = window.frames[iframeId].document;
	/* In Internet Explorer, you will get a XML Element when
	 * calling the frame.document. This object has one default
	 * property 'XMLDocument', this is the actual DOM object
	 * we need. If this property is available (IE), return it.
	 */
	if (xmlDocument.XMLDocument)
	{
		return xmlDocument.XMLDocument;
	}
	/* Other browsers return the XMLDocument immediately, so
	 * we will return the object retrieved from frame.document
	 */
	return xmlDocument;
}; // function Sitebox.Framework.Ajax::_getIframeXmlDocument

/**
 * Saves all WYSIWYG HTML editors to their linked fields.
 *
 * @param 	string 	formId
 */
Sitebox.Framework.Ajax.prototype._saveHtmlEditors = function(formId)
{
	var form = $(formId);

	/* Check if FCKeditor is loaded. */
	try
	{
		Sitebox.logDebug('FCKeditor version ' + FCKeditorAPI.Version + ' found.');
		/* Loop through the forms textareas to see if any of these is a FCKeditor. */
		var textareas = form.getElementsByTagName('TEXTAREA');
		for (var t = 0; t < textareas.length; t++)
		{
			/* Check if the textareas is a FCKeditor. */
			var editor = FCKeditorAPI.GetInstance(textareas[t].id);
			if (editor)
			{
				/* Save the editor content to the textarea. */
				editor.UpdateLinkedField();
				Sitebox.logInfo("Form submit: FCKeditor HTML saved to textarea '" + textareas[t].name + "'.");
			}
		}
	}
	catch (e) { /* Do nothing, no FCK editor loaded. */ }
}; // function Sitebox.Framework.Ajax::_saveHtmlEditors