Ext.BLANK_IMAGE_URL = '/a/ext/resources/images/default/s.gif';
Ext.USE_NATIVE_JSON = 1;

var debug = 1;

var extDH = Ext.DomHelper;
Ext.DomHelper.useDom = true;
var GetDom = Ext.getDom;

if (typeof console == "undefined")
	var console = {
		log: function() {},
		info: function() {},
		warn: function() {}
	};

function IsArray(obj) {
	return obj instanceof Array;
}

// TODO: fix

function DumpFields (d) {
	var a = [];
	
	if (d instanceof Array) {
		var l = d.length;
		for (i = 0; i < l; i++) {
			a.push (d[k]);
		}
		
		return '[' + a.join (",\n") + ']';
	} else if (d instanceof Object) {
		for (var k in d) {
			if (typeof d[k] != 'function')
				a.push(k + ': "' + d[k] + '"');
		}
		
		return '{' + a.join (",\n") + '}';
	}
}

function DumpKeys (d) {
	var a = [];
	
	if (d instanceof Array) {
		var l = d.length;
		for (i = 0; i < l; i++) {
			a.push (d[k]);
		}
		
		return '[' + a.join (",\n") + ']';
	} else if (d instanceof Object) {
		for (var k in d) {
			a.push(k);
		}
		
		return '{' + a.join (",\n") + '}';
	}
}


function Clone (obj) {
    if (obj == null || typeof (obj) != 'object')
        return obj;

    var temp = new obj.constructor(); // changed (twice)
    for (var key in obj)
        temp [key] = Clone (obj [key]);

    return temp;
}

function Merge (to, from) {
    if (
    	from == null || typeof (from) != 'object'
    	|| to == null || typeof (to) != 'object'
    	// we replace arrays
    	|| to instanceof Array
    ) return from;

    for (var key in from)
        to [key] = Merge (to [key], from [key]);

    return to;
}

function baseUri () {
	
	var proto = 'http://';
	
	if (Ext.isSecure) {
		proto = 'https://';
	}
	
	var port = window.location.port;
	
	var result = proto + web_app_request.host + (
		port ? ':' + port : ''
	) + web_app_request.base_uri;
	
	console.log ('base uri is: ' + result);
	
	return result;
}

function userId () {
	return Ext.getDom ('user-id').innerHTML;
}

function DateCache (date) {
	var d = new Date ();
	if (date)
		d = new Date (date);
	var f = d.format ('Y-m-d');
	var pd = d.add (Date.DAY, -1);
	var pf = pd.format ('Y-m-d');
	return [d, f, pf];
}

Date.shortMonthNames = [
	"янв",
	"фев",
	"март",
	"апр",
	"май",
	"июнь",
	"июль",
	"авг",
	"сен",
	"окт",
	"ноя",
	"дек"
];

Date.monthNames = [
	"января",
	"февраля",
	"марта",
	"апреля",
	"мая",
	"июня",
	"июля",
	"августа",
	"сентября",
	"октября",
	"ноября",
	"декабря"
];

//Date.getShortMonthName = function(month) {
//	return Date.shortMonthNames[month];
//}

function LocalizedTimeForGrid (v) {
	return LocalizedTime (v, window.grid_request_date);
}

function LocalizedTime (v, cache, fraction) {
	
	var request_date  = cache[0];
	var request_day   = cache[1];
	var yesterday_day = cache[2];
	var day = v.format ('Y-m-d');
	
	if (day == '1970-01-01') {
		return '—';
	}
	
	if (request_day == day) {
		return v.format ('H:i, сегодня');
	} else if (day == yesterday_day) {
		return v.format ('H:i, вчера');
	} else if (v.getFullYear() == request_date.getFullYear()) {
		return v.format ('H:i, j F');
	}
	
	return v.format ('H:i, j F Y');
}


function ModalWindow (entity, wConfCustom, pConfCustom, onCreate) {
	
	//entity, w, h, title, contentEl, onCreate
	
	var pConf = {
		autoScroll: true
	};
	
	Ext.apply (pConf, pConfCustom);
	
	pConf.items = false;
	
	var wConf = {
		layout:   'fit',
		plain:    false,
		modal:    true,
		closable: true,
		//title:    title,
		items:    pConf,
		bbar:     new Ext.Toolbar ({}),
		closeAction: 'hide'
	};
	
	Ext.apply (wConf, wConfCustom);
	if (!wConf.id) {
		wConf.id = entity;
	}
	
	if (!window[entity]) {
		
		window[entity] = new Ext.Window(wConf);

		if (onCreate && typeof (onCreate) == 'function') {
			onCreate (window[entity]);
		}
	}
	
	window[entity].on('move',function(el, x, y){
		if (y < 0) {
			el.setPosition(x, 0);
		}
		if (x < 0) {
			el.setPosition(0, y);
		}
		
		var bodySize = Ext.getBody().getSize();
		var elSize = el.getSize();
		
		if (y + elSize.height > bodySize.height) {
			el.setPosition (x, bodySize.height - elSize.height);
		}				
		if (x + elSize.width > bodySize.width) {
			el.setPosition (bodySize.width - elSize.width, y);
		}				
	})
	window[entity].on('show',function(el){
		var bodySize = Ext.getBody().getSize();
		var elSize = el.getSize();
		
		if (elSize.width < wConf.width) {
			el.setWidth (wConf.width);
		}
		if (elSize.height < wConf.height) {
			el.setHeight (wConf.height);
		}
		if (elSize.height + 20 > bodySize.height){
			el.setHeight(bodySize.height - 20);
			el.center();
		}
		if (elSize.width + 20 > bodySize.width){
			el.setWidth (bodySize.width - 20);
			el.center ();
		}
	})
	/*window[entity].on('resize',function(el, width, height){
		var bodySize = Ext.getBody().getSize();
		if(height + 20 > bodySize.height){
			el.setHeight(bodySize.height - 20);
			el.center();
		}
		if(width + 20 > bodySize.width){
			el.setWidth(bodySize.width - 20);
			el.center();
		}				
	})*/
	return window[entity];
	
}

function LoadJSONData (request, object, callback) {
	
	request.success = function (resp, smt) {
		var decodeStatus = 'ok';
		var decoded;
		try {
			decoded = Ext.util.JSON.decode (resp.responseText);
			if (decoded.session_user) {
				var u = decoded.session_user;
				/*GetDom ('balance-amount').textContent
					= parseInt(u.balance_total) + parseInt(u.creditlimit);
				GetDom ('balance-blocked').textContent
					= parseInt(u.balance_blocked);*/
			}
		} catch (err) {
			console.log (resp.responseText);
			decodeStatus = 'parse-fail';
			console.log ('status: ' + decodeStatus + ', catched: ' + err);
		}
		
		callback (object, decodeStatus, decoded);
	};

	request.failure = function (resp, smt) {
		callback (object, 'request-fail', null);
	};
	
	if (object) {
		
		var extObject = Ext.get(object);
		
		if (extObject) {
			if (!extObject.overlay) {
				extObject.overlay = new Ext.LoadMask (extObject.dom.parentNode.parentNode, {
					msg: 'Загрузка …'
				});
			}
			extObject.overlay.show();
		}
	}
	
	Ext.Ajax.request(request);
	
}

function JSONLoaded (object) {
	if (!object) 
		return;
		
	var extObject = Ext.get(object);
	if (extObject && extObject.overlay) {
		extObject.overlay.hide();
	}
}

function FormValuesHash (form) {
	var fields = form.elements;
	var fieldsLength = fields.length;
	
	var hash = {};
	
	for (var i = 0; i < fieldsLength; i++) {
		var f = fields[i];
		
		var n = false;
		var v;
		
		if ((f.nodeName == 'INPUT' && (f.type == 'text'
			|| f.type == 'password' || f.type == 'hidden'))
			|| f.nodeName == 'TEXTAREA'
			|| (f.nodeName == 'INPUT' && f.type == 'checkbox' && f.checked)
			|| (f.nodeName == 'INPUT' && f.type == 'radio' && f.checked)
		) {
			n = f.name;
			v = f.value;
		} else if (f.nodeName == 'SELECT' && f.options[f.selectedIndex]) {
			n = f.name;
			v = f.options[f.selectedIndex].value;
		}
		
		if (n) {
			
			if (hash[n]) {
				hash[n].push (v);
			} else {
				hash[n] = [v];
			}
		}
	}
	
	/*var text = '';
	
	for (var k in hash) {
		text += k + ': ' + hash[k].join (', ') + "\n";
	}*/
	
	return hash;
}

function CleanupForm (form) {
	var fields = form.elements;
	var fieldsLength = fields.length;
	
	for (var i = 0; i < fieldsLength; i++) {
		var f = fields[i];
		
		var n = false;
		var v;
		
		if ((f.nodeName == 'INPUT' && (f.type == 'text'
			|| f.type == 'password' || f.type == 'hidden'))
			|| f.nodeName == 'TEXTAREA'
		) {
			f.value = '';
		} else if (
			(f.nodeName == 'INPUT' && f.type == 'checkbox' && f.checked)
			|| (f.nodeName == 'INPUT' && f.type == 'radio' && f.checked)
		) {
			f.checked = false;
		} else if (f.nodeName == 'SELECT' && f.options[f.selectedIndex]) {
			f.selectedIndex = false;
		}
	}
}

function SwitchSearchMode (span) {
	var li = span.parentNode.parentNode;
	
	if (span.id == 'filter-mode') {
		li.className = 'filters';
	} else {
		li.className = 'search';
	}
	
	var tp = Ext.getCmp ('top-panel');
	tp.setHeight(Ext.getDom ('top').clientHeight);
	
	tp.ownerCt.doLayout();
}

function MakeEl (name, attributes) {
	var el = document.createElement (name);
	if (typeof attributes == 'object') {
		for (var i in attributes) {
			el.setAttribute (i, attributes[i]);

			if (i.toLowerCase() == 'class') {
				el.className = attributes[i];  // for IE compatibility

			} else if (i.toLowerCase() == 'style') {
				el.style.cssText = attributes[i]; // for IE compatibility
			}
		}
	}
	for (var i = 2; i<arguments.length; i++) {
		var val = arguments[i];
		if (typeof val == 'string')
			val = document.createTextNode( val );
		if (el && el.appendChild)
			el.appendChild (val);
	}
	return el;
}

function SetWYSIWYGContent (id) {
	
	var ed; // = tinyMCE.get (id);
	var ta = Ext.get (id).dom;
	var v  = ta.value;
	
	if (ed) {
		ed.setContent( v === null || v === undefined ? '' : v );
		ed.startContent = ed.getContent({ format : 'raw' });
		ed.undoManager.clear();
	}
}

function GetWYSIWYGContent (id) {
	
	var ed; // = tinyMCE.get (id);
	
	if (ed) {
		var c = ed.getContent ();
		if (c === null || c === undefined) {
			return '';
		} else {
			return c;
		}
	}
}

function MakeWYSIWYGFrom (tid, w) {
	var htmlSw = MakeEl (
		'div', {'class': 'html-sw'},
		MakeEl ('input', {type: 'checkbox', id: tid+'-sw', onchange: "SwitchWYSIWYGEditor (this.checked, '"+tid+"')"}),
		MakeEl ('label', {'for': tid+'-sw'}, 'я ламер, дай мне wysiwyg!')
	);
	
	var descrTA = Ext.getDom (tid);
	descrTA.parentNode.insertBefore (htmlSw, descrTA);
	
	// var ed = tinyMCE.get (tid);
	//ed.windowManager = new window.WindowManager (ed);
}

function SwitchWYSIWYGEditor (checked, tid) {
	var ta; // = tinyMCE.get (tid);
	
	//console.log (tid+' => '+DumpFields(ta));
	
	//tinyMCE.getInstanceById(id)
	
	if (checked) {
		//Ext.getDom(tid).value = ta.getContent();
		tinyMCE.execCommand('mceAddControl', false, tid);
		//ta.load();
		//ta.show();
	} else {
		//// tinyMCE.execCommand('mceRemoveControl', false, tid);
		//ta.save();
		//ta.hide();
	}
}

/*var x=new Ext.Tip({
	'html':'lol',
	'style':'border:1px solid #ccc; background:white; padding:1px;'
});*/

function RemoveChildNodes (parent) {
	while (parent.hasChildNodes ()){
		parent.removeChild (parent.childNodes [0])
	}
}

function GetAjaxForm (id) {
	if (window.ajaxForms && window.ajaxForms[id]) {
		return window.ajaxForms[id];
	}
	
	alert ('AjaxForm #'+id+' not found');
}

function AjaxForm (conf) {
	
	// default presentation: window
	if (!conf.presentation)
		conf.presentation = 'window';
		
	if(!conf.staticFields)
		conf.staticFields = new Array();
	
	if(!conf.itemPrefix)
		conf.itemPrefix = '';
		
	if(!conf.entity)
		conf.entity = 'record';
	
	this.config = conf;
	
	this.handlers = conf.handlers;
	
	var formId = conf.formId;
	
	var wrapperId = formId;
	if (conf.wrapperId)
		wrapperId = conf.wrapperId;
	
	if (window.ajaxForms && window.ajaxForms[formId]) {
		window.ajaxForms[formId].handlers = conf.handlers;
		return window.ajaxForms[formId];
	}
	

	
	var extForm = Ext.get (formId);
	if (!extForm) {
		alert('cannot attach AJAX interface to form ' + formId);
		console.log('cannot attach AJAX interface to form ' + formId);
		return ;
	}
	
	this.extForm = extForm;

	var instance = this;
	
	var noOverlay = conf.noOverlay;
	
	var explTag = 'div';
	var explClass = 'explanation';

	if (conf.extend) {
		
		var eFields = conf.extend;
		for (var k in eFields) {
			
			var eFieldType = eFields[k];
			var eFieldOpts = {};
			if (IsArray (eFieldType)) {
				eFieldOpts = eFieldType[1];
				eFieldType = eFieldType[0];
				// console.log ('%%%%%%%%%%'  + DumpFields (eFieldOpts));
			}
			if (eFieldType == 'wysiwyg') {
				MakeWYSIWYGFrom (k);
			} else if (eFieldType == 'date') {
				/*var datePicker = new Ext.DatePicker({
					applyTo: k,
					hidden: true,
					constrainToViewport: false,
					style: {position: 'relative', width: '200px'},
					format: "Y-m-d"
				});
    
				//create button
				var button = Ext.DomHelper.insertAfter (Ext.get (k), {
					'tag':'input',
					'type':'button',
					'value':'…'
				}, true);
				
				button.on ('click', function () {
					//parse date from field
					var dt = new Date();
					dt = Date.parseDate(Ext.getDom(k).value,'Y-m-d')
					
					if (!dt) {
						//set it to datepicker
						dt = new Date();
					}
					
					datePicker.setValue(dt);
					datePicker.on('select',function(el,date){
						Ext.getDom(k).value = date.format('Y-m-d');
						datePicker.hide();
					});
					datePicker.show();
				});
				*/
				
				var dateFieldConf = Merge({
					//allowBlank: false,
					format: 'Y-m-d',
					applyTo: k
				}, eFieldOpts);
				
				console.log (DumpFields (dateFieldConf));
				
				var dateField = new Ext.form.DateField (dateFieldConf);
				
				Ext.getDom (k).parentNode.style.cssText = '';
			}
		}
	} else {
	}

	function valuesHash (form, extend) {
		var fields = form.elements;
		var fieldsLength = fields.length;
		
		var hash = {};
		
		for (var i = 0; i < fieldsLength; i++) {
			var f = fields[i];
			
			var n = false;
			var v;
			
			// textarea preparation
			if (f.nodeName == 'TEXTAREA' && extend && extend[f.id] == 'wysiwyg') {
				var editorSw = Ext.getDom (f.id+'-sw');
				if (editorSw && editorSw.checked) {
					var editor; // = tinyMCE.get(f.id);
					if (editor)
						editor.save();
				}
			}
			
			if ((f.nodeName == 'INPUT' && (f.type == 'text'
				|| f.type == 'password' || f.type == 'hidden'))
				|| f.nodeName == 'TEXTAREA'
				|| (f.nodeName == 'INPUT' && f.type == 'checkbox' && f.checked)
				|| (f.nodeName == 'INPUT' && f.type == 'radio' && f.checked)
			) {
				n = f.name;
				v = f.value;
			} else if (f.nodeName == 'SELECT' && f.selectedIndex >= 0 && f.options[f.selectedIndex]) {
				n = f.name;
				v = f.options[f.selectedIndex].value;
			}
			
			if (n) {
				
				if (hash[n]) {
					hash[n].push (v);
				} else {
					hash[n] = [v];
				}
			}
		}
		
		/*var text = '';
		
		for (var k in hash) {
			text += k + ': ' + hash[k].join (', ') + "\n";
		}*/
		
		return hash;
	}
	
	function submitted (object, result, data, req) {
		//TODO: enable all submits
		//Ext.getDom ('make-dir-1').disabled = false;
				
		if(!data){
			alert('ошибка соединения с сервером, попробуйте еще раз');
			JSONLoaded (wrapperId);
			return result;
		}
		
		var formEl    = object.extForm;
		var formItems = formEl.dom;
		var dataErr   = data.errors;
		if (!dataErr) {
			dataErr = {};
		}
		
		var dataExp = data.explanation;
		if (!dataExp) {
			dataExp = {};
		}
		
		//удаляем описания для generic ошибок
		var tmpEl = formEl.dom.nextSibling;
		while (tmpEl){
			var next = tmpEl.nextSibling;
			if (tmpEl.className == explClass && tmpEl.nodeName == explTag) {
				tmpEl.parentNode.removeChild (tmpEl);
			}
			tmpEl = next;
		}
		
		//добавляем описания для generic ошибок
		if (dataErr.generic) {
			
			var genericErr = dataErr.generic;
			var count = genericErr.length;
			
			for (var e = 0; e < count; e++) {

				var errorContent = '';
				if (dataExp [genericErr [e]]){
					errorContent = dataExp [genericErr[e]];
				} else {
					errorContent = 'CODE: ' + genericErr [e];
				}

				alert (errorContent);
				
				/* extDH.append (formEl.parent(), {
					tag:     explTag,
					'class': explClass,
					html: errorContent
				}); */
			}
		}
		
		//Проверяем поля формы на ошибки
		for (var i in formItems) {
			
			var formItem = formItems [i];
			
			if (!(formItem && typeof formItem == 'object' && typeof formItem.form == 'object'))
				continue;
			
			var el = Ext.get (formItem);
			var next = el.next ();
			var elName = el.dom.name;
			
			// console.log ('form field name: ' + elName);
			
			if (dataErr[elName]) {
			
				el.parent().parent().addClass('error');
				
				var errorContent = '';
				var errorCode = dataErr [elName][1];
				
				console.log ('error code: ' + errorCode);
				
				if (dataExp [errorCode]) {
					errorContent = dataExp [errorCode];
				} else {
					errorContent = 'CODE: ' + errorCode;
				}
				
				if (next) {
					next.textContent = errorContent;
				}
				else {
					
					extDH.insertAfter(el, {
						tag:     explTag,
						'class': explClass,
						html: errorContent
					});
				}
			}
			else {
				el.parent().parent().removeClass('error');
				
				if (next && next.dom.className == explClass && next.dom.localName == explTag) {
					next.remove();
				}
			}
		}
		
		// if (data.result != 'error'){
			if (req.handlers && req.handlers.onResponse) {
				req.handlers.onResponse (object, result, data, instance);
			} else if (object.handlers && object.handlers.onResponse) {
				// global handler overrride by caller
				object.handlers.onResponse (object, result, data, instance);
			}	
		// }
			
		JSONLoaded (wrapperId);
		return result;
		
	};
	
	this.submitted = submitted;
	
	function requestSuccess (resp, req) {
		var decodeStatus = 'ok';
		var decoded;
		try {
			decoded = Ext.util.JSON.decode (resp.responseText);
			if (decoded.session_user) {
				var u = decoded.session_user;
				/*GetDom ('balance-amount').textContent
					= parseInt(u.balance_total) + parseInt(u.creditlimit);
				GetDom ('balance-blocked').textContent
					= parseInt(u.balance_blocked);*/
			}
		} catch (err) {
			console.log (resp.responseText);
			decodeStatus = 'parse-fail';
			console.log ('status: ' + decodeStatus + ', catched: ' + err);
		}
		
		var handler;
		if (req.handlers && req.handlers.response)
			handler = req.handlers.response
		else
			handler = submitted;
		
		handler (instance, decodeStatus, decoded, req);
	}
	
	function requestFailure (resp, req) {
		
		var handler;
		if (req.handlers && req.handlers.response)
			handler = req.handlers.response
		else
			handler = submitted;
		
		handler (instance, 'request-fail', null, req);
	}
	
	function ajaxRequest (request) {
		
		request.success = requestSuccess;
	
		request.failure = requestFailure;
		
		if (request.wrapperId && !noOverlay) {
			var extObject = Ext.get (request.wrapperId);
			
			if (extObject) {
				if (!extObject.overlay) {
					extObject.overlay = new Ext.LoadMask (extObject.dom.parentNode.parentNode, {
						msg: 'Загрузка …'
					});
				}
				extObject.overlay.show();
			}
		}
		
		Ext.Ajax.request (request);
	
	}
	
	var who;
	
	function submit (whoFired) {
		
		who = whoFired;
		
		var form = extForm.dom;
		
		var values = valuesHash (form, conf.extend);

		var action = form.action;
		
		if (instance.handlers.beforeSubmit) {
			var changedAction = instance.handlers.beforeSubmit (values, who, action);
			if (changedAction)
				action = changedAction;
		}
		
		//TODO: disable all submits
		//Ext.getDom ('make-dir-1').disabled = true;
		
		ajaxRequest (
			{
				url: action,
				params: values,
				headers: {
					'X-Request-Type': 'XHR'
				},
				method: form.method
			},
			wrapperId,
			submitted
		);
	};
	
	this.submit = submit;

	function restPut (id, opts) {
		
		if (!opts)
			opts = {};
		
		var form = extForm.dom;
		
		var values = valuesHash (form, conf.extend);

		// RESTful
		var action = form.action + '/' + id;
		values.method = 'put';

		if (!opts.handlers)
			opts.handlers = {};
		
		if (opts.handlers.beforeSubmit && typeof opts.handlers.beforeSubmit == 'function') {
			opts.handlers.beforeSubmit (values);
		}
		
		opts.url     = action;
		opts.params  = values;
		opts.method  = form.method;
		if (!opts.headers)
			opts.headers = {};
		opts.headers['X-Request-Type'] = 'XHR';
		
		opts.handlers.response = submitted;
		
		ajaxRequest (opts);
	};
	
	this.restPut = restPut;

	function restPost (opts) {
		
		if (!opts)
			opts = {};
		
		var form = extForm.dom;
		
		var values = valuesHash (form, conf.extend);

		// RESTful
		var action = form.action;
		values.method = 'post';

		if (!opts.handlers)
			opts.handlers = {};
		
		if (opts.handlers.beforeSubmit && typeof opts.handlers.beforeSubmit == 'function') {
			opts.handlers.beforeSubmit (values);
		}
		
		opts.url     = action;
		opts.params  = values;
		opts.method  = form.method;
		if (!opts.headers)
			opts.headers = {};
		opts.headers['X-Request-Type'] = 'XHR';
		
		opts.handlers.response = submitted;
		
		ajaxRequest (opts);
	};
	
	this.restPost = restPost;
	
	function loaded (object, result, data, req) {
		
		var prefix = conf.itemPrefix;
		
		var record = data [conf.entity];
		
		JSONLoaded (wrapperId);
		
		if (!record) {
			console.log ("load info is unvailable");
			return;
		}			
		
		var elements = extForm.dom.elements;

		for(var i in elements){
			
			var el = elements[i];
			if(!el.name)
				continue;
				
			var elName = el.name.substr(prefix.length);
			if(record[ elName ]){
				el.value = record[ elName ]; 
			}
			
		}
		
		if (req.handlers && req.handlers.onResponse) {
			req.handlers.onResponse (object, result, data, instance);
		}
		
		
		if (!conf.staticFields)
			return;
		
		var cache = DateCache ();
		
		for (var fieldName in conf.staticFields) {

			if (!record [fieldName])
				continue;
			
			var place = Ext.getDom (prefix + fieldName);
			
			if (!place)
				continue;
							
			var type = conf.staticFields[fieldName];
			if (type == 'date'){
				var dateField = Date.parseDate (record[fieldName], 'U');
				// console.log (record[fieldName], dateField);
				place.textContent = LocalizedTime (dateField, cache);
			} else if (type == 'text') {
				place.textContent = record[fieldName];
			}
		}
	}
	
	function restGet (id, opts) {

		if (!opts)
			opts = {};
		
		var form = extForm.dom;
		
		cleanup (form);
		
		// RESTful
		var action = form.action + '/' + id;
		
		if (!opts.handlers)
			opts.handlers = {};
		
		if (opts.handlers.beforeSubmit && typeof opts.handlers.beforeSubmit == 'function') {
			opts.handlers.beforeSubmit (values);
		}
		
		opts.url     = action;
		opts.params  = {method: 'get'};
		opts.method  = form.method;
		if (!opts.headers)
			opts.headers = {};
		opts.headers['X-Request-Type'] = 'XHR';
		
		opts.handlers.response = loaded;
		
		ajaxRequest (opts);
		
	};
	
	this.restGet = restGet;
	
	function cleanup (form) {
		var fields = form.elements;
		
		if (!fields) {
			alert ('no fields');
			return;
		}
		
		var fieldsLength = fields.length;
		
		for (var i = 0; i < fieldsLength; i++) {
			var f = fields[i];
			
			if (!(f && typeof f == 'object' && typeof f.form == 'object' && f.name))
				continue;
			
			var el = Ext.get (f);
			var next = el.next ();
			var elName = el.dom.localName;

			el.parent().parent().removeClass('error');
			
			if (next && next.dom.className == explClass && next.dom.localName == explTag) {
				next.remove();
			}
			
			var n = false;
			var v;
			
			if ((f.nodeName == 'INPUT' && (f.type == 'text'
				|| f.type == 'password' || f.type == 'hidden'))
				|| f.nodeName == 'TEXTAREA'
			) {
				f.value = '';
			} else if (
				(f.nodeName == 'INPUT' && f.type == 'checkbox' && f.checked)
				|| (f.nodeName == 'INPUT' && f.type == 'radio' && f.checked)
			) {
				f.checked = false;
			} else if (f.nodeName == 'SELECT' && f.selectedIndex >= 0 && f.options[f.selectedIndex]) {
				f.selectedIndex = -1;
			}
		}
	}
	
	if (conf.presentation == 'window') {
		
		function windowAfterCreate (w) {
			var bbar = w.getBottomToolbar();
			if (!w.rendered) {
				w.render (Ext.getBody());
				bbar.add (MakeEl ('input', {type: 'button', onclick: "Ext.getCmp('"+w.id+"').hide();", value: 'отмена'}));
				bbar.addFill ();
				for (var k in conf.submitButtonIds) {
					var formSubmitId = conf.submitButtonIds[k];
					if (!formSubmitId)
						continue;
					var formSubmit = Ext.get (formSubmitId);
					if (!formSubmit)
						continue;
					bbar.addElement (formSubmitId);
					formSubmit.on ('click', submit);
				}
			}
		}
		
		var widget;
		
		this.show = function () {
			
			if (!widget) {
			
				widget = new ModalWindow (
					formId + '-window',
					conf.widgetConfig,
					conf.panelConfig,
					windowAfterCreate
				);
			}
			
			cleanup (extForm.dom);
			
			if (this.handlers.beforeShow) {
				this.handlers.beforeShow ();
			}
			
			widget.show ();
		};
		
		this.hide = function () {
			if (widget && conf.presentation == 'window') {
				widget.hide ();
			}
		}
		
		extForm.on ('submit', submit);
		
	}
	
	if (!window.ajaxForms)
		window.ajaxForms = [];
	
	window.ajaxForms[formId] = this;
	
	return this;
}


/**
 * клас для вывода тултипа
 * */
function createTip(options){
	if(!options.align)
		options.align='auto';
	if(!options.valign)
		options.valign='auto';
	if(!options.html)
		options.html='lol :) - this is example of tooltip';
		
	var el=Ext.get(options.el);
	var elSize=el.getSize();
	var elPos={
		top:el.getTop(),
		left:el.getLeft()
	};
	var bodySize=Ext.get(document.body).getSize();
	
	if(options.align=='auto'){
		if(elPos.left + elSize.width/2 < bodySize.width/2)
			options.align='left';
		else
			options.align='right';	
	}
	if(options.valign=='auto'){
		if(elPos.top + elSize.height/2 < bodySize.height/2)
			options.valign='top';
		else
			options.valign='bottom';	
	}
		
		
	var content=options.html;
	switch(options.valign){
		case 'top':{
			switch (options.align) {
				case 'left':{
					content='<div class="topTriangle" style="position:absolute;left:0;top:-10px;width:200px;height:10px;background:url(/a/i/triangle.gif) left 0px no-repeat;"></div>'+content;
				}
				break;
				case 'right':{
					content='<div class="topTriangle" style="position:absolute;left:0;top:-10px;width:200px;height:10px;background:url(/a/i/triangle.gif) right 0px no-repeat;"></div>'+content;
				}
				break;
			}			
		}
		break;
		case 'bottom':{
			switch (options.align) {
				case 'left':{
					content+='<div class="bottomTriangle" style="position:absolute;left:0;bottom:-10px;width:200px;height:10px;background:url(/a/i/triangle.gif) left -30px no-repeat;"></div>';
				}
				break;
				case 'right':{
					content+='<div class="bottomTriangle" style="position:absolute;left:0;bottom:-10px;width:200px;height:10px;background:url(/a/i/triangle.gif) right -30px no-repeat;"></div>';
				}
				break;
			}
		}
		break;
	}
	var optTip={
		'html':content,
		'width':200
		//'style':'padding:10px 0;'
	};
	if(options.renderTo)
		optTip.renderTo=options.renderTo;
		
	var x=new Ext.Tip(optTip);
	
	x.showAt([ elPos.left , elPos.top ]);
	
	var xSize=x.getSize();
	
	
	
	switch(options.valign){
		case 'top':{
			switch (options.align) {
				case 'left':{
					x.showAt([ elPos.left + elSize.width/2 , elPos.top + elSize.height + 10]);
				}
				break;
				case 'right':{
					x.showAt([ elPos.left + elSize.width/2 - xSize.width + 10 , elPos.top + elSize.height + 10]);
				}
				break;
			}
		}
		break;
		case 'bottom':{
			switch (options.align) {
				case 'left':{
					x.showAt([ elPos.left + elSize.width/2 , elPos.top - xSize.height - 10]);
				}
				break;
				case 'right':{
					x.showAt([ elPos.left + elSize.width/2 - xSize.width + 10 , elPos.top - xSize.height - 10]);
				}
				break;
			}
		}
		break;
	}
	
	x.hide();
	return x;
}

/*
var x=createTip({
	//'renderTo':'feedback-form',
	'el':'feedback-submit-1'
});

x.show();

var x2=createTip({
	'el':'ext-gen21',
	'html':'тест продажи'
});

x2.show();
 */
