/**
 * Id of element with innerHTML property for out messages
 */
var MESSAGES_OUTPUT = 'messagesOutput';
/**
 * Id of element with innerHTML property for out messages
 */
var ERRORS_OUTPUT = 'errorsOutPut';

var USER_MESSAGE = 'user';

function clearMessages() {
	if($id(MESSAGES_OUTPUT)) {
		$id(MESSAGES_OUTPUT).innerHTML = "";
	}
}

function clearErrors() {
	if($id(ERRORS_OUTPUT)) {
		$id(ERRORS_OUTPUT).innerHTML = "";
	}
}

/** Id of element with innerHTML property for out messages */
var GLOBAL_MESSAGES_OUTPUT = MESSAGES_OUTPUT;
/** Id of element with innerHTML property for out error messages */
var GLOBAL_ERRORS_OUTPUT = ERRORS_OUTPUT;
/** Id of element with innerHTML property for out messages in DIALOG */
var GLOBAL_MESSAGES_OUTPUT_DIALOG = 'messagesOutPutDialog';
/** Id of element with innerHTML property for out error messages in DIALOG */
var GLOBAL_ERRORS_OUTPUT_DIALOG = 'errorsOutPutDialog';
/** Class name of element with innerHTML property for out messages */
var GLOBAL_MESSAGES_CLASS_NAME = 'global-messages'; 
var FIELD_MESSAGES_CLASS_NAME = 'field-messages';
/** Class name of html element with innerHTML property for out error messages */
var GLOBAL_ERROR_MESSAGES_CLASS_NAME = 'global-errors';
var FIELD_ERROR_MESSAGES_CLASS_NAME = 'field-errors';
/** Class name of element with innerHTML property for out messages in DIALOG */
var GLOBAL_MESSAGES_DIALOG_CLASS_NAME = 'GlobalMessagesDialog';
/** Class name of html element with innerHTML property for out error messages in DIALOG */
var GLOBAL_ERROR_MESSAGES_DIALOG_CLASS_NAME = 'GlobalErrorsDialog';

var UNDEFINED = 'undefined';

var USER_MESSAGE = 'user';

var PlacedMessage = Class.create();

debugMode = true;

PlacedMessage.ERROR_MESSAGE = 1;
PlacedMessage.MESSAGE = 2;

PlacedMessage.prototype = {
	
	text : null, place : null, type : null, clean : true,
	
	initialize : function(text, place, type, clean) {
		this.text = text;
		this.place = place;

		if(!type) {
			type = PlacedMessage.MESSAGE;
		}
		if(type != PlacedMessage.ERROR_MESSAGE && type != PlacedMessage.MESSAGE) {
			throw new Error('Invalid type ' + type);
		}
		this.type = type;
		
		this.clean = clean;
		if(this.clean != false || this.clean != true) {
			this.clean = true;
		}
	},
	
	toString : function() {
		return this.place + ": " + this.text;
	}
};

var registeredPlaces = [];

function registerPlace(place) {
	registeredPlaces[registeredPlaces.length] = place;
}

function clearAllMessages() {
	if($id(GLOBAL_ERRORS_OUTPUT)) {
		$id(GLOBAL_ERRORS_OUTPUT).innerHTML = "";
	}
	if($id(GLOBAL_MESSAGES_OUTPUT)) {
		$id(GLOBAL_MESSAGES_OUTPUT).innerHTML = "";
	}
	if(registeredPlaces) {
		for(var i=0; i<registeredPlaces.length; i++) {
			if(!$id(registeredPlaces[i])) continue;
			$id(registeredPlaces[i]).innerHTML = "";
			if($id(registeredPlaces[i]).parentNode) {
				Element.remove($id(registeredPlaces[i]));
			}
		}
		registeredPlaces = [];
	}	
}

function clearDialogMessages(parent) {
	var dialogMessages = getDialogMessages(parent);
	if(dialogMessages) {
		if(dialogMessages.dialogMessages) {
			dialogMessages.messages.innerHTML = "";
		}
		if(dialogMessages.errors) {
			dialogMessages.errors.innerHTML = "";
		}
	}
}

/**
 * Display messages in specifed output: GLOBAL_MESSAGES_OUTPUT. If element not
 * specified on form simple alert messages.
 * @see GLOBAL_MESSAGES_OUTPUT.
 */
function displayMessages(messages, notClean, notScroll) {
	displaySimpleMessages(getMessagesContainer(), messages, notClean, "info", notScroll);
	if($id(ERRORS_OUTPUT) && isEmpty($id(ERRORS_OUTPUT).innerHTML)) {
		Element.hide($id(ERRORS_OUTPUT));
	}
	
}

function displayFieldMessages(fieldMessages, notClean, notScroll) {
	if(Object.isArray(fieldMessages)) {
		for(var i=0; i<fieldMessages.length; i++) {
			text, place, type, clean
			fieldMessages[i] = new PlacedMessage(
					fieldMessages[i].text,
					fieldMessages[i].place,
					PlacedMessage.MESSAGE,
					true
			);
		}
	} else {
		fieldMessages = new PlacedMessage(
				fieldMessages.text,
				fieldMessages.place,
				PlacedMessage.MESSAGE,
				true
		);
		fieldMessages = [fieldMessages];
	}
	displayMessages(fieldMessages);
}

/**
 * Display messages in specifed output: GLOBAL_ERRORS_OUTPUT. If element not
 * specified on form simple alert messages.
 * @see GLOBAL_ERRORS_OUTPUT.
 */
function displayErrorMessages(errorMessages, notClean, notScroll) {
	displaySimpleMessages(getErrorMessagesContainer(), errorMessages, notClean, "error", notScroll);
	if($id(MESSAGES_OUTPUT) && isEmpty($id(MESSAGES_OUTPUT).innerHTML)) {
		Element.hide($id(MESSAGES_OUTPUT));
	}
}

function displayFieldErrors(fieldErrors, notClean, notScroll) {
	if(Object.isArray(fieldErrors)) {
		for(var i=0; i<fieldErrors.length; i++) {
			fieldErrors[i] = new PlacedMessage(
					fieldErrors[i].text,
					fieldErrors[i].place,
					PlacedMessage.ERROR_MESSAGE,
					true
			);
		}
	} else {
		fieldErrors = new PlacedMessage(
				fieldErrors.text,
				fieldErrors.place,
				PlacedMessage.ERROR_MESSAGE,
				true
		);
		fieldErrors = [fieldErrors];
	}
	displayErrorMessages(fieldErrors);
}

function getMessagesContainer() {
	var container = getActivatedDialogMessages(GLOBAL_MESSAGES_DIALOG_CLASS_NAME);
	if(!container || container == null) {
		container = $id(GLOBAL_MESSAGES_OUTPUT);
	}
	return container;
}

function getErrorMessagesContainer() {
	var container = getActivatedDialogMessages(GLOBAL_ERROR_MESSAGES_DIALOG_CLASS_NAME);
	if(!container || container == null) {
		container = $id(GLOBAL_ERRORS_OUTPUT);
	}
	return container;
}

function hideErrorMessages() {
	Element.hide(getErrorMessagesContainer());
}

function showErrorMessages() {
	Element.show(getErrorMessagesContainer());
}


function scrollToErrorMessages() {
	scrollToElementIfNotVisible(getErrorMessagesContainer());
}

function displaySimpleMessages(container, messages, notClean, logLevel, notScroll) {
	if(messages) {
		if(Object.isArray(messages) && messages.length == 0) return;
		if($id(container) && !notClean) {
			$id(container).innerHTML = "";
		} 
		displayListGeneralMessages(container, messages, notClean, logLevel, notScroll);
	}
}

function displayListGeneralMessages(container, messages, notClean, logLevel, notScroll) {
	var result = "";
	var needScroll = !notScroll;
	var scrollTo = container;
	if(Object.isArray(messages)) {
		for(var index=0; index<messages.length; index++) {
			var message = messages[index];
			if(Object.isString(message)) {
				result += message + "<br>";
			} else if (isPlacedMessage(message)) {
				var target = displayMessageInPlace(message);
				if(!target) {
					result += message + "<br>";
				} else {
					needScroll = needScroll && isNotVisible(target);
					scrollTo = target;
				}
			}
		}
	} else {
		result = messages;
	}
	if(result != "") {
		if($id(container)) {
			Element.show($id(container));
		}
		appendMessageToPlace(result, container, !notClean);
		if(needScroll) {
			//scrollToElementIfNotVisible($id(container));
		}
	} else {
		//scrollToElementIfNotVisible($id(scrollTo));
	}
}

function isPlacedMessage(message) {
	return message.text && message.place && message.type;
}

function appendMessageToPlace(message, place, clean) {
	if(place && $id(place)) {
	   	if(!clean) {
	   		$id(place).innerHTML += message;
		} else {
			$id(place).innerHTML = message;
		}	
	} else {
		alertMessage(message);
	}
}

function displayMessageInPlace(placedMessage) {
	var target = null;
	target = $id(placedMessage.place);
	if(!target && (!placedMessage.place.indexOf('_')>=0)) {
		target = $id('_' + placedMessage.place);
	}
	if(!target) {
		var targets = document.getElementsByName(placedMessage.place);
		if(targets && targets.length > 0) {
			target = targets[0]; 
		}
	}
	if(target) {
		if(isContainerElement(target)) {
			checkClass(target, placedMessage.type);
			registerPlace(target);
			appendMessageToPlace(placedMessage.text, target, placedMessage.clean);
		} else {
			var messagePlace = createPlaceForMessage(placedMessage);
			checkTargetClass(target);
			checkClass(messagePlace, placedMessage.type);
			appendMessageToPlace(placedMessage.text, $id(messagePlace), placedMessage.clean);
			registerPlace(messagePlace);
			target.parentNode.appendChild(messagePlace);
		}
	}
	return target;
}

function checkTargetClass(target) {
	var errorClassName = 'errorField';
	if(Element.hasClassName(target, errorClassName)) return;
	
	Element.addClassName(target, errorClassName);
	
	var removeErrorClass = function(event) {
		if(isNotEmpty(event.element().value)) {
			if(Element.hasClassName(errorClassName)) {
				Element.removeClassName(event.element(), errorClassName);
			}
			var childs = event.element().parentNode.childNodes;
			for(var i=0; i<childs.length; i++) {
				if(childs[i].id && childs[i].id.indexOf('errorMessageFor_') == 0) {
					Element.remove(childs[i]);
				}				
			}
		}
	};
	observeForChangeInputField(target, removeErrorClass);
}

function checkClass(place, type) {
	if(!place.className || place.className.strip() == "") {
		switch (type) {
			case PlacedMessage.ERROR_MESSAGE:
				place.className = FIELD_ERROR_MESSAGES_CLASS_NAME; 
				break;
			case PlacedMessage.MESSAGE:
				place.className = FIELD_MESSAGES_CLASS_NAME; 
				break;
			default:
				break;
		}
	}
}

function checkMessageId(placedMessage) {
	switch (placedMessage.type) {
		case PlacedMessage.ERROR_MESSAGE:
			return 'errorMessageFor_' + placedMessage.place;
		case PlacedMessage.MESSAGE:
			return 'messageFor_' + placedMessage.place;
		default:
			return 'messageFor_' + placedMessage.place;
	}
}

function isContainerElement(el) {
	el = $id(el);
	return el && el.nodeName != 'INPUT'
		 && el.nodeName != 'TEXTAREA'
		 && el.nodeName != 'IMG'
		 && el.innerHTML != 'undefined';
}

function createPlaceForMessage(placedMessage) {
	var messageDiv = document.createElement('DIV');
	messageDiv.id = checkMessageId(placedMessage);
	return messageDiv;
}

function getActivatedDialogMessages(className) {
	var els = document.getElementsByClassName(className);
	
	if(!els) return;
	
	for (var i = 0; i < els.length; i++) {
		if(isActivatedDialogMessages(els[i])) {
			return els[i];
		}
	}
	return null;
}

function isActivatedDialogMessages(element) {
	return Element.visible($id(element));
}

function getDialogMessages(parent) {
	var messagesDialog = null;
	var errorMessagesDialog = null;
	if(parent && $id(parent)) {
		messagesDialog = document.getElementsByClassName(parent, GLOBAL_MESSAGES_DIALOG_CLASS_NAME)[0];
		errorMessagesDialog = document.getElementsByClassName(parent, GLOBAL_ERROR_MESSAGES_DIALOG_CLASS_NAME)[0];
	} else {
		messagesDialog = $id(GLOBAL_MESSAGES_OUTPUT_DIALOG);
		errorMessagesDialog = $id(GLOBAL_ERRORS_OUTPUT_DIALOG);
	}
	return {messages : messagesDialog, errors: errorMessagesDialog};
}

function activateDialogMessages(parent) {
	var dialogMessages = getDialogMessages(parent);
	if(dialogMessages) {
		if(dialogMessages.dialogMessages) {
			Element.show(dialogMessages.messages);
		}
		if(dialogMessages.errors) {
			Element.show(dialogMessages.errors);
		}
	}
}

function diactivateDialogMessages(parent) {
	var dialogMessages = getDialogMessages(parent);
	if(dialogMessages) {
		if(dialogMessages.dialogMessages) {
			Element.hide(dialogMessages.messages);
		}
		if(dialogMessages.errors) {
			Element.hide(dialogMessages.errors);
		}
	}
}

/**
 * Display message in specifed output: GLOBAL_ERRORS_OUTPUT. If element not
 * specified on form simple alert messages.
 * @see GLOBAL_ERRORS_OUTPUT.
 */
function displayErrorMessage(errorMessage, notClean) {
	displayErrorMessages(errorMessage, notClean);
}

function scrollToElementIfNotVisible(element) {
	if(isNotVisible(element)) {
		Element.scrollTo(element);
	}
}

function isNotVisible(element) {
	if (!element.viewportOffset)
		return true;
	var pos = element.viewportOffset();
	return ((pos.left < 0 || pos.top < 0)
			 || (document.viewport.getHeight() - pos.top < 0 || document.viewport.getWidth() - pos.left < 0)) 
			 && Element.visible(element);
}

/**
 * Simple alert message, but replace all <br> to \n;
 */
function alertMessage(message) {
	if(message.replace) {
		message = message.replace(/<br>/gi, '\n');
	} else if (message.message) {
		message.message = message.message.replace(/<br>/gi, '\n');
	}
	debugAlert(message);
}

PARAM_VALUES_CHECKER = 'null';

function constructMessage(message, params) {
	if(message && params) {
		if(Object.isArray(params) && params.length > 0) {
			for (var index = 0; index < params.length; index++) {
				if(message.indexOf(PARAM_VALUES_CHECKER) >= 0) {
					message = appendParamToMessage(message, params[index]);
				} else {
					break;
				}
			}
		} else {
			message = appendParamToMessage(message, params);
		}
	}
	return message;
}

function appendParamToMessage(message, param) {
	if(message && param) {
		if(message.indexOf(PARAM_VALUES_CHECKER) >= 0) {
			var before = message.substring(0, message.indexOf(PARAM_VALUES_CHECKER));
			var after = message.substring(message.indexOf(PARAM_VALUES_CHECKER)
				+ PARAM_VALUES_CHECKER.length);
			message = before + param + after;
		}
	}
	return message;
}

function debugAlert(value) {
	if(debugMode) {
 		alert('Debug: ' + value);
	}
}

var Template = Class.create();

Template.prototype = {

	_html : null,
		
	initialize: function(html) {
		this._html = html;
		this._html = this._html.replace(/<\!--/, '');
		this._html = this._html.replace(/-->/, '');
	},

	insertObject : function(name, value) {
		for(var prop in value) {
			if(Object.isObject(value[prop])) {
				this.insertObject(name + "." + prop, value[prop]);
			} else {
				this.insertValue(name + "." + prop, value[prop]);
			}
		}
	},
		
	insertValue : function(name, value) {
		if(value == null || value == undefined) {
			value = "";
		}
		var re = new RegExp('\\$\\(' + name + "\\)", "g");
		this._html = this._html.replace(re, value);
	},
	
	html : function() {
		this.clearValues();
		return this._html;
	},
	
	clearValues : function() {
		this._html = this._html.replace(/\$\([A-z0-9\._]+\)/g, '');
	},
	
	append : function(id, style) {
		this.appendTo(id, style, document.body);
	},
	
	appendTo : function(id, style, container) {
		var div = document.createElement('DIV');
		div.id = id;
		div.innerHTML = this.html();
		
		for(prop in style) {
			div.style[prop] = style[prop];
		}
		if($id(id)) {
			Element.remove(id);
		}
		container.appendChild(div);
	}
};

function loadHtml(filePath, callback) {
	var ajaxRequest = new AjaxRequest(filePath, null,
		function (transport) {
			callback(new Template(transport.responseText));
		}
	);
	ajaxRequest.doRequest('GET');
}

/**
 * Simple alert message, but replace all <br> to \n;
 */
function alertMessage(message) {
	message = message.replace(/<br>/gi, '\n');
	alert(message);
}

Object.extend(Object, {
	evalStandartJSONResponse: function (object) {
		if(object) {
			if(object.evalJSON) {
				object = object.evalJSON();
			}
			Object.evalObjectPropJSON(object, AjaxResponse.GLOBAL_ERRORS);
			Object.evalObjectPropJSON(object, AjaxResponse.FIELD_ERRORS);
			Object.evalObjectPropJSON(object, AjaxResponse.GLOBAL_MESSAGES);
			Object.evalObjectPropJSON(object, AjaxResponse.FIELD_MESSAGES);
			Object.evalObjectPropJSON(object, AjaxResponse.RESULT_FIELD);
		}
		return object;
	},
	
	evalObjectPropJSON: function (object, prop) {
		if(object && prop && object[prop] && object[prop].evalJSON) {
			object[prop] = object[prop].evalJSON();
		}
	}
});

function isNotEmpty(str) {
	return str && str != null && (str.strip?str.strip() != '':true) ; 
}

function isEmpty(str) {
	return !isNotEmpty(str);
}


//-------------------------------------------------------------------
//------------------------ AJAX MAIN CLASS -------------------------
//-------------------------------------------------------------------
AjaxResponse = function() {
	
};

/** All global server-side errros in JSON array. */
AjaxResponse.GLOBAL_ERRORS = 'globalErrors';
/** All fields server-side errros in JSON array. */
AjaxResponse.FIELD_ERRORS = 'fieldErrors';
/** All global server-side messages in JSON array.*/
AjaxResponse.GLOBAL_MESSAGES = 'globalMessages';
/** All fields server-side messages in JSON array.*/
AjaxResponse.FIELD_MESSAGES = 'fieldMessages';
/** Directly result of server-side logic. This is composite object.*/
AjaxResponse.RESULT_FIELD = 'result';

AjaxRequest = function(url, parameters, onSuccessF, onFailureF, onExceptionF, on0F) {
	this.url = url;
	this.parameters = parameters;
	
	if(parameters) {
		this.parameters = parameters;
	} else {
		this.parameters = '';
	}
	
	if(onSuccessF && Object.isFunction(onSuccessF)) {
		this.onSuccessF = onSuccessF;
	}
	if(onFailureF && Object.isFunction(onFailureF)) {
		this.onFailureF = onFailureF;
	}
	if(onExceptionF && Object.isFunction(onExceptionF)) {
		this.onExceptionF = onExceptionF;
	}
	if(on0F && Object.isFunction(on0F)) {
		this.on0F = on0F;
	}
};

/**
 * Ajax parameter name for ajax request. All ajax request should have
 * this parameter  and set it to true.
 */
AjaxRequest.AJAX_MARKER = 'ajax';


AjaxRequest.addUrlParameter = function (url, parameter, value) {
	if(!url) {
		url = '';
	}
	if(isNotEmpty(parameter) && isNotEmpty(value)) {
		if(url.length > 0) {
			url += '&';
		}
		url += parameter + '=' + value;
	}
	return url;
};

AjaxRequest.prototype.url = null;
AjaxRequest.prototype.parameters = null;
AjaxRequest.prototype.method = 'post';
AjaxRequest.prototype.processImage = null;
AjaxRequest.prototype.displayErrors = true;

AjaxRequest.prototype.addUrlParameter = function(parameter, value) {
	if(Object.isString(this.parameters)) {
		this.parameters = AjaxRequest.addUrlParameter(this.parameters, parameter, value);
	} else if (Object.isObject(this.parameters)) {
		this.parameters[parameter] = value;
	}
};
AjaxRequest.prototype.checkExist = function(parameter) {
	if(Object.isString(this.parameters)) {
		return this.parameters.indexOf(parameter + '=') > 0;
	} else if (Object.isObject(this.parameters)) {
		return isNotEmpty(this.parameters[parameter]);
	} else {
		return false;
	}
};
AjaxRequest.prototype.onSuccessF = function(transport) {
	var ajaxObj = Object.evalStandartJSONResponse(transport.responseText);
	if(this.processImage) {
		Element.hide(this.processImage);
	}
};
AjaxRequest.prototype.onFailureF = function(transport) {
	var ajaxObj = Object.evalStandartJSONResponse(transport.responseText);
	if(this.displayErrors) {
		displayErrorMessages(ajaxObj[AjaxResponse.GLOBAL_ERRORS]);
		displayErrorMessages(ajaxObj[AjaxResponse.FIELD_ERRORS]);
	}
	if(this.processImage) {
		Element.hide(this.processImage);
	}
};
AjaxRequest.prototype.onExceptionF = function(requesterObj, exceptionObj) {
	 if(this.displayErrors) {
	 	displayErrorMessages(exceptionObj);
	 }
	 if(this.processImage) {
	 	Element.hide(this.processImage);
	 }
};
AjaxRequest.prototype.on0F = function(transport) {
	ajaxRequest.options.onFailure(transport);
};

AjaxRequest.prototype.showErrors = function(value) {
	this.displayErrors = value;
}

AjaxRequest.prototype.checkBaseLink = function() {
	var baseLink = getBaseLink();
	if(baseLink.length > 2) {
		if(this.url && this.url.indexOf(baseLink) != 0) {
			if(baseLink.substring(baseLink.length - 1, baseLink.length) != "/"
				&& this.url.substring(0, 1) != "/") {
				baseLink += "/";
			}
			this.url = baseLink + this.url;
		}
	}
}

/**
 * Do Ajax request to server
 * @param method request method [get, post]. Default: post. Can be null
 * @param processImage $id(processImage) img for display ajax process. Can be null.
 * @param serializeParams need to serialize url parameters
 */
AjaxRequest.prototype.doRequest = function (method, processImage, serializeParams) {
	if (!this.parameters) {
		this.parameters = "";
	}
	if(isNotEmpty(method)) {
		this.method = method;
	}
	if(!this.checkExist(AjaxRequest.AJAX_MARKER)) {
		this.addUrlParameter(AjaxRequest.AJAX_MARKER, 'true');
	}
	if(this.processImage) {
		Element.show(this.processImage);
	}
	if(Object.isBoolean(serializeParams) && !serializeParams) {
		this.url += '?' + this.parameters;
		this.parameters = '';
	}
	this.checkBaseLink();
	var ajaxRequest = new Ajax.Request(this.url, {
		method: this.method,
		parameters: this.parameters,
		onSuccess: this.onSuccessF,
		onFailure: this.onFailureF,
		onException: this.onExceptionF,
		on0: this.on0F
	});	
};

function getBaseLink() {
	return (typeof baseLink != "undefined" && baseLink)? baseLink: "";
}

function defineProcessImage(src) {
	if(src) {
		if(!$id(src)) {
			if (typeof PROCESS_IMAGE != "undefined" && PROCESS_IMAGE) {
				if($id(PROCESS_IMAGE)) {
					return $id(PROCESS_IMAGE);
				} else {
					return null;
				}
			} else {
				var img = document.createElement('IMG');
				img.style.display = 'none';
				img.src = src;
				document.body.appendChild(img);
				return img;
			}
		} else {
			return $id(src);
		} 
	} else {
		return null;
	}
}

if (typeof Class != "undefined" && Class) {

	AjaxActionSynchronizer = {
			
		SYNCH_NAME : "ajaxSynch",
		
		synchCallbackMap : {},
			
		getSynchValue : function() {
			return new Date().getTime();
		},	
		
		putCallback : function(callback, synchValue) {
			if(synchValue == undefined) {
				synchValue = AjaxActionSynchronizer.getSynchValue();
			}
			if((AjaxActionSynchronizer.synchCallbackMap[synchValue] == undefined 
					|| AjaxActionSynchronizer.synchCallbackMap[synchValue] == null)
					&& Object.isFunction(callback)) {
				AjaxActionSynchronizer.synchCallbackMap[synchValue] = callback;
			}
		},
		
		call: function(result, synchValue) {
			if(synchValue == undefined) {
				synchValue = result.synchValue;
			}
			if(synchValue != undefined) {
				var callback = AjaxActionSynchronizer.synchCallbackMap[synchValue];
				if(Object.isFunction(callback)) {
					callback(result);
				}
			}
		},
		
		execute : function(simpleAjaxAction, action, params, callback) {
			if(callback == undefined) {
				callback = simpleAjaxAction.callback;
			}
			var synchValue = AjaxActionSynchronizer.getSynchValue();
			if(params == undefined) {
				params = {};
			}
			params[AjaxActionSynchronizer.SYNCH_NAME] = synchValue;
			AjaxActionSynchronizer.putCallback(callback, synchValue);
			simpleAjaxAction.execute(action, params, AjaxActionSynchronizer.call);
		}
	}
	
	SimpleAjaxAction = Class.create();
	
	SimpleAjaxAction.prototype = {
		
		url : null,
		processImage : null,
		action : null,
		callback : null,
		errorCallback: null,
		method : null,
		params : null,
		
		initialize : function(url, method, processImage) {
			if(url) {
				this.url = url;
			}
			if(method) {
				if(method == 'POST' || method == 'GET') {
					this.method = method;
				} else {
					this.processImage = processImage;
				}
			}
			if(processImage) {
				this.processImage = processImage;
			}
		},
		
		execute : function(action, params, callback, errorCallback) {
			if(action) {
				this.action = action;
			}
			if(callback) {
				this.callback = callback;
			}
			if(errorCallback) {
				this.errorCallback = errorCallback;
			}
			if(params) {
				this.params = params;
			}
			this.processImage = defineProcessImage(this.processImage);
			objSelf = this;
			var ajaxRequest = new AjaxRequest(this.url, this.getParams(), function(data) {
				if(data.responseText) {
					data = Object.evalStandartJSONResponse(data.responseText);
				} else {
					data = Object.evalStandartJSONResponse(data);
				}
				
				if(data[AjaxResponse.GLOBAL_ERRORS].length > 0 || data[AjaxResponse.FIELD_ERRORS].length > 0) {
					displayErrorMessages(data[AjaxResponse.GLOBAL_ERRORS]);
					displayFieldErrors(data[AjaxResponse.FIELD_ERRORS]);

					if(objSelf.errorCallback) {
						try {
							objSelf.errorCallback(data.result, data[AjaxActionSynchronizer.SYNCH_NAME]);
						} catch (e) {
							debugAlert(e);
						}
					}
				} else {
					displayMessages(data[AjaxResponse.GLOBAL_MESSAGES]);
					displayFieldMessages(data[AjaxResponse.FIELD_MESSAGES]);
					
					if(objSelf.callback) {
						try {
							objSelf.callback(data.result, data[AjaxActionSynchronizer.SYNCH_NAME]);
						} catch (e) {
							debugAlert(e);
						}
					}
				}
				
				if(this.processImage) {
					Element.hide(this.processImage);
				}
			});
			if(this.action && !ajaxRequest.checkExist(AjaxRequest.AJAX_MARKER)) {
				ajaxRequest.addUrlParameter(this.action, this.action);
			}
			ajaxRequest.doRequest(this.method, this.processImage);
		},
		
		getParams : function() {
			return this.params;
		}
	}
	
	FullFormSubmitAjaxAction = Class.create(SimpleAjaxAction, {
		
		form : null,
		
		initialize : function($super, url, form, processImage) {
			$super(url, 'POST', processImage);
			this.form = form;
		}, 
		
		execute : function($super, action, callback, errorCallback) {
			$super(action, null, callback, errorCallback);
		},
		
		getParams : function($super) {
			if(!this.params) {
				this.params = {};
			}
			var formParams = serializeFrom($id(this.form));
			for(var prop in formParams) {
				if(this.params[prop] == undefined) {
					this.params[prop] = formParams[prop];
				}
			}
			return this.params;
		}
	});
}
