text: var UI = {};
  
  UI.Element = function () {};
  
  UI.Element.prototype = {
  
  	setId: function ( id ) {
  
  		this.dom.id = id;
  		
  		return this;
  
  	},
  
  	setClass: function ( name ) {
  
  		this.dom.className = name;
  
  		return this;
  
  	},
  
  	setStyle: function ( style, array ) {
  
  		for ( var i = 0; i < array.length; i ++ ) {
  
  			this.dom.style[ style ] = array[ i ];
  
  		}
  
  	},
  
  	setDisabled: function ( value ) {
  
  		this.dom.disabled = value;
  
  		return this;
  
  	},
  
  	setTextContent: function ( value ) {
  
  		this.dom.textContent = value;
  
  		return this;
  
  	}
  
  }
  
  // properties
  
  var properties = [ 'position', 'left', 'top', 'right', 'bottom', 'width', 'height', 'border', 'borderLeft',
  'borderTop', 'borderRight', 'borderBottom', 'borderColor', 'display', 'overflow', 'margin', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'padding', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'color',
  'backgroundColor', 'opacity', 'fontSize', 'fontWeight', 'textTransform', 'cursor' ];
  
  properties.forEach( function ( property ) {
  
  	var method = 'set' + property.substr( 0, 1 ).toUpperCase() + property.substr( 1, property.length );
  
  	UI.Element.prototype[ method ] = function () {
  
  		this.setStyle( property, arguments );
  		return this;
  
  	};
  
  } );
  
  // events
  
  var events = [ 'KeyUp', 'KeyDown', 'MouseOver', 'MouseOut', 'Click', 'Change' ];
  
  events.forEach( function ( event ) {
  
  	var method = 'on' + event;
  
  	UI.Element.prototype[ method ] = function ( callback ) {
  
  		this.dom.addEventListener( event.toLowerCase(), callback, false );
  		return this;
  
  	};
  
  } );
  
  
  // Panel
  
  UI.Panel = function () {
  
  	UI.Element.call( this );
  
  	var dom = document.createElement( 'div' );
  	dom.className = 'Panel';
  	dom.style.userSelect = 'none';
  	dom.style.WebkitUserSelect = 'none';
  	dom.style.MozUserSelect = 'none';
  
  	this.dom = dom;
  
  	return this;
  };
  
  UI.Panel.prototype = Object.create( UI.Element.prototype );
  
  UI.Panel.prototype.add = function () {
  
  	for ( var i = 0; i < arguments.length; i ++ ) {
  
  		this.dom.appendChild( arguments[ i ].dom );
  
  	}
  
  	return this;
  
  };
  
  
  UI.Panel.prototype.remove = function () {
  
  	for ( var i = 0; i < arguments.length; i ++ ) {
  
  		this.dom.removeChild( arguments[ i ].dom );
  
  	}
  
  	return this;
  
  };
  
  // Text
  
  UI.Text = function ( text ) {
  
  	UI.Element.call( this );
  
  	var dom = document.createElement( 'span' );
  	dom.className = 'Text';
  	dom.style.cursor = 'default';
  	dom.style.display = 'inline-block';
  	dom.style.verticalAlign = 'middle';
  
  	this.dom = dom;
  	this.setValue( text );
  
  	return this;
  
  };
  
  UI.Text.prototype = Object.create( UI.Element.prototype );
  
  UI.Text.prototype.setValue = function ( value ) {
  
  	if ( value !== undefined ) {
  
  		this.dom.textContent = value;
  
  	}
  
  	return this;
  
  };
  
  
  // Input
  
  UI.Input = function () {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'input' );
  	dom.className = 'Input';
  	dom.style.padding = '2px';
  	dom.style.border = '1px solid #ccc';
  
  	dom.addEventListener( 'keydown', function ( event ) {
  
  		event.stopPropagation();
  
  	}, false );
  
  	this.dom = dom;
  
  	return this;
  
  };
  
  UI.Input.prototype = Object.create( UI.Element.prototype );
  
  UI.Input.prototype.getValue = function () {
  
  	return this.dom.value;
  
  };
  
  UI.Input.prototype.setValue = function ( value ) {
  
  	this.dom.value = value;
  
  	return this;
  
  };
  
  
  // TextArea
  
  UI.TextArea = function () {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'textarea' );
  	dom.className = 'TextArea';
  	dom.style.padding = '2px';
  	dom.style.border = '1px solid #ccc';
  
  	dom.addEventListener( 'keydown', function ( event ) {
  
  		event.stopPropagation();
  
  	}, false );
  
  	this.dom = dom;
  
  	return this;
  
  };
  
  UI.TextArea.prototype = Object.create( UI.Element.prototype );
  
  UI.TextArea.prototype.getValue = function () {
  
  	return this.dom.value;
  
  };
  
  UI.TextArea.prototype.setValue = function ( value ) {
  
  	this.dom.value = value;
  
  	return this;
  
  };
  
  
  // Select
  
  UI.Select = function () {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'select' );
  	dom.className = 'Select';
  	dom.style.width = '64px';
  	dom.style.height = '16px';
  	dom.style.border = '0px';
  	dom.style.padding = '0px';
  
  	this.dom = dom;
  
  	return this;
  
  };
  
  UI.Select.prototype = Object.create( UI.Element.prototype );
  
  UI.Select.prototype.setMultiple = function ( boolean ) {
  
  	this.dom.multiple = boolean;
  
  	return this;
  
  };
  
  UI.Select.prototype.setOptions = function ( options ) {
  
  	var selected = this.dom.value;
  
  	while ( this.dom.children.length > 0 ) {
  
  		this.dom.removeChild( this.dom.firstChild );
  
  	}
  
  	for ( var key in options ) {
  
  		var option = document.createElement( 'option' );
  		option.value = key;
  		option.innerHTML = options[ key ];
  		this.dom.appendChild( option );
  
  	}
  
  	this.dom.value = selected;
  
  	return this;
  
  };
  
  UI.Select.prototype.getValue = function () {
  
  	return this.dom.value;
  
  };
  
  UI.Select.prototype.setValue = function ( value ) {
  
  	this.dom.value = value;
  
  	return this;
  
  };
  
  // FancySelect
  
  UI.FancySelect = function () {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'div' );
  	dom.className = 'FancySelect';
  	dom.style.background = '#fff';
  	dom.style.border = '1px solid #ccc';
  	dom.style.padding = '0';
  	dom.style.cursor = 'default';
  	dom.style.overflow = 'auto';
  
  	this.dom = dom;
  
  	this.options = [];
  	this.selectedValue = null;
  
  	return this;
  
  };
  
  UI.FancySelect.prototype = Object.create( UI.Element.prototype );
  
  UI.FancySelect.prototype.setOptions = function ( options ) {
  
  	var scope = this;
  
  	var changeEvent = document.createEvent( 'HTMLEvents' );
  	changeEvent.initEvent( 'change', true, true );
  
  	while ( scope.dom.children.length > 0 ) {
  
  		scope.dom.removeChild( scope.dom.firstChild );
  
  	}
  
  	scope.options = [];
  
  	for ( var key in options ) {
  
  		var option = document.createElement( 'div' );
  		option.style.padding = '4px';
  		option.style.whiteSpace = 'nowrap';
  		option.innerHTML = options[ key ];
  		option.value = key;
  		scope.dom.appendChild( option );
  
  		scope.options.push( option );
  
  		option.addEventListener( 'click', function ( event ) {
  
  			scope.setValue( this.value );
  			scope.dom.dispatchEvent( changeEvent );
  
  		}, false );
  
  	}
  
  	return scope;
  
  };
  
  UI.FancySelect.prototype.getValue = function () {
  
  	return this.selectedValue;
  
  };
  
  UI.FancySelect.prototype.setValue = function ( value ) {
  
  	if ( typeof value === 'number' ) value = value.toString();
  
  	for ( var i = 0; i < this.options.length; i ++ ) {
  
  		var element = this.options[ i ];
  
  		if ( element.value === value ) {
  
  			element.style.backgroundColor = '#f0f0f0';
  
  			// scroll into view
  
  			var y = i * element.clientHeight;
  			var scrollTop = this.dom.scrollTop;
  			var domHeight = this.dom.clientHeight;
  
  			if ( y < scrollTop || y > scrollTop + domHeight ) {
  
  				this.dom.scrollTop = y;
  
  			}
  
  		} else {
  
  			element.style.backgroundColor = '';
  
  		}
  
  	}
  
  	this.selectedValue = value;
  
  	return this;
  
  };
  
  // Checkbox
  
  UI.Checkbox = function ( boolean ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'input' );
  	dom.className = 'Checkbox';
  	dom.type = 'checkbox';
  
  	this.dom = dom;
  	this.setValue( boolean );
  
  	return this;
  
  };
  
  UI.Checkbox.prototype = Object.create( UI.Element.prototype );
  
  UI.Checkbox.prototype.getValue = function () {
  
  	return this.dom.checked;
  
  };
  
  UI.Checkbox.prototype.setValue = function ( value ) {
  
  	if ( value !== undefined ) {
  
  		this.dom.checked = value;
  
  	}
  
  	return this;
  
  };
  
  
  // Color
  
  UI.Color = function () {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'input' );
  	dom.className = 'Color';
  	dom.style.width = '64px';
  	dom.style.height = '16px';
  	dom.style.border = '0px';
  	dom.style.padding = '0px';
  	dom.style.backgroundColor = 'transparent';
  
  	try { dom.type = 'color'; } catch ( exception ) {}
  
  	this.dom = dom;
  
  	return this;
  
  };
  
  UI.Color.prototype = Object.create( UI.Element.prototype );
  
  UI.Color.prototype.getValue = function () {
  
  	return this.dom.value;
  
  };
  
  UI.Color.prototype.getHexValue = function () {
  
  	return parseInt( this.dom.value.substr( 1 ), 16 );
  
  };
  
  UI.Color.prototype.setValue = function ( value ) {
  
  	this.dom.value = value;
  
  	return this;
  
  };
  
  UI.Color.prototype.setHexValue = function ( hex ) {
  
  	this.dom.value = "#" + ( '000000' + hex.toString( 16 ) ).slice( -6 );
  
  	return this;
  
  };
  
  
  // Number
  
  UI.Number = function ( number ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'input' );
  	dom.className = 'Number';
  	dom.style.color = '#0080f0';
  	dom.style.fontSize = '12px';
  	dom.style.backgroundColor = 'transparent';
  	dom.style.border = '1px solid transparent';
  	dom.style.padding = '2px';
  	dom.style.cursor = 'col-resize';
  	dom.value = '0.00';
  
  	dom.addEventListener( 'keydown', function ( event ) {
  
  		event.stopPropagation();
  
  		if ( event.keyCode === 13 ) dom.blur();
  
  	}, false );
  
  	this.min = - Infinity;
  	this.max = Infinity;
  
  	this.precision = 2;
  	this.step = 1;
  
  	this.dom = dom;
  	this.setValue( number );
  
  	var changeEvent = document.createEvent( 'HTMLEvents' );
  	changeEvent.initEvent( 'change', true, true );
  
  	var distance = 0;
  	var onMouseDownValue = 0;
  
  	var onMouseDown = function ( event ) {
  
  		event.preventDefault();
  
  		distance = 0;
  
  		onMouseDownValue = parseFloat( dom.value );
  
  		document.addEventListener( 'mousemove', onMouseMove, false );
  		document.addEventListener( 'mouseup', onMouseUp, false );
  
  	};
  
  	var onMouseMove = function ( event ) {
  
  		var currentValue = dom.value;
  
  		var movementX = event.movementX || event.webkitMovementX || event.mozMovementX || 0;
  		var movementY = event.movementY || event.webkitMovementY || event.mozMovementY || 0;
  
  		distance += movementX - movementY;
  
  		var number = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
  
  		dom.value = Math.min( scope.max, Math.max( scope.min, number ) ).toFixed( scope.precision );
  
  		if ( currentValue !== dom.value ) dom.dispatchEvent( changeEvent );
  
  	};
  
  	var onMouseUp = function ( event ) {
  
  		document.removeEventListener( 'mousemove', onMouseMove, false );
  		document.removeEventListener( 'mouseup', onMouseUp, false );
  
  		if ( Math.abs( distance ) < 2 ) {
  
  			dom.focus();
  			dom.select();
  
  		}
  
  	};
  
  	var onChange = function ( event ) {
  
  		var number = parseFloat( dom.value );
  
  		if ( isNaN( number ) === false ) {
  
  			dom.value = number;
  
  		}
  
  	};
  
  	var onFocus = function ( event ) {
  
  		dom.style.backgroundColor = '';
  		dom.style.borderColor = '#ccc';
  		dom.style.cursor = '';
  
  	};
  
  	var onBlur = function ( event ) {
  
  		dom.style.backgroundColor = 'transparent';
  		dom.style.borderColor = 'transparent';
  		dom.style.cursor = 'col-resize';
  
  	};
  
  	dom.addEventListener( 'mousedown', onMouseDown, false );
  	dom.addEventListener( 'change', onChange, false );
  	dom.addEventListener( 'focus', onFocus, false );
  	dom.addEventListener( 'blur', onBlur, false );
  
  	return this;
  
  };
  
  UI.Number.prototype = Object.create( UI.Element.prototype );
  
  UI.Number.prototype.getValue = function () {
  
  	return parseFloat( this.dom.value );
  
  };
  
  UI.Number.prototype.setValue = function ( value ) {
  
  	if ( value !== undefined ) {
  
  		this.dom.value = value.toFixed( this.precision );
  
  	}
  
  	return this;
  
  };
  
  UI.Number.prototype.setRange = function ( min, max ) {
  
  	this.min = min;
  	this.max = max;
  
  	return this;
  
  };
  
  UI.Number.prototype.setPrecision = function ( precision ) {
  
  	this.precision = precision;
  
  	return this;
  
  };
  
  
  // Integer
  
  UI.Integer = function ( number ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'input' );
  	dom.className = 'Number';
  	dom.style.color = '#0080f0';
  	dom.style.fontSize = '12px';
  	dom.style.backgroundColor = 'transparent';
  	dom.style.border = '1px solid transparent';
  	dom.style.padding = '2px';
  	dom.style.cursor = 'col-resize';
  	dom.value = '0.00';
  
  	dom.addEventListener( 'keydown', function ( event ) {
  
  		event.stopPropagation();
  
  	}, false );
  
  	this.min = - Infinity;
  	this.max = Infinity;
  
  	this.step = 1;
  
  	this.dom = dom;
  	this.setValue( number );
  
  	var changeEvent = document.createEvent( 'HTMLEvents' );
  	changeEvent.initEvent( 'change', true, true );
  
  	var distance = 0;
  	var onMouseDownValue = 0;
  
  	var onMouseDown = function ( event ) {
  
  		event.preventDefault();
  
  		distance = 0;
  
  		onMouseDownValue = parseFloat( dom.value );
  
  		document.addEventListener( 'mousemove', onMouseMove, false );
  		document.addEventListener( 'mouseup', onMouseUp, false );
  
  	};
  
  	var onMouseMove = function ( event ) {
  
  		var currentValue = dom.value;
  
  		var movementX = event.movementX || event.webkitMovementX || event.mozMovementX || 0;
  		var movementY = event.movementY || event.webkitMovementY || event.mozMovementY || 0;
  
  		distance += movementX - movementY;
  
  		var number = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
  
  		dom.value = Math.min( scope.max, Math.max( scope.min, number ) ) | 0;
  
  		if ( currentValue !== dom.value ) dom.dispatchEvent( changeEvent );
  
  	};
  
  	var onMouseUp = function ( event ) {
  
  		document.removeEventListener( 'mousemove', onMouseMove, false );
  		document.removeEventListener( 'mouseup', onMouseUp, false );
  
  		if ( Math.abs( distance ) < 2 ) {
  
  			dom.focus();
  			dom.select();
  
  		}
  
  	};
  
  	var onChange = function ( event ) {
  
  		var number = parseInt( dom.value );
  
  		if ( isNaN( number ) === false ) {
  
  			dom.value = number;
  
  		}
  
  	};
  
  	var onFocus = function ( event ) {
  
  		dom.style.backgroundColor = '';
  		dom.style.borderColor = '#ccc';
  		dom.style.cursor = '';
  
  	};
  
  	var onBlur = function ( event ) {
  
  		dom.style.backgroundColor = 'transparent';
  		dom.style.borderColor = 'transparent';
  		dom.style.cursor = 'col-resize';
  
  	};
  
  	dom.addEventListener( 'mousedown', onMouseDown, false );
  	dom.addEventListener( 'change', onChange, false );
  	dom.addEventListener( 'focus', onFocus, false );
  	dom.addEventListener( 'blur', onBlur, false );
  
  	return this;
  
  };
  
  UI.Integer.prototype = Object.create( UI.Element.prototype );
  
  UI.Integer.prototype.getValue = function () {
  
  	return parseInt( this.dom.value );
  
  };
  
  UI.Integer.prototype.setValue = function ( value ) {
  
  	if ( value !== undefined ) {
  
  		this.dom.value = value | 0;
  
  	}
  
  	return this;
  
  };
  
  UI.Integer.prototype.setRange = function ( min, max ) {
  
  	this.min = min;
  	this.max = max;
  
  	return this;
  
  };
  
  
  // Break
  
  UI.Break = function () {
  
  	UI.Element.call( this );
  
  	var dom = document.createElement( 'br' );
  	dom.className = 'Break';
  
  	this.dom = dom;
  
  	return this;
  
  };
  
  UI.Break.prototype = Object.create( UI.Element.prototype );
  
  
  // HorizontalRule
  
  UI.HorizontalRule = function () {
  
  	UI.Element.call( this );
  
  	var dom = document.createElement( 'hr' );
  	dom.className = 'HorizontalRule';
  
  	this.dom = dom;
  
  	return this;
  
  };
  
  UI.HorizontalRule.prototype = Object.create( UI.Element.prototype );
  
  
  // Button
  
  UI.Button = function ( value ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'button' );
  	dom.className = 'Button';
  
  	this.dom = dom;
  	this.dom.textContent = value;
  
  	return this;
  
  };
  
  UI.Button.prototype = Object.create( UI.Element.prototype );
  
  UI.Button.prototype.setLabel = function ( value ) {
  
  	this.dom.textContent = value;
  
  	return this;
  
  };
  