
	if (!FORM_JS) {
		var FORM_JS = true;
		
		var Form = function (sId, aErrors) {
			this.sId = sId;
			this.rows = [];
			this.input = [];
			this.errors = aErrors;
			
			on_window_load(createMethodReference(this, this.initialize));
		};
		
		Form.prototype.initialize = function () {
			this.oForm = $(this.sId);
			
			var row = getElementsByClass('form_row', this.oForm);
			for (var k = 0; k < row.length; k ++) {
				var r = {
					"input": {}
				};
				
				var input = getElementsByClass('form_input', row[k]);
				var is_error = false;
				
				for (var j = 0; j < input.length; j ++) {
					var i = {
						"status": getElementsByClass('form_status', input[j])[0],
						"row": row[k],
						"input": input[j],
						"form": this,
						"controlChecking": false,
						"controlCheckingTimeout": null
					};
					
					var control = i['control'] = this.getControl(input[j]);

					if (!control)
						continue;
						
					var name = i['name'] = control.getAttribute('name').replace('[]', '');

					var ctrl = this.getControls(input[j]);
					for (var l = 0; l < ctrl.length; l ++) {
						attachEvent(ctrl[l], 'blur', createMethodReference(i, this.controlChange));
						attachEvent(ctrl[l], 'change', createMethodReference(i, this.controlChangeDelay));
						attachEvent(ctrl[l], 'keyup', createMethodReference(i, this.controlChangeDelay));
					}
					
					r.input[name] = i;
					this.input[name] = i;
					
					if (this.errors[name]) {
						is_error = true;
						i.status.innerHTML = this.errors[name];
						input[j].className += ' form_error';
					} else {
						i.status.style.display = 'none';
					}
				}
				
				if (is_error) {
					//row[k].className += ' form_error';
				}
			}
		};

		Form.prototype.getControl = function (node) {
			var input = node.getElementsByTagName('input');
			if (input.length > 0)
				return input[0];

			input = node.getElementsByTagName('textarea');
			if (input.length > 0)
				return input[0];

			input = node.getElementsByTagName('select');
			if (input.length > 0)
				return input[0];

			return null;
		};
		
		Form.prototype.getControls = function (node) {
			var res = [];
			
			var input = node.getElementsByTagName('input');
			for (var i = 0; i < input.length; i ++)
				res.push(input[i]);
				
			input = node.getElementsByTagName('textarea');
			for (i = 0; i < input.length; i ++)
				res.push(input[i]);
				
			input = node.getElementsByTagName('select');
			for (i = 0; i < input.length; i ++)
				res.push(input[i]);

			return res;
		};
		
		Form.prototype.controlChangeDelay = function () {
			clearInterval(this.controlCheckingTimeout);
			this.controlCheckingTimeout = setTimeout(createMethodReference(this, this.form.controlChange), 1000);
		};
		
		Form.prototype.controlChange = function () {
			if (!this.controlChecking) {
				clearInterval(this.controlCheckingTimeout);
				
				this.controlChecking = true;
				
				try {
					var sUrl = this.form.oForm.getAttribute('action');
					sUrl = sUrl.replace(/\/[^\/]+\/(\?(.*))?$/, '/checkfield/?$2');
					sUrl += '&field=' + this.name;
					
					var conn = new TConnection;
					conn.sUrl = sUrl;
					conn.sMethod = 'POST';
					
					conn.onData = createMethodReference(this, function (c) {
						try {
							try {
								eval('var data = ' + c.responseText + ';');
								
								if (data) {
									if (data.is_null) {
										this.input.className += ' form_error';
										this.status.innerHTML = data.errstr;
										this.status.style.display = '';
									} else {
										this.input.className = this.input.className.replace(/ form_error/g, '');
										
										this.status.innerHTML = '';
										this.status.style.display = 'none';
									}
								}
							} catch (e) {
								debug(e);
							}
						} finally {
							this.controlChecking = false;
						}
					});
					
					setFormDataToConnection(this.form.oForm, conn);
					
					conn.open();
				} catch (e) {
					debug(e);
					this.controlChecking = false;
				}
			}
		};
	}
