Form.Label = function (id) {
	var labels = document.getElementsByTagName('label'), label;
	for (var i = 0, n = labels.length; i < n; i++) {
		if (labels[i].htmlFor == id) {
			label = labels[i];
			break;
		}
	}
	if (! label) {
		return null;
	}
	
	label.error = '';
	Engine.attachDestructor(label, 'error');
	
	Engine.attachWatcher(label, 'error', function (property, oldValue, newValue) {
		if (newValue) {
			HTML.addStyleClass(this, 'error');
			HTML.addFloatingMessage(this);
		} else {
			HTML.removeStyleClass(this, 'error');
			HTML.removeFloatingMessage(this);
		}
		return newValue;
	});
	
	label.changed = false;
	Engine.attachDestructor(label, 'changed');
	
	Engine.attachWatcher(label, 'changed', function (property, oldValue, newValue) {
		if (newValue) {
			HTML.addStyleClass(this, 'changed');
		} else {
			HTML.removeStyleClass(this, 'changed');
		}
		return newValue;
	});
	
	return label;
}

Form.Item = function (id) {
	var item = document.getElementById(id);
	if (! item) {
		return null;
	}
	
	var name = item.name;
	
	item.label = Form.Label(id);
	Engine.attachDestructor(item, 'label');
	
	item.error = '';
	Engine.attachDestructor(item, 'error');

	item.changed = false;
	Engine.attachDestructor(item, 'changed');

	item.clear = function () {
		if (this.value !== '') {
			this.value = '';
		}
		this.error = false;
	}
	Engine.attachDestructor(item, 'clear');

	item.reset = function () {
		if (this.value !== this.defaultValue) {
			this.value = new String(this.defaultValue);
		}
		this.error = false;
	}
	Engine.attachDestructor(item, 'reset');
	
	Engine.attachWatcher(item, 'value', function (property, oldValue, newValue) {
		if (this.value !== newValue) {
			this.value = newValue;
		}
		this.fireEvent('onchange');
		return newValue;
	});
	
	Engine.attachWatcher(item, 'error', function (property, oldValue, newValue) {
		if (this.label) {
			this.label.error = newValue;
		}
		if (newValue) {
			HTML.addStyleClass(this, 'error');
			HTML.addFloatingMessage(this);
		} else {
			HTML.removeStyleClass(this, 'error');
			HTML.removeFloatingMessage(this);
		}
		return newValue;
	});
	
	Engine.attachWatcher(item, 'changed', function (property, oldValue, newValue) {
		if (this.label) {
			this.label.changed = newValue;
		}
		if (newValue) {
			HTML.addStyleClass(this, 'changed');
		} else {
			HTML.removeStyleClass(this, 'changed');
		}
		return newValue;
	});
	
	item.attachEvent('onreset', function () {
		var item = event.srcElement;
		item.reset();
	});
	
	item.attachEvent('onchange', function () {
		var item = event.srcElement;
		item.changed = Boolean(item.value != item.defaultValue);
		item.error = '';
	});
	
	item.validators = [];
	Engine.attachDestructor(item, 'validators');
	
	item.isValid = function () {
		for (var i = 0, n = this.validators.length; i < n; i++) {
			if (! this.validators[i].isValid(this)) {
				this.error = this.validators[i].message;
				return false;
			}
		}
		this.error = '';
		return true;
	}
	Engine.attachDestructor(item, 'isValid');

	return item;
}
