text: /**
   * @author mrdoob / http://mrdoob.com/
   */
  
  UI.Texture = function ( mapping ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'span' );
  
  	var input = document.createElement( 'input' );
  	input.type = 'file';
  	input.addEventListener( 'change', function ( event ) {
  
  		loadFile( event.target.files[ 0 ] );
  
  	} );
  
  	var canvas = document.createElement( 'canvas' );
  	canvas.width = 32;
  	canvas.height = 16;
  	canvas.style.cursor = 'pointer';
  	canvas.style.marginRight = '5px';
  	canvas.style.border = '1px solid #888';
  	canvas.addEventListener( 'click', function ( event ) {
  
  		input.click();
  
  	}, false );
  	canvas.addEventListener( 'drop', function ( event ) {
  
  		event.preventDefault();
  		event.stopPropagation();
  		loadFile( event.dataTransfer.files[ 0 ] );
  
  	}, false );
  	dom.appendChild( canvas );
  
  	var name = document.createElement( 'input' );
  	name.disabled = true;
  	name.style.width = '64px';
  	name.style.border = '1px solid #ccc';
  	dom.appendChild( name );
  
  	var loadFile = function ( file ) {
  
  		if ( file.type.match( 'image.*' ) ) {
  
  			var reader = new FileReader();
  
  			if ( file.type === 'image/targa' ) {
  
  				reader.addEventListener( 'load', function ( event ) {
  
  					var canvas = new THREE.TGALoader().parse( event.target.result );
  
  					var texture = new THREE.CanvasTexture( canvas, mapping );
  					texture.sourceFile = file.name;
  
  					scope.setValue( texture );
  
  					if ( scope.onChangeCallback ) scope.onChangeCallback();
  
  				}, false );
  
  				reader.readAsArrayBuffer( file );
  
  			} else {
  
  				reader.addEventListener( 'load', function ( event ) {
  
  					var image = document.createElement( 'img' );
  					image.addEventListener( 'load', function( event ) {
  
  						var texture = new THREE.Texture( this, mapping );
  						texture.sourceFile = file.name;
  						texture.needsUpdate = true;
  
  						scope.setValue( texture );
  
  						if ( scope.onChangeCallback ) scope.onChangeCallback();
  
  					}, false );
  
  					image.src = event.target.result;
  
  				}, false );
  
  				reader.readAsDataURL( file );
  
  			}
  
  		}
  
  	};
  
  	this.dom = dom;
  	this.texture = null;
  	this.onChangeCallback = null;
  
  	return this;
  
  };
  
  UI.Texture.prototype = Object.create( UI.Element.prototype );
  UI.Texture.prototype.constructor = UI.Texture;
  
  UI.Texture.prototype.getValue = function () {
  
  	return this.texture;
  
  };
  
  UI.Texture.prototype.setValue = function ( texture ) {
  
  	var canvas = this.dom.children[ 0 ];
  	var name = this.dom.children[ 1 ];
  	var context = canvas.getContext( '2d' );
  
  	if ( texture !== null ) {
  
  		var image = texture.image;
  
  		if ( image !== undefined && image.width > 0 ) {
  
  			name.value = texture.sourceFile;
  
  			var scale = canvas.width / image.width;
  			context.drawImage( image, 0, 0, image.width * scale, image.height * scale );
  
  		} else {
  
  			name.value = texture.sourceFile + ' (error)';
  			context.clearRect( 0, 0, canvas.width, canvas.height );
  
  		}
  
  	} else {
  
  		name.value = '';
  
  		if ( context !== null ) {
  
  			// Seems like context can be null if the canvas is not visible
  
  			context.clearRect( 0, 0, canvas.width, canvas.height );
  
  		}
  
  	}
  
  	this.texture = texture;
  
  };
  
  UI.Texture.prototype.onChange = function ( callback ) {
  
  	this.onChangeCallback = callback;
  
  	return this;
  
  };
  
  //Video
  UI.Video = function ( mapping ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'span' );
  
  	var input = document.createElement( 'input' );
  	input.type = 'file';
  	input.addEventListener( 'change', function ( event ) {
  
  		loadFile( event.target.files[ 0 ] );
  
  	} );
  
  	var canvas = document.createElement( 'canvas' );
  	canvas.width = 32;
  	canvas.height = 16;
  	canvas.style.cursor = 'pointer';
  	canvas.style.marginRight = '5px';
  	canvas.style.border = '1px solid #888';
  	canvas.addEventListener( 'click', function ( event ) {
  
  		input.click();
  
  	}, false );
  	canvas.addEventListener( 'drop', function ( event ) {
  
  		event.preventDefault();
  		event.stopPropagation();
  		loadFile( event.dataTransfer.files[ 0 ] );
  
  	}, false );
  	dom.appendChild( canvas );
  
  	var name = document.createElement( 'input' );
  	name.disabled = true;
  	name.style.width = '64px';
  	name.style.border = '1px solid #ccc';
  	dom.appendChild( name );
  
  	var loadFile = function ( file ) {
  
  		if ( file.type.match( 'video.*' ) ) {
  
  			var reader = new FileReader();
  			reader.addEventListener( 'load', function ( event ) {
  
  				var video = document.createElement( 'video' );
  				video.addEventListener( 'load', function( event ) {
  
  					var videoTexture = new THREE.Texture( this );
  					videoTexture.sourceFile = file.name;
  					videoTexture.needsUpdate = true;
  
  					scope.setValue( videoTexture );
  
  					if ( scope.onChangeCallback ) scope.onChangeCallback();
  
  				}, false );
  
  				video.src = event.target.result;
  
  			}, false );
  
  			reader.readAsDataURL( file );
  
  		}
  
  	};
  
  	this.dom = dom;
  	this.texture = null;
  	this.onChangeCallback = null;
  
  	return this;
  
  };
  
  UI.Video.prototype = Object.create( UI.Element.prototype );
  UI.Video.prototype.constructor = UI.Texture;
  
  UI.Video.prototype.getValue = function () {
  
  	return this.video;
  
  };
  
  UI.Video.prototype.setValue = function ( video ) {
  
  	var canvas = this.dom.children[ 0 ];
  	var name = this.dom.children[ 1 ];
  	var context = canvas.getContext( '2d' );
  
  	if ( video !== null ) {
  
  		console.log(video.sourceFile);
  
  		// var vid = texture.image;
  
  		// if ( image !== undefined && image.width > 0 ) {
  
  		// 	name.value = texture.sourceFile;
  
  		// 	var scale = canvas.width / image.width;
  		// 	context.drawImage( image, 0, 0, image.width * scale, image.height * scale );
  
  		// } else {
  
  		// 	name.value = texture.sourceFile + ' (error)';
  		// 	context.clearRect( 0, 0, canvas.width, canvas.height );
  
  		// }
  
  	} else {
  
  		name.value = '';
  		context.clearRect( 0, 0, canvas.width, canvas.height );
  
  	}
  
  	this.texture = texture;
  
  };
  
  UI.Video.prototype.onChange = function ( callback ) {
  
  	this.onChangeCallback = callback;
  
  	return this;
  
  };
  
  // Outliner
  
  UI.Outliner = function ( editor ) {
  
  	UI.Element.call( this );
  
  	var scope = this;
  
  	var dom = document.createElement( 'div' );
  	dom.className = 'Outliner';
  	dom.tabIndex = 0;	// keyup event is ignored without setting tabIndex
  
  	// hack
  	this.scene = editor.scene;
  
  	// Prevent native scroll behavior
  	dom.addEventListener( 'keydown', function ( event ) {
  
  		switch ( event.keyCode ) {
  			case 38: // up
  			case 40: // down
  				event.preventDefault();
  				event.stopPropagation();
  				break;
  		}
  
  	}, false );
  
  	// Keybindings to support arrow navigation
  	dom.addEventListener( 'keyup', function ( event ) {
  
  		switch ( event.keyCode ) {
  			case 38: // up
  				scope.selectIndex( scope.selectedIndex - 1 );
  				break;
  			case 40: // down
  				scope.selectIndex( scope.selectedIndex + 1 );
  				break;
  		}
  
  	}, false );
  
  	this.dom = dom;
  
  	this.options = [];
  	this.selectedIndex = - 1;
  	this.selectedValue = null;
  
  	return this;
  
  };
  
  UI.Outliner.prototype = Object.create( UI.Element.prototype );
  UI.Outliner.prototype.constructor = UI.Outliner;
  
  UI.Outliner.prototype.selectIndex = function ( index ) {
  
  	if ( index >= 0 && index < this.options.length ) {
  
  		this.setValue( this.options[ index ].value );
  
  		var changeEvent = document.createEvent( 'HTMLEvents' );
  		changeEvent.initEvent( 'change', true, true );
  		this.dom.dispatchEvent( changeEvent );
  
  	}
  
  };
  
  UI.Outliner.prototype.setOptions = function ( options ) {
  
  	var scope = this;
  
  	while ( scope.dom.children.length > 0 ) {
  
  		scope.dom.removeChild( scope.dom.firstChild );
  
  	}
  
  	function onClick() {
  
  		scope.setValue( this.value );
  
  		var changeEvent = document.createEvent( 'HTMLEvents' );
  		changeEvent.initEvent( 'change', true, true );
  		scope.dom.dispatchEvent( changeEvent );
  
  	}
  
  	// Drag
  
  	var currentDrag;
  
  	function onDrag( event ) {
  
  		currentDrag = this;
  
  	}
  
  	function onDragStart( event ) {
  
  		event.dataTransfer.setData( 'text', 'foo' );
  
  	}
  
  	function onDragOver( event ) {
  
  		if ( this === currentDrag ) return;
  
  		var area = event.offsetY / this.clientHeight;
  
  		if ( area < 0.25 ) {
  
  			this.className = 'option dragTop';
  
  		} else if ( area > 0.75 ) {
  
  			this.className = 'option dragBottom';
  
  		} else {
  
  			this.className = 'option drag';
  
  		}
  
  	}
  
  	function onDragLeave() {
  
  		if ( this === currentDrag ) return;
  
  		this.className = 'option';
  
  	}
  
  	function onDrop( event ) {
  
  		if ( this === currentDrag ) return;
  
  		this.className = 'option';
  
  		var scene = scope.scene;
  		var object = scene.getObjectById( currentDrag.value );
  
  		var area = event.offsetY / this.clientHeight;
  
  		if ( area < 0.25 ) {
  
  			var nextObject = scene.getObjectById( this.value );
  			moveObject( object, nextObject.parent, nextObject );
  
  		} else if ( area > 0.75 ) {
  
  			var nextObject = scene.getObjectById( this.nextSibling.value );
  			moveObject( object, nextObject.parent, nextObject );
  
  		} else {
  
  			var parentObject = scene.getObjectById( this.value );
  			moveObject( object, parentObject );
  
  		}
  
  	}
  
  	function moveObject( object, newParent, nextObject ) {
  
  		if ( nextObject === null ) nextObject = undefined;
  
  		var newParentIsChild = false;
  
  		object.traverse( function ( child ) {
  
  			if ( child === newParent ) newParentIsChild = true;
  
  		} );
  
  		if ( newParentIsChild ) return;
  
  		editor.execute( new MoveObjectCommand( object, newParent, nextObject ) );
  
  		var changeEvent = document.createEvent( 'HTMLEvents' );
  		changeEvent.initEvent( 'change', true, true );
  		scope.dom.dispatchEvent( changeEvent );
  
  	}
  
  	//
  
  	scope.options = [];
  
  	for ( var i = 0; i < options.length; i ++ ) {
  
  		var div = options[ i ];
  		div.className = 'option';
  		scope.dom.appendChild( div );
  
  		scope.options.push( div );
  
  		div.addEventListener( 'click', onClick, false );
  
  		if ( div.draggable === true ) {
  
  			div.addEventListener( 'drag', onDrag, false );
  			div.addEventListener( 'dragstart', onDragStart, false ); // Firefox needs this
  
  			div.addEventListener( 'dragover', onDragOver, false );
  			div.addEventListener( 'dragleave', onDragLeave, false );
  			div.addEventListener( 'drop', onDrop, false );
  
  		}
  
  
  	}
  
  	return scope;
  
  };
  
  UI.Outliner.prototype.getValue = function () {
  
  	return this.selectedValue;
  
  };
  
  UI.Outliner.prototype.setValue = function ( value ) {
  
  	for ( var i = 0; i < this.options.length; i ++ ) {
  
  		var element = this.options[ i ];
  
  		if ( element.value === value ) {
  
  			element.classList.add( 'active' );
  
  			// scroll into view
  
  			var y = element.offsetTop - this.dom.offsetTop;
  			var bottomY = y + element.offsetHeight;
  			var minScroll = bottomY - this.dom.offsetHeight;
  
  			if ( this.dom.scrollTop > y ) {
  
  				this.dom.scrollTop = y;
  
  			} else if ( this.dom.scrollTop < minScroll ) {
  
  				this.dom.scrollTop = minScroll;
  
  			}
  
  			this.selectedIndex = i;
  
  		} else {
  
  			element.classList.remove( 'active' );
  
  		}
  
  	}
  
  	this.selectedValue = value;
  
  	return this;
  
  };
  
  UI.THREE = {};
  
  UI.THREE.Boolean = function ( boolean, text ) {
  
  	UI.Span.call( this );
  
  	this.setMarginRight( '10px' );
  
  	this.checkbox = new UI.Checkbox( boolean );
  	this.text = new UI.Text( text ).setMarginLeft( '3px' );
  
  	this.add( this.checkbox );
  	this.add( this.text );
  
  };
  
  UI.THREE.Boolean.prototype = Object.create( UI.Span.prototype );
  UI.THREE.Boolean.prototype.constructor = UI.THREE.Boolean;
  
  UI.THREE.Boolean.prototype.getValue = function () {
  
  	return this.checkbox.getValue();
  
  };
  
  UI.THREE.Boolean.prototype.setValue = function ( value ) {
  
  	return this.checkbox.setValue( value );
  
  };
  