	
	var cmsForm_ajax = {
		
		clicked: "",
		fields: {},
		callbacks: {"after": {}, "before": {}},
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
		/**
		 *	Регистрирует в объекте новый коллбэк
		 *	
		 *	@param	string		formName		Название формы
		 *	@param	function	func				Функция для вызова
		 *	@param	string		type				Тип коллбэка — normal|replace
		 *	@return	void
		 */
		
		callback: function(formName, func, type) {
			
			type = type || "after";
			
			if (!this.callbacks[type][formName]) this.callbacks[type][formName] = new Array();
			
			this.callbacks[type][formName].push(func);
			
		},
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
		/**
		 *	Возвращает просчитанный ID поля
		 *	
		 *	@param	string		name				Название поля
		 *	@param	string		formID			ID формы
		 *	@return	string
		 */
		 
		getID: function(name, formID) {
			
			return formID + "_" + name.replace(/\|/ig, "__");
			
		},
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
		/**
		 *	Выводит ошибку около поля
		 *	
		 *	@param	string		id					ID поля
		 *	@param	string		src					ID объекта для привязки (для радиокнопок и чекбоксов привязка — <div class='group'>, а не сам контрол
		 *	@param	string		html				HTML текст ошибки
		 *	@return	void
		 */
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
		errorHTML: function(id, src, html, text) {
			
			var obj = $("#" + src);
			
			if (obj.get(0)) {
				
				if (!$("#" + id + "_errorText").get(0))	obj.after("<span class='cmsForm_errorText' id='" + id + "_errorText'></span>");
				if (!$("#" + id + "_error").get(0))			obj.after("<span class='cmsForm_error'     id='" + id + "_error'><img src='/images/free.gif'></span>");
				
				$("#" + id + "_error").css({width: obj.get(0).style.width}).show();
				
				if (html) { // для span
					
					$("#" + id + "_errorText").show().append(html);
					
				}
				
				if (text) { // для title
					
					var title = obj.attr("title");
					title = (title) ? title + ", " + text : text;
					
					obj.attr("title", title);
					
				}
				
				obj.addClass("cmsForm_errorField");
				
			} else cmsConsole_error("Невозможно найти объект «" + src + "» с ID «" + id + "».\nТекст ошибки: " + html);
			
		},
		
		errorHide: function(id, src) {
			
			$("#" + id + "_error").hide();
			$("#" + id).removeClass("cmsForm_errorField");
			
			$("#" + id + "_errorText").hide().html(""); // для span
			$("#" + src).attr("title", ""); // для title
			
		},
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
		/**
		 *	Перезагружает CAPTCHA, возможный TODO — отлов всех капч для formID, но по-идее форма с капчей должна быть одна на страницу
		 *	
		 *	@param	string		formName		Название формы
		 *	@return	void
		 */
		 
		reloadCaptcha: function(formName, formID) {
			
			$("#" + this.getID("confirm", formID)).attr("value", "");
			
			var img = $("#" + this.getID("captcha", formID));
			img.attr("src", "/_core/classes/form_ajax/ajax_confirm.php?formName=" + formName + "&width=" + img.width() + "&height=" + img.height() + "&rnd=" + Math.random());
			
		},
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
		/**
		 *	Собирает данные формы и отправляет их на сервер, затем обрабатывает результат
		 *	
		 *	@param	object		formObj			Объект формы
		 *	@return	bool
		 */
		 
		submit: function(formObj) {
			
			var formID = formObj.id;
			var formName = formObj.name;
			var send = new Array();
			var form = $("#" + formID);
			var self = this; // anti-jQuery
			
			// Разбор формы и формирование массива для отправки
			
			//alert(JSON.stringify(self.fields[formName])); return false;
			
			for (var name in self.fields[formName]) {
				
				var tmp = self.fields[formName][name];
				var id = self.getID(tmp.name, formID);
				
				if (tmp.type == "radio") {
					
					send[tmp.name] = $("input[id^=" + id + "]:checked").attr("value");
					
				} else if (tmp.type == "checkbox") {
					
					send[tmp.name] = $("#" + id).attr("checked") == true ? 1 : "";
					
				} else if (tmp.type == "submit") {
					
					if (self.clicked == tmp.name) {
						
						send[tmp.name] = $("#" + id).attr("value");
						self.clicked = "";
						
					}
					
				} else send[tmp.name] = $("#" + id).attr("value");
				
				// также ошибки можно сбросить и здесь, но лучше после запроса
				
			}
			
			var sendJSON = new Array();
			sendJSON[formName] = send;
			
			//alert(JSON.stringify(sendJSON)); return false;
			
			// AJAX запрос
			
			cmsAjax(
				form.attr("action"), // backend
				sendJSON,
				function(res) {
					
					$("#" + formID + "_html").hide();
					
					if (res.criticalError) {
						
						alert("Critical: " + res.criticalError);
						
					} else {
						
						// удаленные поля грохаем
						if (res.fieldsDeleted) for (var i = 0; i < res.fieldsDeleted.length; i++) delete self.fields[formName][res.fieldsDeleted[i]];
						
						for (var name in self.fields[formName]) {
							
							var tmp = self.fields[formName][name];
							var id = self.getID(tmp.name, formID);
							var src = (tmp.type == "radio" || tmp.type == "checkbox") ? id + "_block" : id;
							
							// сбрасываем ошибки
							self.errorHide(id, src);
							
						}
						
						// Вызов кастомных callback'ов перед основной обработкой
						if (self.callbacks.before[formName]) for (var i = 0; i < self.callbacks.before[formName].length; i++) {
							
							res = self.callbacks.before[formName][i](res, formID);
							if (res == null) break;
							
						}
						
						// Действия по-умолчанию
						
						if (res != null) { // Если callback'и не зарубили res
							
							if (res.errors && res.errors.length > 0) {
								
								//alert(JSON.stringify(res.errors));
								
								for (var i = 0; i < res.errors.length; i++) {
									
									var tmp = self.fields[formName][res.errors[i].name];
									var id = res.errors[i].id;
									
									if (tmp) {
										
										var src = (tmp.type == "radio" || tmp.type == "checkbox") ? id + "_block" : id;
										
										self.errorHTML(id, src, res.errors[i].html, res.errors[i].text);
										
									} else cmsConsole_error("Для вывода ошибки не найдено поле «" + res.errors[i].id + "»");
									
								}
								
							} else cmsConsole_notice("Отправка данных формы «" + formName + "» успешна. Ошибок не обнаружено.");
							
							// заменяем html в определенной области
							if (res.html) $("#" + formID + "_html").show().html(res.html);
							
							// заменяем всю форму нафиг, если все ок
							if (res.htmlReplace) form.replaceWith(res.htmlReplace);
							
							// алертим
							if (res.alert) alert(res.alert);
							
							// Вызов кастомных callback'ов после основной обработки
							if (self.callbacks.after[formName]) for (var i = 0; i < self.callbacks.after[formName].length; i++) {
								
								res = self.callbacks.after[formName][i](res, formID);
								if (res == null) break;
								
							}
							
							// редирект
							if (res.redirect) document.location = res.redirect;
							
							//cmsConsole_notice(JSON.stringify(self.fields[formName]));
							
						}
						
					}
					
				}
			);
			
			return false;
			
		}
		
		// ------------------------------------------------------------------------------------------------------------------------------------------------------------- //
		
	};
