فهرست منبع

refactors editor menubar as preparation for undo/redo mechanism

Dennis Wilson 11 سال پیش
والد
کامیت
bacb3b19b2
6فایلهای تغییر یافته به همراه304 افزوده شده و 377 حذف شده
  1. 65 148
      editor/js/Menubar.Add.js
  2. 41 48
      editor/js/Menubar.Edit.js
  3. 100 123
      editor/js/Menubar.File.js
  4. 21 25
      editor/js/Menubar.Help.js
  5. 20 33
      editor/js/Menubar.View.js
  6. 57 0
      editor/js/libs/ui.js

+ 65 - 148
editor/js/Menubar.Add.js

@@ -1,29 +1,14 @@
 Menubar.Add = function ( editor ) {
 Menubar.Add = function ( editor ) {
 
 
-	var container = new UI.Panel();
-	container.setClass( 'menu' );
-
-	var title = new UI.Panel();
-	title.setTextContent( 'Add' );
-	title.setMargin( '0px' );
-	title.setPadding( '8px' );
-	container.add( title );
-
-	//
-
-	var options = new UI.Panel();
-	options.setClass( 'options' );
-	container.add( options );
+	var menuConfig,
+		optionsPanel;
 
 
 	var meshCount = 0;
 	var meshCount = 0;
 	var lightCount = 0;
 	var lightCount = 0;
 
 
-	// add object
+	// event handlers
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Object3D' );
-	option.onClick( function () {
+	function onObject3DOptionClick () {
 
 
 		var mesh = new THREE.Object3D();
 		var mesh = new THREE.Object3D();
 		mesh.name = 'Object3D ' + ( ++ meshCount );
 		mesh.name = 'Object3D ' + ( ++ meshCount );
@@ -31,19 +16,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
-
-	// divider
+	}
 
 
-	options.add( new UI.HorizontalRule() );
-
-	// add plane
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Plane' );
-	option.onClick( function () {
+	function onPlaneOptionClick () {
 
 
 		var width = 200;
 		var width = 200;
 		var height = 200;
 		var height = 200;
@@ -59,15 +34,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
+	};
 
 
-	// add box
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Box' );
-	option.onClick( function () {
+	function onBoxOptionClick () {
 
 
 		var width = 100;
 		var width = 100;
 		var height = 100;
 		var height = 100;
@@ -84,15 +53,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
-
-	// add circle
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Circle' );
-	option.onClick( function () {
+	}
+	
+	function onCircleOptionClick () {
 
 
 		var radius = 20;
 		var radius = 20;
 		var segments = 8;
 		var segments = 8;
@@ -104,15 +67,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
-
-	// add cylinder
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Cylinder' );
-	option.onClick( function () {
+	function onCylinderOptionClick () {
 
 
 		var radiusTop = 20;
 		var radiusTop = 20;
 		var radiusBottom = 20;
 		var radiusBottom = 20;
@@ -128,15 +85,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// add sphere
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Sphere' );
-	option.onClick( function () {
+	function onSphereOptionClick () {
 
 
 		var radius = 75;
 		var radius = 75;
 		var widthSegments = 32;
 		var widthSegments = 32;
@@ -149,15 +100,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
-
-	// add icosahedron
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Icosahedron' );
-	option.onClick( function () {
+	function onIcosahedronOptionClick () {
 
 
 		var radius = 75;
 		var radius = 75;
 		var detail = 2;
 		var detail = 2;
@@ -169,15 +114,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// add torus
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Torus' );
-	option.onClick( function () {
+	function onTorusOptionClick () {
 
 
 		var radius = 100;
 		var radius = 100;
 		var tube = 40;
 		var tube = 40;
@@ -192,15 +131,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
-
-	// add torus knot
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'TorusKnot' );
-	option.onClick( function () {
+ 	function onTorusKnotOptionClick () {
 
 
 		var radius = 100;
 		var radius = 100;
 		var tube = 40;
 		var tube = 40;
@@ -217,19 +150,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( mesh );
 		editor.addObject( mesh );
 		editor.select( mesh );
 		editor.select( mesh );
 
 
-	} );
-	options.add( option );
-
-	// divider
+	}
 
 
-	options.add( new UI.HorizontalRule() );
-
-	// add sprite
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Sprite' );
-	option.onClick( function () {
+	function onSpriteOptionClick () {
 
 
 		var sprite = new THREE.Sprite( new THREE.SpriteMaterial() );
 		var sprite = new THREE.Sprite( new THREE.SpriteMaterial() );
 		sprite.name = 'Sprite ' + ( ++ meshCount );
 		sprite.name = 'Sprite ' + ( ++ meshCount );
@@ -237,19 +160,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( sprite );
 		editor.addObject( sprite );
 		editor.select( sprite );
 		editor.select( sprite );
 
 
-	} );
-	options.add( option );
-
-	// divider
-
-	options.add( new UI.HorizontalRule() );
+	}
 
 
-	// add point light
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Point light' );
-	option.onClick( function () {
+	function onPointLightOptionClick () {
 
 
 		var color = 0xffffff;
 		var color = 0xffffff;
 		var intensity = 1;
 		var intensity = 1;
@@ -261,15 +174,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( light );
 		editor.addObject( light );
 		editor.select( light );
 		editor.select( light );
 
 
-	} );
-	options.add( option );
-
-	// add spot light
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Spot light' );
-	option.onClick( function () {
+	function onSpotLightOptionClick () {
 
 
 		var color = 0xffffff;
 		var color = 0xffffff;
 		var intensity = 1;
 		var intensity = 1;
@@ -286,15 +193,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( light );
 		editor.addObject( light );
 		editor.select( light );
 		editor.select( light );
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// add directional light
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Directional light' );
-	option.onClick( function () {
+	function onDirectionalLightOptionClick () {
 
 
 		var color = 0xffffff;
 		var color = 0xffffff;
 		var intensity = 1;
 		var intensity = 1;
@@ -308,15 +209,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( light );
 		editor.addObject( light );
 		editor.select( light );
 		editor.select( light );
 
 
-	} );
-	options.add( option );
-
-	// add hemisphere light
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Hemisphere light' );
-	option.onClick( function () {
+	function onHemisphereLightOptionClick () {
 
 
 		var skyColor = 0x00aaff;
 		var skyColor = 0x00aaff;
 		var groundColor = 0xffaa00;
 		var groundColor = 0xffaa00;
@@ -330,15 +225,9 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( light );
 		editor.addObject( light );
 		editor.select( light );
 		editor.select( light );
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// add ambient light
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Ambient light' );
-	option.onClick( function () {
+	function onAmbientLightOptionClick() {
 
 
 		var color = 0x222222;
 		var color = 0x222222;
 
 
@@ -348,11 +237,39 @@ Menubar.Add = function ( editor ) {
 		editor.addObject( light );
 		editor.addObject( light );
 		editor.select( light );
 		editor.select( light );
 
 
-	} );
-	options.add( option );
+	}
+
+	// configure menu contents
+
+	with(UI.MenubarHelper) {
+
+		menuConfig   = [
+			createOption( 'Object3D', onObject3DOptionClick ),
+			createDivider(),
+
+			createOption( 'Plane', onPlaneOptionClick ),
+			createOption( 'Box', onBoxOptionClick ),
+			createOption( 'Circle', onCircleOptionClick ),
+			createOption( 'Cylinder', onCylinderOptionClick ),
+			createOption( 'Sphere', onSphereOptionClick  ),
+			createOption( 'Icosahedron', onIcosahedronOptionClick ),
+			createOption( 'Torus', onTorusOptionClick ),
+			createOption( 'Torus Knot', onTorusKnotOptionClick ),
+			createDivider(),
+
+			createOption( 'Sprite', onSpriteOptionClick  ),
+			createDivider(),
+
+			createOption( 'Point light', onPointLightOptionClick ),
+			createOption( 'Spot light', onSpotLightOptionClick ),
+			createOption( 'Directional light', onDirectionalLightOptionClick ),
+			createOption( 'Hemisphere light', onHemisphereLightOptionClick ),
+			createOption( 'Ambient light', onAmbientLightOptionClick )
+		];
 
 
-	//
+	}
 
 
-	return container;
+	optionsPanel = UI.MenubarHelper.createOptionsPanel( menuConfig );
 
 
+	return UI.MenubarHelper.createMenuContainer( 'Add', optionsPanel );
 }
 }

+ 41 - 48
editor/js/Menubar.Edit.js

@@ -1,26 +1,23 @@
 Menubar.Edit = function ( editor ) {
 Menubar.Edit = function ( editor ) {
 
 
-	var container = new UI.Panel();
-	container.setClass( 'menu' );
+	var menuConfig,
+		optionsPanel;
 
 
-	var title = new UI.Panel();
-	title.setTextContent( 'Edit' );
-	title.setMargin( '0px' );
-	title.setPadding( '8px' );
-	container.add( title );
+	// event handlers
 
 
-	//
+	// function onUndoOptionClick () {
 
 
-	var options = new UI.Panel();
-	options.setClass( 'options' );
-	container.add( options );
+	// 	console.log( 'UNDO not implemented yet' );
 
 
-	// clone
+	// }
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Clone' );
-	option.onClick( function () {
+	// function onRedoOptionClick () {
+
+	// 	console.log( 'REDO not implemented yet' );
+
+	// }
+
+	function onCloneOptionClick () {
 
 
 		var object = editor.selected;
 		var object = editor.selected;
 
 
@@ -31,33 +28,20 @@ Menubar.Edit = function ( editor ) {
 		editor.addObject( object );
 		editor.addObject( object );
 		editor.select( object );
 		editor.select( object );
 
 
-	} );
-	options.add( option );
-
-	// delete
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Delete' );
-	option.onClick( function () {
+	function onDeleteOptionClick () {
 
 
 		editor.removeObject( editor.selected );
 		editor.removeObject( editor.selected );
 		editor.deselect();
 		editor.deselect();
 
 
-	} );
-	options.add( option );
+	}
 
 
-	options.add( new UI.HorizontalRule() );
-
-	// convert to BufferGeometry
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Convert' );
-	option.onClick( function () {
+	function onConvertOptionClick () {
 
 
+		// convert to BufferGeometry
+		
 		var object = editor.selected;
 		var object = editor.selected;
-
 		if ( object.geometry instanceof THREE.Geometry ) {
 		if ( object.geometry instanceof THREE.Geometry ) {
 
 
 			if ( object.parent === undefined ) return; // avoid flattening the camera or scene
 			if ( object.parent === undefined ) return; // avoid flattening the camera or scene
@@ -69,18 +53,11 @@ Menubar.Edit = function ( editor ) {
 			object.geometry = THREE.BufferGeometryUtils.fromGeometry( object.geometry );
 			object.geometry = THREE.BufferGeometryUtils.fromGeometry( object.geometry );
 
 
 			editor.signals.objectChanged.dispatch( object );
 			editor.signals.objectChanged.dispatch( object );
-
 		}
 		}
 
 
-	} );
-	options.add( option );
-
-	// flatten
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Flatten' );
-	option.onClick( function () {
+	function onFlattenOptionClick () {
 
 
 		var object = editor.selected;
 		var object = editor.selected;
 
 
@@ -101,12 +78,28 @@ Menubar.Edit = function ( editor ) {
 
 
 		editor.signals.objectChanged.dispatch( object );
 		editor.signals.objectChanged.dispatch( object );
 
 
-	} );
-	options.add( option );
+	}
+
+	// configure menu contents
+
+	with(UI.MenubarHelper) {
+
+		menuConfig   = [
+			// createOption( 'Undo', onUndoOptionClick ),
+			// createOption( 'Redo', onRedoOptionClick ),
+			// createDivider(),
+
+			createOption( 'Clone', onCloneOptionClick ),
+			createOption( 'Delete', onDeleteOptionClick ),
+			createDivider(),
 
 
+			createOption( 'Convert', onConvertOptionClick ),
+			createOption( 'Flatten', onFlattenOptionClick )
+		];
 
 
-	//
+	}
 
 
-	return container;
+	optionsPanel = UI.MenubarHelper.createOptionsPanel( menuConfig );
 
 
+	return UI.MenubarHelper.createMenuContainer( 'Edit', optionsPanel );
 }
 }

+ 100 - 123
editor/js/Menubar.File.js

@@ -1,74 +1,102 @@
 Menubar.File = function ( editor ) {
 Menubar.File = function ( editor ) {
 
 
-	var container = new UI.Panel();
-	container.setClass( 'menu' );
+	var menuConfig,
+		optionsPanel,
+		fileInput;
 
 
-	var title = new UI.Panel();
-	title.setTextContent( 'File' );
-	title.setMargin( '0px' );
-	title.setPadding( '8px' );
-	container.add( title );
 
 
-	//
+	// helpers
+	
+	function exportGeometry ( exporterClass ) {
 
 
-	var options = new UI.Panel();
-	options.setClass( 'options' );
-	container.add( options );
-
-	// new
+		var object = editor.selected;
+		var exporter = new exporterClass();
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'New' );
-	option.onClick( function () {
+		var output;
 
 
-		if ( confirm( 'Are you sure?' ) ) {
+		if ( exporter instanceof THREE.BufferGeometryExporter ||
+			 exporter instanceof THREE.Geometry2Exporter ||
+		     exporter instanceof THREE.GeometryExporter ) {
 
 
-			editor.config.clear();
-			editor.storage.clear( function () {
+			output = JSON.stringify( exporter.parse( object.geometry ), null, '\t' );
+			output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
 
 
-				location.href = location.pathname;
+		} else {
 
 
-			} );
+			output = exporter.parse( object.geometry );
 
 
 		}
 		}
 
 
-	} );
-	options.add( option );
+		var blob = new Blob( [ output ], { type: 'text/plain' } );
+		var objectURL = URL.createObjectURL( blob );
 
 
-	options.add( new UI.HorizontalRule() );
+		window.open( objectURL, '_blank' );
+		window.focus();
 
 
+	};
 
 
-	// import
+	function exportObject ( exporterClass ) {
 
 
-	var input = document.createElement( 'input' );
-	input.type = 'file';
-	input.addEventListener( 'change', function ( event ) {
+		var object = editor.selected;
+		var exporter = new exporterClass();
 
 
-		editor.loader.loadFile( input.files[ 0 ] );
+		var output = JSON.stringify( exporter.parse( object ), null, '\t' );
+		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
 
 
-	} );
+		var blob = new Blob( [ output ], { type: 'text/plain' } );
+		var objectURL = URL.createObjectURL( blob );
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Import' );
-	option.onClick( function () {
+		window.open( objectURL, '_blank' );
+		window.focus();
+
+	}
+
+	function exportScene ( exporterClass ) {
+
+		var exporter = new exporterClass();
+
+		var output = JSON.stringify( exporter.parse( editor.scene ), null, '\t' );
+		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
+
+		var blob = new Blob( [ output ], { type: 'text/plain' } );
+		var objectURL = URL.createObjectURL( blob );
+
+		window.open( objectURL, '_blank' );
+		window.focus();
 
 
-		input.click();
+	}
+
+	// event handlers
+
+	function onNewOptionClick () {
+
+		if ( confirm( 'Are you sure?' ) ) {
+
+			editor.config.clear();
+			editor.storage.clear( function () {
+
+				location.href = location.pathname;
+
+			} );
+
+		}
 
 
-	} );
-	options.add( option );
+	}
 
 
-	options.add( new UI.HorizontalRule() );
+	function onImportOptionClick () {
 
 
+		fileInput.click();
 
 
-	// export geometry
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Export Geometry' );
-	option.onClick( function () {
+	function onFileInputChange ( event ) {
 
 
+		editor.loader.loadFile( fileInput.files[ 0 ] );
+
+	}
+
+	function onExportGeometryOptionClick () {
+		
 		var object = editor.selected;
 		var object = editor.selected;
 
 
 		if ( object === null ) {
 		if ( object === null ) {
@@ -101,15 +129,9 @@ Menubar.File = function ( editor ) {
 
 
 		}
 		}
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// export object
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Export Object' );
-	option.onClick( function () {
+	function onExportObjectOptionClick () {
 
 
 		if ( editor.selected === null ) {
 		if ( editor.selected === null ) {
 
 
@@ -120,92 +142,47 @@ Menubar.File = function ( editor ) {
 
 
 		exportObject( THREE.ObjectExporter );
 		exportObject( THREE.ObjectExporter );
 
 
-	} );
-	options.add( option );
-
-	// export scene
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Export Scene' );
-	option.onClick( function () {
+	function onExportSceneOptionClick () {
 
 
 		exportScene( THREE.ObjectExporter );
 		exportScene( THREE.ObjectExporter );
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// export OBJ
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Export OBJ' );
-	option.onClick( function () {
+	function onExportOBJOptionClick () {
 
 
 		exportGeometry( THREE.OBJExporter );
 		exportGeometry( THREE.OBJExporter );
 
 
-	} );
-	options.add( option );
-
-	var exportGeometry = function ( exporterClass ) {
-
-		var object = editor.selected;
-		var exporter = new exporterClass();
-
-		var output;
-
-		if ( exporter instanceof THREE.BufferGeometryExporter ||
-			 exporter instanceof THREE.Geometry2Exporter ||
-		     exporter instanceof THREE.GeometryExporter ) {
-
-			output = JSON.stringify( exporter.parse( object.geometry ), null, '\t' );
-			output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
-
-		} else {
-
-			output = exporter.parse( object.geometry );
-
-		}
-
-		var blob = new Blob( [ output ], { type: 'text/plain' } );
-		var objectURL = URL.createObjectURL( blob );
-
-		window.open( objectURL, '_blank' );
-		window.focus();
+	}
 
 
-	};
+	// create file input element for scene import
 
 
-	var exportObject = function ( exporterClass ) {
-
-		var object = editor.selected;
-		var exporter = new exporterClass();
+	fileInput = document.createElement( 'input' );
+	fileInput.type = 'file';
+	fileInput.addEventListener( 'change', onFileInputChange);
 
 
-		var output = JSON.stringify( exporter.parse( object ), null, '\t' );
-		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
+	// configure menu contents
+	
+	with(UI.MenubarHelper) {
 
 
-		var blob = new Blob( [ output ], { type: 'text/plain' } );
-		var objectURL = URL.createObjectURL( blob );
+		menuConfig   = [
+			createOption( 'New', onNewOptionClick ),
+			createDivider(),
 
 
-		window.open( objectURL, '_blank' );
-		window.focus();
+			createOption( 'Import', onImportOptionClick ),
+			createDivider(),
 
 
-	};
+			createOption( 'Export Geometry', onExportGeometryOptionClick ),
+			createOption( 'Export Object', onExportObjectOptionClick ),
+			createOption( 'Export Scene', onExportSceneOptionClick ),
+			createOption( 'Export OBJ', onExportOBJOptionClick )
+		];
+	
+	}
 
 
-	var exportScene = function ( exporterClass ) {
-
-		var exporter = new exporterClass();
-
-		var output = JSON.stringify( exporter.parse( editor.scene ), null, '\t' );
-		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
-
-		var blob = new Blob( [ output ], { type: 'text/plain' } );
-		var objectURL = URL.createObjectURL( blob );
-
-		window.open( objectURL, '_blank' );
-		window.focus();
-
-	};
+	optionsPanel = UI.MenubarHelper.createOptionsPanel( menuConfig );
 
 
-	return container;
+	return UI.MenubarHelper.createMenuContainer( 'File', optionsPanel );
 
 
 }
 }

+ 21 - 25
editor/js/Menubar.Help.js

@@ -1,38 +1,34 @@
 Menubar.Help = function ( editor ) {
 Menubar.Help = function ( editor ) {
 
 
-	var container = new UI.Panel();
-	container.setClass( 'menu' );
+	var menuConfig,
+		optionsPanel;
 
 
-	var title = new UI.Panel();
-	title.setTextContent( 'Help' );
-	title.setMargin( '0px' );
-	title.setPadding( '8px' );
-	container.add( title );
+	// event handlers
 
 
-	//
+	function onSourcecodeOptionClick () {
 
 
-	var options = new UI.Panel();
-	options.setClass( 'options' );
-	container.add( options );
+		window.open( 'https://github.com/mrdoob/three.js/tree/master/editor', '_blank' )
 
 
-	// source code
+	}
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Source code' );
-	option.onClick( function () { window.open( 'https://github.com/mrdoob/three.js/tree/master/editor', '_blank' ) } );
-	options.add( option );
+	function onAboutOptionClick () {
 
 
-	// about
+		window.open( 'http://threejs.org', '_blank' );
 
 
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'About' );
-	option.onClick( function () { window.open( 'http://threejs.org', '_blank' ) } );
-	options.add( option );
+	}
 
 
-	//
+	// configure menu contents
 
 
-	return container;
+	with(UI.MenubarHelper) {
 
 
+		menuConfig = [
+			createOption( 'Source code', onSourcecodeOptionClick ),
+			createOption( 'About', onAboutOptionClick )
+		];
+
+	}
+
+	optionsPanel = UI.MenubarHelper.createOptionsPanel( menuConfig );
+
+	return UI.MenubarHelper.createMenuContainer( 'Help', optionsPanel );
 }
 }

+ 20 - 33
editor/js/Menubar.View.js

@@ -1,48 +1,35 @@
 Menubar.View = function ( editor ) {
 Menubar.View = function ( editor ) {
+	var menuConfig,
+		optionsPanel;
 
 
-	var container = new UI.Panel();
-	container.setClass( 'menu' );
-
-	var title = new UI.Panel();
-	title.setTextContent( 'View' );
-	title.setMargin( '0px' );
-	title.setPadding( '8px' );
-	container.add( title );
-
-	//
-
-	var options = new UI.Panel();
-	options.setClass( 'options' );
-	container.add( options );
-
-	// themes
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Light theme' );
-	option.onClick( function () {
+	function onLightThemeOptionClick () {
 
 
 		editor.setTheme( 'css/light.css' );
 		editor.setTheme( 'css/light.css' );
 		editor.config.setKey( 'theme', 'css/light.css' );
 		editor.config.setKey( 'theme', 'css/light.css' );
 
 
-	} );
-	options.add( option );
+	}
 
 
-	// about
-
-	var option = new UI.Panel();
-	option.setClass( 'option' );
-	option.setTextContent( 'Dark theme' );
-	option.onClick( function () {
+	function onDarkThemeOptionClick () {
 
 
 		editor.setTheme( 'css/dark.css' );
 		editor.setTheme( 'css/dark.css' );
 		editor.config.setKey( 'theme', 'css/dark.css' );
 		editor.config.setKey( 'theme', 'css/dark.css' );
 
 
-	} );
-	options.add( option );
+	}
+	
+
+	// configure menu contents
+
+	with(UI.MenubarHelper) {
+
+		menuConfig = [
+			createOption( 'Light theme', onLightThemeOptionClick ),
+			createOption( 'Dark theme', onDarkThemeOptionClick )
+		];
+
+	}
 
 
-	//
+	optionsPanel = UI.MenubarHelper.createOptionsPanel( menuConfig );
 
 
-	return container;
+	return UI.MenubarHelper.createMenuContainer( 'Help', optionsPanel );
 
 
 }
 }

+ 57 - 0
editor/js/libs/ui.js

@@ -945,3 +945,60 @@ UI.Button.prototype.setLabel = function ( value ) {
 	return this;
 	return this;
 
 
 };
 };
+
+
+UI.MenubarHelper = (function() {
+
+	function createMenuContainer( name, optionsPanel ) {
+		var container,
+			title;
+
+		container		= new UI.Panel();
+		title			= new UI.Panel();
+
+		title.setTextContent( name );
+		title.setMargin( '0px' );
+		title.setPadding( '8px' );
+
+		container.setClass( 'menu' );
+		container.add( title );
+		container.add( optionsPanel );
+
+		return container;
+	}
+	
+	function createOption(name, callbackHandler) {
+		var option;
+
+		option = new UI.Panel();
+		option.setClass( 'option' );
+		option.setTextContent( name );
+		option.onClick( callbackHandler );
+
+		return option;
+	}
+
+	function createOptionsPanel(menuConfig) {
+		var options;
+
+		options = new UI.Panel();
+		options.setClass( 'options' );
+
+		menuConfig.forEach(function(option) {
+			options.add(option);
+		});
+
+		return options;
+	}
+
+	function createDivider() {
+		return new UI.HorizontalRule();
+	}
+
+	return {
+		createMenuContainer:createMenuContainer,
+		createOption:createOption,
+		createOptionsPanel:createOptionsPanel,
+		createDivider:createDivider
+	};
+})();