Function.prototype.bind = function(scope, args){ 
	var _this = this;
	return function() { 
		return _this.apply(scope, args ? args : []); 
	};
};
/**
 * Single global object used by library. It contain all classes and namespaces.
 * @module ITc
 * @class ITc
 * @static
 */
var ITc = {
	/**
	 * Merge properties of object Overwrite to object Original. If Defaults 
	 * @method apply
	 * @param Original {Object}
	 * @param Overwrite {Object}
	 * @param Defaults {Object} defaults properties from this object will be set to the Original object if they are not defined in the Original & Overwrite object
	 * @return {Object}
	 */
	apply: function(Original, Overwrite, Defaults){
		var p;
		if (!Original) Original = {};
		if (Defaults) {
			for(p in Defaults){
				if (Original[p] === undefined) {
					Original[p] = Defaults[p];
				};
			};
		};
		if (!Overwrite || typeof Overwrite != 'object') return Original;
		for(p in Overwrite){
			Original[p] = Overwrite[p];
		};
		return Original;
	},
	
	/**
	 * Extend class from Parent object.
	 * @param NC {Object|Null} object for new class (namespace), should be passed if construct is defined
	 * @param EC {Object} class that should be extended
	 * @param Over {Object} new properties/methods that should be defined/rewrited
	 * @returns {Object} link to the new class
	 */
	extendClass:function(NC, Parent, Over){
		if (typeof NC != 'function'){
			NC = function(){
				NC.superclass.apply(this,arguments);
			};
		};
		NC.prototype = ITc.apply({}, Parent.prototype);
		if (Over) ITc.apply(NC.prototype, Over);
		NC.superclass = Parent;
		return NC;
	},

	/**
	 * Create a copy of the object.
	 * @param Obj {Object}
	 * @returns {Object}
	 */
	clone: function(o) {
		if(!o || 'object' !== typeof o) {
			return o;
		}
		if('function' === typeof o.clone) {
			return o.clone();
		}
		var c = '[object Array]' === Object.prototype.toString.call(o) ? [] : {};
		var p, v;
		for(p in o) {
			if(o.hasOwnProperty(p)) {
				v = o[p];
				if(v && 'object' === typeof v) {
					c[p] = ITc.clone(v);
				}else {
					c[p] = v;
				};
			};
		};
		return c;
	},

//TODO replace!!
	callCallback: function(callback, scope, args) {
		if (callback) {
			var fn = null;
			var sc = null;
			if (typeof callback == 'function') {
				fn = callback;
			} else if (callback.fn) {
				fn = callback.fn;
				if (callback.scope) {
					scope = callback.scope;
				}
			}
			if (fn) {
				fn.apply(scope ? scope : window, args);
			}
		}
	},
	//define namespaces
	utils: {},
	ui: {},
	format: {},
	
/***** document ready event processing [start] *****/
	rList: [],
	onReady: function(fn, scope) {
		if (scope) {
			this.rList.push(fn.bind(scope));
		} else {
			this.rList.push(fn);
		};
	},
	_onPLoad: function() {
		for(var i=0; i<this.rList.length; i++) {
			this.rList[i]();
		}
	}
/***** document ready event processing [end] *****/
};
//TODO change this!!!
jQuery(ITc._onPLoad.bind(ITc));

/**
 * 
 */
ITc.getEl = function(id, jQuery){
	if (id) {
		if (jQuery) {
			if (typeof id == 'string') {
				return $('#'+id);
			} else {
				return $(id);
			};
		};
		if (typeof id == 'string') {
			return document.getElementById(id);
		} else if (id.tagName) {
			return id;
		} else if (id.length) {
			return id[0];
		};
	};
	return null;
};

/**
 * 
 */
ITc.createElement = function(Conf,TargetNode,appendMode){
	var tagName = typeof Conf == 'string' ? Conf : null;
	if (typeof Conf == 'string') {
		var tagName = Conf;
	} else {
		if (Conf.tag){
			var tagName = Conf.tag;
		};
	};
	
	if (tagName) {
		var N = document.createElement(tagName);
		if (typeof Conf == 'object'){
			var cProp, jsName,
				aliases = {
					'cls':'className',
					'class':'className',
					'html':'innerHTML'
				};
			
			for(cProp in Conf){
				jsName = aliases[cProp] ? aliases[cProp] : cProp;
				
				if (jsName == 'ignore'){
					continue;
				};
				
				if (typeof Conf[cProp] == 'object'){
					ITc.apply(N[jsName], Conf[cProp]);
				} else {
					N[jsName] = Conf[cProp];
				};
			};
		};
		
		if (TargetNode){
			if (typeof TargetNode == 'srtring'){
				TargetNode = ITc.getEl(TargetNode);
				if (!TargetNode){
					return N;
				};
			};
			if (!appendMode) {
				appendMode = 'append';
			};
			switch(appendMode){
			case 'before':
				TargetNode.parentNode.insertBefore(N,TargetNode);
				break;

			case 'after':
				TargetNode.parentNode.insertBefore(N,TargetNode.nextSibling);
				break;
			
			case 'asFirstNode':
				if (TargetNode.firstChild){
					TargetNode.insertBefore(N,TargetNode.firstChild);
				} else {
					TargetNode.appendChild(N);
				};
				break;
				
			case 'append':
			default:
				TargetNode.appendChild(N);
				break;
			};
		};
		return N;
	};
	return null;
};

/**
 * Base class that implement event model.
 * Allow overwrite any method of class via config and add listeners for events via 'listeners' key.
 * @example var Ex = new Class1({
 * 		width:200,
 * 		listeners:{
 * 			event1:function(){
 * 				//debug for event 1
 * 			},
 * 			event2:function(){
 * 				//debug for event 2
 * 			},
 * 			scope: SomeElement
 * 		},
 * 		method1:function(){
 * 			//debug for method 1
 * 		}
 * });
 * Variable Ex will have 2 listeners one for 'event1' and one for 'event2'. Also it will has new 'method1' (Of course if class 'Class1' extends ITc.Component).
 * Also listeners for events can be added in following way:Ex.on('EventName',FUNCTION,SCOPE);
 */
ITc.Component = function(config){
	if (config) {
		ITc.apply(this, config);
		if (this.listeners){
			var i,
				scope = this.listeners.scope ? this.listeners.scope : window;
			for(i in this.listeners){
				if (i == 'scope') continue;
				this.on(i, this.listeners[i], scope);
			};
		};
	};
	this._initComponent();
};
ITc.Component.prototype = {
	fireEvent:function(eventName){
		var i,narg = [];
		for(i=1;i<arguments.length;i++){
			narg.push(arguments[i]);
		};
		$(this).triggerHandler(eventName.toLowerCase(), narg);
	},
	on:function(eventName,callback,scope){
		$(this).bind(eventName.toLowerCase(), scope ? function(){
			//will remove 1-st argument Event object generated by JQuery
			var args = [],i;
			for(i=1; i<arguments.length;i++){
				args.push(arguments[i]);
			};
			callback.apply(scope, args);
		} : callback);
	},
	_initComponent:function(){}
	//,destructor:function(){}
};

/**
 * static functions for debugging
 */
ITc.debug = {
	on:false,
	mode:'console',
	show:function(val, mode){
		if (!this.on) return;
		if (!mode) mode = this.mode;
		
		if (mode == 'console'){
			try{
				if (console) console.info(val);
			}catch(e){
				
			};
			return;
		} else if (mode == 'log'){
			return this.log(val);
		};
		alert('DEBUG: ' + val);
	},
	log:function(val){
		if (!this.on) return;
		var logDivId = 'debugLogDiv',
			Ld = ITc.getEl(logDivId);
		if (!Ld) {
			var Actions = ITc.createElement({
				tag:'div',
				style:{
					'border':'1px solid #EE0000',
					'borderBottom':'none'
				},
				html:'<a>Clear</a>'
			},document.body);
			
			Ld = ITc.createElement({
				tag:'div',
				id:logDivId,
				style:{
					padding:'5px',
					background:'#FFF',
					border:'1px solid #EE0000',
					height:'50px',
					overflow:'auto'
				}
				
				
			},document.body);
		};
		
		ITc.createElement({
			tag:'div',
			html:val,
			style:{
				'borderBottom':'1px solid #CCC'
			}
		}, Ld, 'asFirstNode');
	},
	throwException:function(message,type){
		if (!type){
			type = 'trace';
		};
		message = '['+type+']'+message;
		switch(type){
		case 'fatal':
			throw(message);
			//this.show(message);
			//console.trace();
			break;

		case 'notice':
			this.show(message);
			break;

		case 'trace':
		default:
			this.show(message);
			break;
		};
	}
};
/**
 * allow perform AJAX requests
 * @param srting url
 * @param object options
 * allowed options :
 * 		- parameters - object with parameters that should be passed to the server 
 * 		- onSuccess - function that will call on success (response from server contain 'success' property equal to true)
 * 		- onFail - function that will be call on fail
 * 		- scope - scope for onSuccess callback
 * 		- fscope - scope for onFail callback (if not defined scope will be used)
 * 		- requestType - {html,json}, 'json' is default
 * @exmple:
	ITc.request('/ajax/login.php',{
		maskEl:'testDiv1',
		parameters: {cmd:'login'},
		requestType: 'html',
		onSuccess: function(Result,Action) {
			//debug arguments
		},
		onFail: function(Result,Action) {
			//debug arguments
		},
		scope: window,
		fscope: window
	});
 * 
 * 
 */
ITc.request = function(url, Options){
	
	if(!url || url == '') return;
	if (!Options) Options = {};
	
	if (Options.maskEl){
		ITc.mask.show(Options.maskEl);
	};
	var RH = new ITc.request.ResponseHandler(Options);
	$.post(
		url,
		Options.parameters ? Options.parameters : null,
		function(){
			RH.processResponse.apply(RH, arguments);
		}
	);
};
ITc.request.ResponseHandler = function(Options){
	this.O = Options ? Options : {};
};
ITc.request.ResponseHandler.prototype = {
	reqFailMessage: 'This request has failed.  Please try later.',
	O: {},
	processResponse:function(response,is_success){
		var jsonObj;
		if (this.O.maskEl){
			ITc.mask.hide(this.O.maskEl);
		};
		if (is_success == 'success') {
			if (this.O.requestType && this.O.requestType == 'html') {
				jsonObj = {
					success: true
				};
			} else {
				try{
					eval('jsonObj = ' + response);
				} catch(exep){
				};
			};
		};
		var Action = {
			result: jsonObj,
			responseText: response,
			options: this.O
		};
		if (jsonObj) {
			var msg_text, msgs;
			if (jsonObj.success) {
				if (jsonObj.messages) msgs = jsonObj.messages;
			} else {
				if (jsonObj.errors) msgs = jsonObj.errors;
			}
			if (msgs) {
				if (msgs instanceof Array) {
					msg_text = msgs.join('<br />');
				} else if (typeof msgs == 'object') {
					msg_text = '';
					for(var k in msgs) {
						msg_text += msgs[k];
					};
				} else {//typeof msgs == 'string'
					msg_text = msgs;
				};
				if (msg_text) {
					if (jsonObj.success) {
						ITc.ui.alert.show(msg_text, null,
							this.internalOnSuccess.bind(this, [Action])
						);
					} else {
						ITc.ui.alert.show(msg_text, 'Error',
							this.internalOnFail.bind(this, [Action])
						);
					};
					return;
				};
			};
			if (jsonObj.success) {
				this.internalOnSuccess(Action);
			} else {
				this.internalOnFail(Action);
			};
			return;
		};
		ITc.ui.alert.show(this.reqFailMessage, 'Error',
			this.internalOnFail.bind(this, [Action])
		);
	},
	internalOnSuccess:function(Action){
		var O = this.O;
		if (O.onSuccess && typeof O.onSuccess == 'function') {
			O.onSuccess.apply((O.scope ? O.scope : window), [Action.result, Action]);
		};
	},
	internalOnFail:function(Action){
		var O = this.O;
		if (O.onFail && typeof O.onFail == 'function') {
			O.onFail.apply((O.fscope ? O.fscope : (O.scope ? O.scope : window)), [Action.result, Action]);
		};
	}
};

ITc.loadElContent = function(el, url, options){
	if (!options) options = {};
	
	var updaterObj = {
		domEl: ITc.getEl(el), 
		onSuccess: {
			fn:options.onSuccess ? options.onSuccess : null,
			scope:options.scope ? options.scope : null
		},
		onFail: {
			fn:options.onFail ? options.onFail : null,
			scope:options.fscope ? options.fscope : (options.scope ? options.scope : null)
		},
		callOriginalCallback:function(args, isSuccess){
			if (isSuccess) {
				ITc.callCallback(this.onSuccess, null, args);
			} else {
				ITc.callCallback(this.onFail, null, args);
			};
		}
	};
	if (!updaterObj.domEl){
		return;
	};
	var nop = {
		parameters: options.parameters ? options.parameters : null,
		requestType: 'html',
		onFail: function(r,action){
			this.callOriginalCallback(arguments, false);
		},
		onSuccess: function(r,action){
			$(this.domEl).html(action.responseText);
			this.callOriginalCallback(arguments, true);
		},
		scope:updaterObj,
		fscope:updaterObj
	};
	if (!options.withoutMask) {
		nop.maskEl = options.maskEl ? options.maskEl : el;
	};
	ITc.request(url, nop);
};
/**
 * functions for showing/hiding masks
 */
ITc.mask = {
	/**
	 * show mask for element
	 * @access public
	 * @param String|DomNode el if string - should be id of the element
	 * @returns void
	 */
	show: function(el){
		el = ITc.getEl(el);
		if(!el || el.mask_element) return;
		var element_loader = $('<div class="ui-widget-overlay"><div class="loading-indicator"></div></div>');// itc-mask
		if ($.browser.msie && $.browser.version < 7) {
			var w = el.offsetWidth;
			element_loader.css({
				'height':h+'px',
				'width':w+'px'
			});
		}
		if (el.style.position != 'relative') {
			el.style.position = 'relative';
		}
		element_loader.appendTo(el);
		el.mask_element = ITc.getEl(element_loader);
		//el.appendChild(el.mask_element);
	},
	
	/**
	 * remove mask from element
	 * @access public
	 * @param String|DomNode el if string - should be id of the element
	 * @returns void
	 */
	hide: function(el) {
		el = ITc.getEl(el);
		if(el && el.mask_element){
			var mel = el.mask_element;
			if (mel.parentNode) {
				mel.parentNode.removeChild(mel);
			};
			el.mask_element = null;
		};
	}
};
/**
 * Class for configurations objects processing
 * allow get/set configuration values in the easy way
 */
ITc.Config = function(config){
	if (config) {
		this.setConfig(config);
	};
};
ITc.Config.prototype = {
	conf:{},
	pathDelimiter:'->',
	/**
	 * method allow set current value for config
	 * @access public
	 * @param Object config
	 * @returns void
	 */
	setConfig:function(config){
		this.conf = config && typeof config == 'object' ? config : {};
	},
	
	/**
	 * method allow get value of some key
	 * @access public
	 * @param string key
	 * @param Array|string path if string used each key should be separated by 'pathDelimiter'
	 * @param Mixed defaultValue value that will be returned in case if key does not exist
	 * @returns Mixed
	 */
	getVal:function(key, path, defaultValue){
		if (!key && !path){
			return this.conf;
		};
		
		return this._getter(
			path ? this._processPath(path,null) : this.conf,
			key,
			defaultValue
		);
	},
	
	/**
	 * method allow set value of some key
	 * @access public
	 * @param string key
	 * @param Mixed val
	 * @param Array|string path if string used each key should be separated by 'pathDelimiter'
	 * @returns bool
	 */
	setVal:function(key, val, path){
		var cO = this.conf;
		if (path) {
			cO = this._processPath(path, null, true);
		};
		if (cO){
			cO[key] = val;
			return true;
		};
		return false;
	},
	
	convertObjectToArray: function(obj){
		var r = [];
		if (!obj) return r;
		for(var i in obj) {
			r.push([i, obj[i]]);
		};
		return r;
	},
	
	_getter:function(ob,key,defaultValue){
		if (ob && typeof ob == 'object' && key){
			if (ob[key] !== undefined){
				return ob[key];
			};
		};
		return defaultValue;
	},
	_processPath:function(path,defaultValue,allowCreateNew){
		if (!(path instanceof Array)) {
			path = path.split(this.pathDelimiter);
		};
		if (path.length < 1){
			return defaultValue;
		};
		var i,
			ctmp,
			cO = this.conf;
		for(i=0; i<path.length; i++){
			ctmp = this._getter(cO,path[i],null);
			if (!ctmp || typeof ctmp != 'object'){
				if (allowCreateNew){
					cO[path[i]] = {};
					cO = cO[path[i]];
				} else {
					return defaultValue;
				};
			} else {
				cO = ctmp;
			};
		};
		return cO;
	}
};
ITc.utils.pageConfig = {
	C:null,
	getVal: function(key, path, defaultValue){
		if (this._init()){
			return this.C.getVal(key, path, defaultValue);
		};
		return defaultValue;
	},
	setVal: function(key, value, path){
		if (this._init()){
			return this.C.setVal(key, value, path);
		};
		return false;
	},
	setConfig:function(PageConfig){
		if (this._init()){
			return this.C.setConfig(PageConfig);
		};
		return false;
	},
	convertObjectToArray:function(Obj){
		if (this._init()){
			return this.C.convertObjectToArray(Obj);
		};
		return false;
	},
	_init:function(){
		if (!this.C){
			try{
				if (PageConfig){
					this.C = new ITc.Config(PageConfig);
					return true;
				};
			}catch(e){
			};
			this.C = new ITc.Config({});
		};
		return true;
	}
};
//ITc.PCfg = ITc.utils.pageConfig;
/**
 * Class Dialog
 * 
 * supported events:
 * 'onInited'
 * 'onRender'
 * 'onShow'
 * 'onClose'
 * 'onSuccessSubmit'
 */
ITc.ui.Dialog = ITc.extendClass(null, ITc.Component, {
	/**
	 * list of options those should be passed to $.dialog function
	 */
	dProperties:{
		dialogClass:1,
		width:1,
		height:1,
		minHeight:1,
		autoOpen:1,
		resizable:1,
		position:1,
		modal:1
	},
	dialogClass:'light-dialog',
	width:580,
	autoOpen:false,
	resizable:false,
	modal:true,
	position:'center',
	minHeight:60,
	
	distroyOnClose:false,
	
	title:null,
//	Title:null,
	
	autoCenter:true,
	
	style:null,
	
	buttons:null,
	buttonDefaultConf:{
		cls:'form_button'
	},
	ButtonsPanel:null,
	buttonsPanelClass:'btn-cont',
	
	/**
	 * allow set up parameters for ITc.loadElContent(DialogBody,url,options)
	 * after content will be loaded 'onRender' event will be fired
	 */
	loadBody:{
		url:null,
		options:null
	},
	
	El:null,
	rendered:false,
	
	AjaxForm:null,
	formResetTitles:false,
	
	show:function(){
		if (!this.El)return;
		this.El.dialog('open');
		this.fireEvent('onShow',this);
	},
	close:function(){
		if (!this.El)return;
		this.El.dialog('close');
		this.fireEvent('onClose',this);
		if (this.distroyOnClose){
			this.destroy();
		};
	},
	submitForm:function(){
		var AF = this.getAjaxForm(true);
		if (!AF) return;
		AF.submit();
	},
	getAjaxForm:function(need_reload){
		if (!this.El)return null;
		if (!this.AjaxForm || need_reload){
			var formDom = ITc.getEl(this.El.find('form'));
			if (!formDom){
				return null;
			};
			this.AjaxForm = new ITc.ui.AjaxForm({
				form:formDom,
				resetTitles:this.formResetTitles,
				listeners:{
					success:function(Form, action){
						this._onFormSuccessSubmit.apply(this, arguments);
					},
					scope:this
				}
			});
		};
		return this.AjaxForm;
	},
	
	_onFormSuccessSubmit:function(Form, action){
		this.fireEvent('onSuccessSubmit', this, Form, action);
	},
	
	_initComponent:function(){
		this.rendered = false;
		this._initDialog();
	},
	_initDialog:function(){
		if (!this.El){
			this.El = $('<div></div>');
		} else if (typeof this.El == 'string') {
			this.El = $(this.El);
		} else {
			var domEl = ITc.getEl(this.El);
			if (!domEl){
				this.El = null;
				return null;
			};
			this.El = $(domEl);
		};

		var Conf = {},
			pKey;
		for(pKey in this.dProperties){
			if (this[pKey] !== undefined){
				Conf[pKey] = this[pKey];
			};
		};

		if (this.style){
			this.El.css(this.style);
		};
		this.El.dialog(Conf);

		this.fireEvent('onInited',this);
		this._render();
	},
	_render:function(){
		if (this.rendered) {
			return;
		};
		this._titleRender();
		this._buttonsRender();
		if (this.loadBody && this.loadBody.url){
			this._loadBody(this.loadBody.url, this.loadBody.options ? this.loadBody.options : null, true);
		} else {
			this._afterRender();
		};
	},
	_afterRender:function(){
		if (!this.rendered) {
			this.rendered = true;
			if (this.autoCenter){
				this.El.dialog('option','position','center');
			};
			this._initActions();
			this.fireEvent('onRender',this);
		};
	},
	_titleRender:function(){
		if (!this.title) return;
		$(".ui-dialog-title").html('<b>'+this.title+'</b>');
	},
	_buttonsRender:function(){
		if (!this.buttons) return;
		this.ButtonsPanel = $('<div></div>').insertAfter(this.El);
		if (this.buttonsPanelClass){
			this.ButtonsPanel.addClass(this.buttonsPanelClass);
		};
		if (!(this.buttons instanceof Array)){
			this.buttons = this._buttonsConvertConfig(this.buttons);
		};
		var i;
		for(i=0;i<this.buttons.length;i++){
			this._buttonsRenderItem(this.buttons[i]);
		};
		$('<div class="clear"></div>').appendTo(this.ButtonsPanel);
	},
	_buttonsRenderItem:function(btnConfig){
		if (!this.ButtonsPanel || !btnConfig) return;
		if (this.buttonDefaultConf){
			btnConfig = ITc.apply({},btnConfig,this.buttonDefaultConf);
		};
		
		var B = $('<input type="button" value="'+btnConfig.text+'" />').appendTo(this.ButtonsPanel);
//		var B = $('<a>'+btnConfig.text+'</a>').appendTo(this.ButtonsPanel);
		if (btnConfig.cls){
			B.addClass(btnConfig.cls);
		};
		if (btnConfig.style){
			B.css(btnConfig.style);
		};
		if (btnConfig.handler){
			if (typeof btnConfig.handler == 'function'){
				B.click(btnConfig.handler.bind(this.El[0]));
			} else {
				if (!btnConfig.handler.scope){//if scope don't set - set dialog
					btnConfig.handler.scope = this;
				};
				B.click(function(){
					ITc.callCallback(btnConfig.handler, null, arguments);
				});
			};
		};
	},
	_buttonsConvertConfig:function(btnsConfig){
		var btns = [],title;
		for(title in btnsConfig){
			btns.push({
				text:title,
				handler:btnsConfig[title]
			});
		};
		return btns;
	},
	_loadBody:function(url, options, fireRenderEvent){
		if (!url)return;
		if (!options){
			options = {};
		};
		if (fireRenderEvent){
			if (this.rendered){
				this.rendered = false;
			};
			var orignialCallback = null;
			if (options.onSuccess){
				var orignialCallback = {
					fn: options.onSuccess,
					scope: options.scope ? options.scope : null
				};
			};
			options.onSuccess = function() {
				if (orignialCallback){
					ITc.callCallback(orignialCallback,null,arguments);
				};
				this._afterRender();
			};
			options.scope = this;
		};
		ITc.loadElContent(this.El, url, options);
	},
	destructor:function() {
		if (this.El){
			this.El.dialog('destroy');
			this.El.remove();
		};
	},
	destroy:function() {
		this.destructor();
	},
	/*** helper methods [start] ***/
	fileSelectorConfig: {
		el_selector: '.file-selector',
		image: '/img/upload_button.png',
		imageheight: 29,
		imagewidth: 77,
		width: 316
	},
	_renderFileSelectors:function(config) {
		if (!this.El) return;
		if (!config) config = {};
		
		config = ITc.apply(ITc.clone(this.fileSelectorConfig),config);
		this.El.find(config.el_selector).filestyle(config);
	},
	
	_renderSelectoxes:function(selector, config){
		if (!selector) selector = 'select';
		var Sel = this.El.find(selector);
		if (Sel.length < 1) return;
		Sel.selectbox(config ? config : {});
		if (this.El.css('overflow') != 'visible') {
			this.El.css({
				overflow:'visible'
			});
			this.El.parent().css({
				overflow:'visible'
			});
		};
	},
	
	_initActions:function(selector) {
		if (!selector) selector = 'a[hmethod]';
		var Self = this;
		this.El.find(selector).click(function(e){
			e.preventDefault();
			var B = $(this),
				method = B.attr('hmethod');
			if (typeof Self[method] == 'function') {
				Self[method].apply(Self);
			};
		});
	}
	/*** helper methods [end] ***/
});
ITc.ui.alert = {
	/**
	 * @method Show
	 */
	show:function(msg, title, handler, scope){
		new ITc.ui.Dialog({
			El:'<div>'+msg+'</div>',
			title: title ? title : 'Message',
			width: 400,
			autoOpen:true,
			distroyOnClose:true,
			buttons: [
				{
					text: 'Ok',
					cls: 'radius_5', //'rbutton small red right',
					handler:{
						fn:function(){
							this.close();
						}
					}
				}
			],
			listeners:{
				onOkButton: handler ? handler : function(){},
				onClose: function(D){
					D.fireEvent('onOkButton',D);
				},
				scope: scope
			}
		});
	}
};
ITc.ui.confirm = {
	show: function(msg, title, okAction, scope, cancelAction, Btns){
		if (!msg) return;
		var CancelButton = {
				text:'Cancel',
				cls: 'radius_5',//'rbutton small grey right',
				handler:{
					fn: function(){
						this.close();
					}
				}
			},
			OkButton = {
				text:'Ok',
				cls: 'radius_5',//'rbutton small red right',
				handler:{
					fn:function(){
						this.okButtonPressed = true;
						this.close();
					}
				}
			};
		if (Btns){
			if (Btns.ok) {
				if (typeof Btns.ok == 'string'){
					OkButton.text = Btns.ok;
				} else {
					ITc.apply(OkButton, Btns.ok);
				};
			};
			if (Btns.cancel) {
				if (typeof Btns.cancel == 'string'){
					CancelButton.text = Btns.cancel;
				} else {
					ITc.apply(CancelButton, Btns.cancel);
				};
			};
		};
		
		var D = new ITc.ui.Dialog({
			El: '<div>'+msg+'</div>',
			title: title ? title : 'Confirm',
			width: 400,
			buttons: [
				CancelButton,OkButton
			],
			listeners:{
				onOkButton: okAction ? okAction : function(){},
				onCancelButton: cancelAction ? cancelAction : function(){},
				onClose: function(D){
					if (D.okButtonPressed){
						D.fireEvent('onOkButton',D);
					} else {
						D.fireEvent('onCancelButton',D);
					};
					D.destroy();
				},
				scope: scope
			}
		});
		D.show();
	}
};
ITc.ui.AjaxForm = ITc.extendClass(null, ITc.Component, {
	//form HTML dom element
	form: null,

	inputErrorClass: 'field-invalid',

	divErrorClass: 'error',

	maskEl: null,

	resetTitles: false,

//TODO resolve with this
	use_error_flags: true,

	submit: function() {
		this._onSubmit();
	},
	setValues: function(formValues){
		if (!formValues) return;
		var f, i, map = this._getRealFieldSet();
		for (i in formValues){
			if (!map[i]) continue;
			f = map[i];
			//special for jquery
			if (f.type == 'checkbox') {
				f.checked = formValues[i] ? true : false;
			} else {
				$(f).val(formValues[i]);
			};
		};
	},
	resetFieldsMap: function() {
		if (this.fieldsMap) {
			this.fieldsMap = null;
		};
	},
	resetErrors: function() {
		this._setupErrors({});
	},
	reset: function() {
		this.form.reset();
		this._setupErrors({});
	},
	
	_initComponent: function() {
		this.form = ITc.getEl(this.form);
		if (!this.form) {
			ITc.debug.show('FATAL - FORM element not found!!');
			return false;
		};
		if(!this.maskEl){
			this.maskEl = this.form;
		};
		this.form.onsubmit = this._onSubmit.bind(this);
	},
	_onSubmit: function() {
		if (!this.form){
			return false;
		};
		ITc.mask.show(this.maskEl);
		
		if (this.resetTitles) {
			var field,k,fm = this._getRealFieldSet();
			for(k in fm) {
				field = fm[k];
				if (field.title && (field.type == 'text' || field.type == 'textarea') && field.title == field.value) {
					field.value = '';
				};
			};
		};
		
		if (this.form.enctype == 'multipart/form-data') {
			this._onSendMultypartForm();
		} else {
			var data = $(this.form).serialize();
			var _this = this;
			$.post(this.form.action, data, function(){_this._onRequestComplete.apply(_this, arguments);});
		};
		
		if (this.resetTitles) {
			for(k in fm) {
				field = fm[k];
				if (field.title && (field.type == 'text' || field.type == 'textarea') && field.value == '') {
					field.value = field.title;
				};
			};
		};
		return false;
	},
	
	_onSendMultypartForm: function(){
		/*** iframe creation ***/
		var id = 'iframe_upload_files';
		if (!ITc.getEl(id)) {
			var frame = document.createElement('iframe');
			frame.id = id;
			frame.name = id;
			frame.style.display = 'none';
			var isIE = $.browser.msie;
			if(isIE){
				frame.src = 'javascript:false;';
			};
			document.body.appendChild(frame);
			if(isIE){
				document.frames[id].name = id;
			};
			var _onLoadCallback = function(){
				var responseText = null;
				try {
					var doc = frame.contentDocument ? frame.contentDocument : frame.contentWindow.document;
					if(isIE){
						var doc = frame.contentWindow.document;
					}else {
						var doc = (frame.contentDocument || window.frames[id].document);
					}
					if(doc && doc.body){
						responseText = doc.body.innerHTML;
					}
				} catch(e) {// ignore
				}
				this._onRequestComplete(responseText, responseText ? 'success' : 'fail');
			};
			var _this = this;
			$(frame).load(function() {
				setTimeout(_onLoadCallback.bind(_this), 200);
			});
			this.form.target = id;
		}
		this.form.submit();
	},
	
	_onRequestComplete: function(response, is_success) {
		var R;
		if (is_success == 'success') {
			if (response instanceof Object) {
				R = response;
			} else {
				try{
					eval('R = ' + response);
				} catch(exep){
				}
			}
		};

		var action = {
			result: R,
			responseText: response
		};
		ITc.mask.hide(this.maskEl);
		if (!R){
			ITc.ui.alert.show('Request has Failed. Please, try later.', 'Error',
				this._OnFail.bind(this, [action])
			);
			return;
		};

		var msgs, msg_text;
		if (R.success) {
			if (R.messages) {
				msgs = R.messages;
			};
			this.resetErrors();
		} else if (R.errors) {
			if (R.errors instanceof Array) {
				msgs = R.errors;
			} else if (typeof R.errors == 'object') {
				msgs = this._setupErrors(R.errors);
			} else {
				msgs = R.errors;
			};
		};
		
		if (msgs) {
			if (msgs instanceof Array) {
				msg_text = msgs.join('<br />');
			} else if (typeof msgs == 'object') {
				msg_text = '';
				for(var k in msgs) {
					if (msg_text) {
						msg_text += '<br />';
					};
					msg_text += msgs[k];
				};
			} else {
				msg_text = msgs;//typeof msgs == 'string'
			};
		};
		
		if (msg_text) {
			if (R.success) {
				ITc.ui.alert.show(msg_text, null,
					this._OnSuccess.bind(this, [action])
				);
			} else {
				ITc.ui.alert.show(msg_text, 'Error',
					this._OnFail.bind(this, [action])
				);
			};
		} else {
			if (R.success) {
				this._OnSuccess(action);
			} else {
				this._OnFail(action);
			};
		};
	},
	_OnSuccess: function(action) {
		this.fireEvent('success', this, action);
	},
	_OnFail: function(action) {
		this.fireEvent('failure', this, action);
	},
	_setFieldState: function(field, is_valid, errMessage) {
		if (!is_valid) {
			var ErrDiv = this._getErrorDivForField(field,true);
			ErrDiv.style.display = '';
			$(ErrDiv).html(errMessage);
			//ErrDiv.innerHTML = mes;
			
			if(!field.is_invalid){
				field.begining_class = field.className;
				field.className += ' '+this.inputErrorClass;
				field.is_invalid = true;
			} else {
				return;//as field already has right state
			};
		} else if(field.is_invalid) {
			var ErrDiv = this._getErrorDivForField(field);
			if (ErrDiv) {
				ErrDiv.style.display = 'none';
			};
			field.is_invalid = false;
			field.className = field.begining_class;
		} else {
			return;//as field already has right state
		};
		if (this.use_error_flags && field.error_flag_div) {
			field.error_flag_div.className = 'flag ' + (is_valid ? 'ok' : 'error');
		};
	},
	
	_getErrorDivForField:function(Field, shouldBeCreated){
		if (!Field.error_div) {
			if (!shouldBeCreated){
				return null;
			};
			
			if (Field.getAttribute('id') == 'recaptcha_response_field') {
				var errDivId = 'reapply_recaptcha_error_div';
			} else {
				var errDivId = Field.getAttribute('error_div_id')
			}
			var ErrDiv;
			
			if (errDivId) {
				ErrDiv = document.getElementById(errDivId);
			};
			if (!ErrDiv){
				ErrDiv = document.createElement('div');
				ErrDiv.className = this.divErrorClass;
				Field.parentNode.appendChild(ErrDiv);
			};
			Field.error_div = ErrDiv;
		};
		return Field.error_div;
	},
	
	_getFieldsMap: function() {
		if (!this.fieldsMap) {
			var m = this.form.elements;
			var map = {};
			var error_div_id, error_div, flag_div_id, flag_div;
			for(var i=0; i<m.length; i++) {
				//if (m[i].type == 'hidden' || m[i].type == 'button' || m[i].type == 'submit' || m[i].type == 'image') {
				if ( m[i].type == 'button' || m[i].type == 'submit' || m[i].type == 'image') {
					continue;
				}

				var data_key = this._getFieldDataKey(m[i]);
				if (data_key) {
					if (map[data_key]) {
						ITc.debug.show('already_set' + data_key);
					} else {
						map[data_key] = m[i];
						error_div_id = m[i].getAttribute('error_div_id');
						if (error_div_id) {
							error_div = document.getElementById(error_div_id);
							if (error_div) {
								m[i].error_div = error_div;
							}
						}
						//m[i].onfocus = this._setFieldState.bind(this, m[i], true);
						if (this.use_error_flags) {
							flag_div_id = m[i].getAttribute('error_flag_id');
							if (flag_div_id) {
								var flag_div = document.getElementById(flag_div_id);
								if (flag_div) {
									m[i].error_flag_div = flag_div;
								}
							}
						}
					}
				} else {
					ITc.debug.show('Cant get ' + m[i].name + m[i].type);
				}
			}
			this.fieldsMap = map;
		}
		return this.fieldsMap;
	},
	
	_getRealFieldSet: function(){
		if (!this.realFieldsMap) {
			var m = this.form.elements;
			var map = {};
			for(var i=0; i<m.length; i++) {
				if (m[i].type == 'button' || m[i].type == 'submit' || m[i].type == 'image' || m[i].type == 'radio') {
					continue;
				}
				var data_key = this._getFieldDataKey(m[i]);
				if (data_key) {
					if (map[data_key]) {
						ITc.debug.show('already_set' + data_key);
					} else {
						map[data_key] = m[i];
					};
				} else {
					ITc.debug.show('Cant get ' + m[i].name + m[i].type);
				};
			};
			this.realFieldsMap = map;
		}
		return this.realFieldsMap;
	},
	_setupErrors: function(messages){
		var m = this._getFieldsMap();
		for(var name in m) {
			if (messages[name]) {
				this._setFieldState(m[name], false, messages[name]);
				delete(messages[name]);
			} else {
				this._setFieldState(m[name], true);
			};
		};
		//check for left messages (field not found)
		for (var i in messages) {
			return messages;
		};
		return null;
	},
	_getFieldDataKey: function(field) {
		var res = field.getAttribute('dataIndex');
		if (!res) {
			res = field.name.replace(/(?:.*\[)(.*)(?:\])/, '$1');
		};
		return res;
	}
});
ITc.CharsCounter = ITc.extendClass(null, ITc.Component, {
	field: null,
	counter_box: null,
	max_chars: 1000,
	update_delay:300,
	_cctout: null,
	_c_len: 0,
	_initComponent: function() {
		this.counter_box = ITc.getEl(this.counter_box);
		this.field = ITc.getEl(this.field);
		
		if (!this.counter_box || !this.field) {
			return;
		};
		var self = this,
			listener = function(ev, el){
				if (!ev) {
					ev = event;
				};
				if (!el) {
					el = this;
				};
				var len = el.value.length;
				if(len == self.max_chars && ev.charCode > 0){
					return false;
				};
				self._c_len = len;
				clearTimeout(self._cctout);
				self._cctout = setTimeout(self._cUpdaterDelegate, self.update_delay);
				return true;
			};
		if (!this._cUpdaterDelegate) {
			this._cUpdaterDelegate = this.countUpdater.bind(this);
		};
		this.field.onkeyup = listener;
		this.field.onkeypress = listener;
		
		self._c_len = this.field.value.length;
		this.countUpdater(true);
	},
	countUpdater: function(block_substr){
		if (this._c_len > this.max_chars) {
			if (!block_substr) {
				this.field.value = this.field.value.substr(0, this.max_chars);
			};
			this.counter_box.innerHTML = '0';
		} else {
			this.counter_box.innerHTML = (this.max_chars - this._c_len);
		};
	}
});
ITc.utils.resLoader = {
	load:function(type,src,callback,scope){
		if (!src)return;
		var el,
			head = document.getElementsByTagName('head')[0];

		switch(type){
		case 'js':
			el = document.createElement('script');
			el.src = src;
			el.type = 'text/javascript';
			break;

		case 'css':
			el = document.createElement('link');
			el.href = src;
			el.type = 'text/css';
			el.rel = 'stylesheet';
			break;
		
		default:
			return false;
			break;
		};
		if (callback){
			callback = callback.bind(scope ? scope : window, [type,src, el]);
			if (type == 'js') {
//JQuery used - need to change
				if (!$.browser.msie){
					el.onload = callback;
				} else {
					el.onreadystatechange = function(){
						if (this.readyState == 'complete'){
							callback();
						};
					};
				};
				head.appendChild(el);
			} else {
				head.appendChild(el);
				callback();
			};
		} else {
			head.appendChild(el);
		};
		return true;
	}
};
ITc.format.usMoney = function(v){
	v = (Math.round((v-0)*100))/100;
	v = (v == Math.floor(v)) ? v + '.00' : ((v*10 == Math.floor(v*10)) ? v + '0' : v);
	v = String(v);
	var ps = v.split('.');
	var whole = ps[0];
	var sub = ps[1] ? '.'+ ps[1] : '.00';
	var r = /(\d+)(\d{3})/;
	while (r.test(whole)) {
		whole = whole.replace(r, '$1' + ',' + '$2');
	}
	v = whole + sub;
	/*if(v.charAt(0) == '-'){
		return '-' + v.substr(1);
	}*/
	return v;
};

