Browse Source

Merge branch 'dev' of https://github.com/mrdoob/three.js into dev

Neha Agarwal 10 years ago
parent
commit
7ea73e7c0a
100 changed files with 9903 additions and 1573 deletions
  1. 6 0
      .npmignore
  2. 1 1
      README.md
  3. 226 245
      build/three.js
  4. 294 266
      build/three.min.js
  5. 2 2
      docs/api/CONTRIBUTING.md
  6. 15 0
      docs/api/cameras/Camera.html
  7. 2 2
      docs/api/cameras/CubeCamera.html
  8. 30 1
      docs/api/cameras/OrthographicCamera.html
  9. 23 2
      docs/api/cameras/PerspectiveCamera.html
  10. 2 2
      docs/api/core/EventDispatcher.html
  11. 31 2
      docs/api/core/Geometry.html
  12. 1 1
      docs/api/core/Object3D.html
  13. 0 5
      docs/api/core/Raycaster.html
  14. 14 2
      docs/api/examples/cameras/CombinedCamera.html
  15. 17 34
      docs/api/extras/ImageUtils.html
  16. 2 2
      docs/api/extras/SceneUtils.html
  17. 2 2
      docs/api/extras/animation/Animation.html
  18. 0 105
      docs/api/extras/animation/AnimationMorphTarget.html
  19. 36 54
      docs/api/extras/animation/KeyFrameAnimation.html
  20. 5 2
      docs/api/extras/core/Curve.html
  21. 48 45
      docs/api/extras/core/CurvePath.html
  22. 15 12
      docs/api/extras/core/Gyroscope.html
  23. 64 21
      docs/api/extras/core/Path.html
  24. 51 23
      docs/api/extras/core/Shape.html
  25. 12 3
      docs/api/extras/geometries/BoxGeometry.html
  26. 2 0
      docs/api/extras/geometries/CircleGeometry.html
  27. 2 0
      docs/api/extras/geometries/CylinderGeometry.html
  28. 2 0
      docs/api/extras/geometries/DodecahedronGeometry.html
  29. 1 0
      docs/api/extras/geometries/IcosahedronGeometry.html
  30. 1 0
      docs/api/extras/geometries/OctahedronGeometry.html
  31. 2 0
      docs/api/extras/geometries/PlaneGeometry.html
  32. 3 1
      docs/api/extras/geometries/RingGeometry.html
  33. 13 12
      docs/api/extras/geometries/ShapeGeometry.html
  34. 2 0
      docs/api/extras/geometries/SphereGeometry.html
  35. 2 0
      docs/api/extras/geometries/TetrahedronGeometry.html
  36. 86 4
      docs/api/extras/geometries/TextGeometry.html
  37. 1 0
      docs/api/extras/geometries/TorusGeometry.html
  38. 2 0
      docs/api/extras/geometries/TorusKnotGeometry.html
  39. 7 15
      docs/api/extras/helpers/PointLightHelper.html
  40. 18 19
      docs/api/extras/helpers/SpotLightHelper.html
  41. 73 56
      docs/api/extras/objects/MorphBlendMesh.html
  42. 20 2
      docs/api/lights/AmbientLight.html
  43. 5 2
      docs/api/lights/AreaLight.html
  44. 27 4
      docs/api/lights/DirectionalLight.html
  45. 23 1
      docs/api/lights/HemisphereLight.html
  46. 9 1
      docs/api/lights/Light.html
  47. 30 2
      docs/api/lights/PointLight.html
  48. 48 19
      docs/api/lights/SpotLight.html
  49. 9 9
      docs/api/materials/MeshBasicMaterial.html
  50. 0 18
      docs/api/materials/MeshLambertMaterial.html
  51. 5 15
      docs/api/materials/MeshPhongMaterial.html
  52. 17 25
      docs/api/objects/Bone.html
  53. 32 17
      docs/api/objects/LOD.html
  54. 22 15
      docs/api/objects/LensFlare.html
  55. 2 13
      docs/api/objects/Line.html
  56. 53 0
      docs/api/objects/LineSegments.html
  57. 33 1
      docs/api/objects/Mesh.html
  58. 83 55
      docs/api/objects/MorphAnimMesh.html
  59. 128 0
      docs/api/objects/Skeleton.html
  60. 76 12
      docs/api/objects/SkinnedMesh.html
  61. 9 9
      docs/api/renderers/webgl/WebGLProgram.html
  62. 23 21
      docs/api/renderers/webgl/WebGLShader.html
  63. 58 0
      docs/api/textures/CubeTexture.html
  64. 1 1
      docs/index.html
  65. 4 2
      docs/list.js
  66. 2 0
      docs/manual/introduction/Creating-a-scene.html
  67. 10 9
      docs/manual/introduction/Matrix-transformations.html
  68. 1 1
      docs/page.js
  69. 272 0
      docs/scenes/bones-browser.html
  70. 144 0
      docs/scenes/geometry-browser.html
  71. 549 0
      docs/scenes/js/geometry.js
  72. 0 5
      docs/scenes/js/material.js
  73. 64 29
      editor/examples/arkanoid.app.json
  74. 38 5
      editor/index.html
  75. 1 1
      editor/js/Menubar.Edit.js
  76. 4 1
      editor/js/Menubar.File.js
  77. 188 30
      editor/js/Script.js
  78. 4 30
      editor/js/Sidebar.Geometry.js
  79. 238 194
      editor/js/Sidebar.Material.js
  80. 7 0
      editor/js/Sidebar.Script.js
  81. 1 1
      editor/js/Viewport.js
  82. 877 0
      editor/js/libs/acorn/acorn.js
  83. 1299 0
      editor/js/libs/acorn/acorn_loose.js
  84. 344 0
      editor/js/libs/acorn/walk.js
  85. 13 6
      editor/js/libs/app.js
  86. 32 0
      editor/js/libs/codemirror/addon/dialog.css
  87. 157 0
      editor/js/libs/codemirror/addon/dialog.js
  88. 38 0
      editor/js/libs/codemirror/addon/show-hint.css
  89. 383 0
      editor/js/libs/codemirror/addon/show-hint.js
  90. 86 0
      editor/js/libs/codemirror/addon/tern.css
  91. 698 0
      editor/js/libs/codemirror/addon/tern.js
  92. 24 10
      editor/js/libs/codemirror/codemirror.css
  93. 928 64
      editor/js/libs/codemirror/codemirror.js
  94. 245 0
      editor/js/libs/codemirror/mode/glsl.js
  95. 13 2
      editor/js/libs/codemirror/mode/javascript.js
  96. 2 0
      editor/js/libs/codemirror/theme/monokai.css
  97. 456 0
      editor/js/libs/jsonlint.js
  98. 276 0
      editor/js/libs/tern-threejs/threejs.js
  99. 87 0
      editor/js/libs/ternjs/comment.js
  100. 589 0
      editor/js/libs/ternjs/def.js

+ 6 - 0
.npmignore

@@ -0,0 +1,6 @@
+examples/
+src/
+test/
+utils/
+docs/
+editor/

+ 1 - 1
README.md

@@ -17,7 +17,7 @@ Alternatively see [how to build the library yourself](https://github.com/mrdoob/
 <script src="js/three.min.js"></script>
 ```
 
-This code creates a scene, a camera, and a geometric cube, and it adds the cube to the scene. It then creates a &lt;canvas&gt; renderer for the scene and camera, and it adds that viewport to the document.body element. Finally it animates the cube within the scene for the camera.
+This code creates a scene, a camera, and a geometric cube, and it adds the cube to the scene. It then creates a `WebGL` renderer for the scene and camera, and it adds that viewport to the document.body element. Finally it animates the cube within the scene for the camera.
 
 ```html
 <script>

File diff suppressed because it is too large
+ 226 - 245
build/three.js


File diff suppressed because it is too large
+ 294 - 266
build/three.min.js


+ 2 - 2
docs/api/CONTRIBUTING.md

@@ -2,6 +2,6 @@ Contributing to the documentation
 =================================
 
 - To link to the page for `ClassName`, use `[page:ClassName link title]` (or just `[page:ClassName]`). Use `[page:ClassName.memberName]` to link to a class member (a property or method) called `memberName` on the page for `ClassName`. You can write `[page:.memberName]` to link to a property or method called `memberName` on the same page.
-- Use `[example:exampleName title]` (ot just `[example:exampleName]`) to link to the example `threejs.org/examples/#exampleName`.
+- Use `[example:exampleName title]` (not just `[example:exampleName]`) to link to the example `threejs.org/examples/#exampleName`.
 - Document a property by writing `<h3>[property:TypeName propertyName]</h3>`.
-- Document a method using `<h3>[method:ReturnType methodName]</h3>`.
+- Document a method using `<h3>[method:ReturnType methodName]</h3>`.

+ 15 - 0
docs/api/cameras/Camera.html

@@ -34,6 +34,14 @@
 
 
 		<h2>Methods</h2>
+		
+		<h3>[method:Vector3 getWorldDirection]([page:Vector3 vector])</h3>
+		<div>
+		vector — (optional)<br />
+		<br />
+		It returns a vector representing the direction in which the camera is looking, in world space.
+		</div>
+		
 
 		<h3>[method:null lookAt]( [page:Vector3 vector] )</h3>
 		<div>
@@ -41,6 +49,13 @@
 		<br />
 		This makes the camera look at the vector position in the global space as long as the parent of this camera is the scene or at position (0,0,0).
 		</div>
+		
+		<h3>[method:Camera clone]( [page:Camera camera] )</h3>
+		<div>
+		camera — camera to clone<br />
+		<br />
+		It returns a clone of camera.
+		</div>
 
 
 		<h2>Source</h2>

+ 2 - 2
docs/api/cameras/CubeCamera.html

@@ -17,7 +17,7 @@
 		
 		<div>[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]</div>
 		<div>[example:webgl_materials_cubemap_dynamic2 materials / cubemap / dynamic2 ]</div>
-		<div>[example:webgl_materials_cubemap_dynamic2 shading / physical ]</div>
+		<div>[example:webgl_shading_physical shading / physical ]</div>
 		
 		<code>//Create cube camera
 		var cubeCamera = new THREE.CubeCamera( 1, 100000, 128 );
@@ -67,7 +67,7 @@
 
 
 
-		<h3>[method:todo updateCubeMap]([page:todo renderer], [page:todo scene])</h3>
+		<h3>[method:null updateCubeMap]([page:WebGLRenderer renderer], [page:Scene scene])</h3>
 		<div>
 		renderer -- The current WebGL renderer <br />
 		scene -- The current scene

+ 30 - 1
docs/api/cameras/OrthographicCamera.html

@@ -15,7 +15,21 @@
 
 
 		<h2>Example</h2>
-
+		
+		<div>[example:canvas_camera_orthographic camera / orthographic ]</div>
+		<div>[example:canvas_camera_orthographic2 camera / orthographic2 ]</div>
+		<div>[example:webgl_camera camera ]</div>
+		<div>[example:webgl_interactive_cubes_ortho interactive / cubes / ortho ]</div>
+		<div>[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]</div>
+		<div>[example:webgl_materials_normaldisplacementmap materials / normaldisplacementmap ]</div>
+		<div>[example:webgl_postprocessing_advanced postprocessing / advanced ]</div>
+		<div>[example:webgl_postprocessing_dof2 postprocessing / dof2 ]</div>
+		<div>[example:webgl_postprocessing_godrays postprocessing / godrays ]</div>
+		<div>[example:webgl_rtt rtt ]</div>
+		<div>[example:webgl_shaders_tonemapping shaders / tonemapping ]</div>
+		<div>[example:webgl_shadowmap shadowmap ]</div>
+		<div>[example:webgl_terrain_dynamic terrain / dynamic ]</div>
+		
 		<code>var camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 );
 scene.add( camera );</code>
 
@@ -36,6 +50,9 @@ scene.add( camera );</code>
 
 		<h2>Properties</h2>
 
+		<h3>[property:number zoom]</h3>
+		<div>Gets or sets the zoom factor of the camera. </div>
+		
 		<h3>[property:Float left]</h3>
 		<div>Camera frustum left plane.</div>
 
@@ -61,6 +78,18 @@ scene.add( camera );</code>
 		<div>
 		Updates the camera projection matrix. Must be called after change of parameters.
 		</div>
+		
+		<h3>[method:OrthographicCamera clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of OrthographicCamera.
+		</div>
+
+		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return camera data in JSON format.
+		</div>
 
 
 		<h2>Source</h2>

+ 23 - 2
docs/api/cameras/PerspectiveCamera.html

@@ -15,7 +15,15 @@
 
 
 		<h2>Example</h2>
-
+		
+		<div>[example:canvas_effects_stereo effects / stereo ]</div>
+		<div>[example:canvas_geometry_birds geometry / birds ]</div>
+		<div>[example:canvas_geometry_cube geometry / cube ]</div>
+		<div>[example:webgl_animation_skinning_blending animation / skinning / blending ]</div>
+		<div>[example:webgl_animation_skinning_morph animation / skinning / blending ]</div>
+		<div>[example:webgl_interactive_cubes interactive / cubes ]</div>
+		<div>[example:webgl_loader_collada_skinning loader / collada / skinning ]</div>
+		
 		<code>var camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
 scene.add( camera );</code>
 
@@ -33,6 +41,9 @@ scene.add( camera );</code>
 
 		<h2>Properties</h2>
 
+		<h3>[property:number zoom]</h3>
+		<div>Gets or sets the zoom factor of the camera. </div>
+		
 		<h3>[property:Float fov]</h3>
 		<div>Camera frustum vertical field of view, from bottom to top of view, in degrees.</div>
 
@@ -110,7 +121,17 @@ camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
 		<div>
 		Updates the camera projection matrix. Must be called after change of parameters.
 		</div>
-
+		
+		<h3>[method:PerspectiveCamera clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of PerspectiveCamera.
+		</div>
+		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return camera data in JSON format.
+		</div>
 
 		<h2>Source</h2>
 

+ 2 - 2
docs/api/core/EventDispatcher.html

@@ -50,9 +50,9 @@
 		Removes a listener from an event type.
 		</div>
 
-		<h3>[method:null dispatchEvent]( [page:String type] )</h3>
+		<h3>[method:null dispatchEvent]( [page:object event] )</h3>
 		<div>
-		type - The type of event that gets fired.
+		event - The event that gets fired.
 		</div>
 		<div>
 		Fire an event type.

+ 31 - 2
docs/api/core/Geometry.html

@@ -102,12 +102,30 @@
 
 		<h3>[property:Array skinWeights]</h3>
 		<div>
-		Array of skinning weights, matching number and order of vertices.
+		Array of [page:Vector4 Vector4s] representing the skinning weights as used in a [page:SkinnedMesh],
+		The weights match the number and order of vertices in the geometry. The weighted values
+		are typically between the values of 0 and 1 and affect the amount that the individuals bones affect
+		a given vertex.
 		</div>
 
 		<h3>[property:Array skinIndices]</h3>
 		<div>
-		Array of skinning indices, matching number and order of vertices.
+		Array of [page:Vector4 Vector4s] representing the indices of individual bones in the [page:Skeleton.bones] array,
+		The indices match the number and order of vertices in the geometry.
+		<code>
+		// e.g.
+		geometry.skinIndices[15] = new THREE.Vector4(   0,   5,   9, 0 );
+		geometry.skinWeights[15] = new THREE.Vector4( 0.2, 0.5, 0.3, 0 );
+		
+		// corresponds with the following vertex
+		geometry.vertices[15];
+		
+		// these bones will be used like so:
+		skeleton.bones[0]; // weight of 0.2
+		skeleton.bones[5]; // weight of 0.5
+		skeleton.bones[9]; // weight of 0.3
+		skeleton.bones[0]; // weight of 0
+		</code>
 		</div>
 
 		<h3>[property:Object boundingBox]</h3>
@@ -185,6 +203,11 @@
 		Bakes matrix transform directly into vertex coordinates.
 		</div>
 
+		<h3>[method:null center]()</h3>
+		<div>
+		Center the geometry based on the bounding box.
+		</div>
+
 		<h3>[method:null computeFaceNormals]()</h3>
 		<div>
 		Computes face normals.
@@ -228,6 +251,12 @@
 		Checks for duplicate vertices using hashmap.<br />
 		Duplicated vertices are removed and faces' vertices are updated.
 		</div>
+
+		<h3>[method:null normalize]()</h3>
+		<div>
+		Normalize the geometry. <br />
+		Make the geometry centered and has a bounding sphere whose raidus equals to 1.0.
+		</div>
 		
 		<h3>[method:Geometry clone]()</h3>
 		<div>

+ 1 - 1
docs/api/core/Object3D.html

@@ -66,7 +66,7 @@
 
 		<h3>[property:Vector3 up]</h3>
 		<div>
-		Up direction.
+		Up direction. Default is THREE.Vector3( 0, 1, 0 ).
 		</div>
 
 		<h3>[property:Matrix4 matrix]</h3>

+ 0 - 5
docs/api/core/Raycaster.html

@@ -98,11 +98,6 @@
 		This value shouldn't be negative and should be larger than the near property. 
 		</div>
 
-		<h3>[property:float precision]</h3>
-		<div>
-		The precision factor of the raycaster when intersecting [page:Mesh] objects. 
-		</div>
-
 		<h3>.[page:float linePrecision]</h3>
 		<div>
 		The precision factor of the raycaster when intersecting [page:Line] objects. 

+ 14 - 2
docs/api/examples/cameras/CombinedCamera.html

@@ -16,7 +16,14 @@
  		Use this only if you do not wish to manage
  		both an Orthographic and Perspective Camera</div>
 
-
+		<h2>Examples</h2>
+		
+		<div>[example:canvas_camera_orthographic2 camera / orthographic2 ]</div>
+		
+		<code>//Create combined camera
+		camera = new THREE.CombinedCamera( window.innerWidth / 2, window.innerHeight / 2, 70, 1, 1000, - 500, 1000 );
+		</code>
+		
 		<h2>Constructor</h2>
 
 
@@ -79,6 +86,11 @@
 		Gets camera frustum far plane.
 		</div> 
 
+		<h3>[property:Matrix4 projectionMatrix]</h3>
+		<div>
+		This is the matrix which contains the projection.
+		</div>
+		
 		<h3>[property:OrthographicCamera cameraO]</h3>
 		<div>
 		Gets or sets the internal OrthographicCamera used as camera.
@@ -182,6 +194,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/cameras/CombinedCamera.js examples/cameras/CombinedCamera.js]
 	</body>
 </html>

+ 17 - 34
docs/api/extras/ImageUtils.html

@@ -44,18 +44,7 @@
 		Parses a DDS Image from the string into a CompressedTexture. 
 		</div>
 
-		<h3>[method:todo loadCompressedTexture]([page:todo url], [page:todo mapping], [page:todo onLoad], [page:todo onError])</h3>
-		<div>
-		url -- todo <br />
-		mapping -- todo <br />
-		onLoad -- todo <br />
-		onError -- todo
-		</div>
-		<div>
-		todo
-		</div>
-
-		<h3>[method:todo loadTexture]([page:String url], [page:UVMapping mapping], [page:Function onLoad], [page:Function onError])</h3>
+		<h3>[method:Texture loadTexture]([page:String url], [page:UVMapping mapping], [page:Function onLoad], [page:Function onError])</h3>
 		<div>
 		url -- the url of the texture<br />
 		mapping -- Can be an instance of [page:UVMapping THREE.UVMapping], [page:CubeReflectionMapping THREE.CubeReflectionMapping] or [page:SphericalReflectionMapping THREE.SphericalReflectionMapping]. Describes how the image is applied to the object.<br />Use undefined instead of null as a default value. See mapping property of [page:Texture texture] for more details. <br/>
@@ -63,38 +52,32 @@
 		onError -- callback function
 		</div>
 		<div>
-		todo
+		A helper function to generates a [page:Texture THREE.Texture] from an image URL. Provides a load and error
+		callback.
 		</div>
 
-		<h3>[method:todo getNormalMap]([page:todo image], [page:todo depth])</h3>
+		<h3>[method:canvas getNormalMap]([page:Image image], [page:Float depth])</h3>
 		<div>
-		image -- todo <br />
-		depth -- todo
+		image -- A loaded image element <br />
+		depth -- The depth of the normal map. Defaults to between -1 and 1.
 		</div>
 		<div>
-		todo
+		Translates an image element into a normal map with the range (-1, -1, -1) to (1, 1, 1) multiplied by the depth.
+		Returns a canvas element.
 		</div>
 
-		<h3>[method:todo loadCompressedTextureCube]([page:todo array], [page:todo mapping], [page:todo onLoad], [page:todo onError])</h3>
+		<h3>[method:CubeTexture loadTextureCube]([page:Array array], [page:Textures mapping], [page:function onLoad], [page:function onError])</h3>
 		<div>
-		array -- todo <br />
-		mapping -- todo <br />
-		onLoad -- todo <br />
-		onError -- todo
+		array -- An array of 6 images <br />
+		mapping -- Either [page:Textures THREE.CubeReflectionMapping] or [page:Textures THREE.CubeRefractionMapping]<br />
+		onLoad -- callback <br />
+		onError -- callback
 		</div>
 		<div>
-		todo
-		</div>
-
-		<h3>[method:todo loadTextureCube]([page:todo array], [page:todo mapping], [page:todo onLoad], [page:todo onError])</h3>
-		<div>
-		array -- todo <br />
-		mapping -- todo <br />
-		onLoad -- todo <br />
-		onError -- todo
-		</div>
-		<div>
-		todo
+		Creates a [page:CubeTexture] from 6 images.<br /><br />
+		
+		The images are loaded in the following order where p is positiive and n is negative: [ px, nx, py, ny, pz, nz ].
+		See [page:CubeTexture] for an example in code.
 		</div>
 
 		<h2>Source</h2>

+ 2 - 2
docs/api/extras/SceneUtils.html

@@ -32,7 +32,7 @@
 		parent -- The parent to attach the object from.
 		</div>
 		<div>
-		Attaches the object to the parent without the moving the object in the worldspace.
+		Attaches the object to the parent without the moving the object in the worldspace. Beware that to do this the matrixWorld needs to be updated, this can be done by calling the updateMatrixWorld method on the parent object.
 		</div>
 
 		<h3>[method:null detach]([page:Object3D child], [page:Object3D parent], [page:Object3D scene])</h3>
@@ -42,7 +42,7 @@
 		parent -- The parent to detach the object from.
 		</div>
 		<div>
-		Detaches the object from the parent and adds it back to the scene without moving in worldspace.
+		Detaches the object from the parent and adds it back to the scene without moving in worldspace. Beware that to do this the matrixWorld needs to be updated, this can be done by calling the updateMatrixWorld method on the parent object.
 		</div>
 
 		<h2>Source</h2>

+ 2 - 2
docs/api/extras/animation/Animation.html

@@ -48,7 +48,7 @@
 		
 		<h3>[property:number timeScale]</h3>
 		<div>
-		The timez 
+		How much to scale the speed of the animation. Defaults to 1.
 		</div>
 
 		<h3>[property:boolean isPlaying]</h3>
@@ -75,7 +75,7 @@
 		
 		<h3>[method:null play]([page:Number startTime])</h3>
 		<div>
-		Starts the animation from a moment startTime in the animation.
+		Starts the animation at the startTime (in seconds) of the animation.
 		</div>
 
 		<h3>[method:null stop]()</h3>

+ 0 - 105
docs/api/extras/animation/AnimationMorphTarget.html

@@ -1,105 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<script src="../../../list.js"></script>
-		<script src="../../../page.js"></script>
-		<link type="text/css" rel="stylesheet" href="../../../page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<div class="desc">todo</div>
-
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]([page:todo root], [page:todo data])</h3>
-		<div>
-		root -- todo <br />
-		data -- todo
-		</div>
-		<div>
-		todo
-		</div>
-
-		<h2>Properties</h2>
-		
-		<h3>[property:todo root]</h3>
-		<div>
-		todo
-		</div> 
-
-		<h3>[property:todo data]</h3>
-		<div>
-		todo
-		</div> 
-		
-		<h3>[property:todo hierarchy]</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[property:number currentTime]</h3>
-		<div>
-		todo
-		</div>
-		
-		<h3>[property:number timeScale]</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[property:boolean isPlaying]</h3>
-		<div>
-		todo
-		</div>
-		
-		<h3>[property:boolean isPaused]</h3>
-		<div>
-		todo
-		</div>
-		
-		<h3>[property:boolean loop]</h3>
-		<div>
-		todo
-		</div> 
-		
-		<h3>[property:number influence]</h3>
-		<div>
-		todo
-		</div> 
-
-
-		<h2>Methods</h2>
-
-		<h3>[method:todo play]()</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[method:todo pause]()</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[method:todo stop]()</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[method:todo update]([page:todo deltaTimeMS])</h3>
-		<div>
-		deltaTimeMS -- todo
-		</div>
-		<div>
-		todo
-		</div>
-
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>

+ 36 - 54
docs/api/extras/animation/KeyFrameAnimation.html

@@ -9,124 +9,106 @@
 	<body>
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">Runs a keyframe animation as imported from the [page:ColladaLoader].</div>
+
+		<h2>Examples</h2>
+		
+		<div>[example:webgl_loader_collada_keyframe loader / collada / keyframe ]</div>
 
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo root], [page:todo data], [page:todo JITCompile])</h3>
+		<h3>[name]([page:Object data])</h3>
 		<div>
-		root -- todo <br />
-		data -- todo <br />
-		JITCompile -- todo
+		data -- An individual animation object from a the [page:ColladaLoader], e.g. loadedColladaObj.animations[0] <br />
 		</div>
 		<div>
-		todo
+		Creates a new keyframe animation and initializes it to the first keyframes.
 		</div>
 		
 
 		<h2>Properties</h2>
 
-		<h3>[property:todo root]</h3>
+		<h3>[property:Object3D root]</h3>
 		<div>
-		todo
+		The root object of the animation
 		</div> 
 
-		<h3>[property:todo data]</h3>
+		<h3>[property:Object data]</h3>
 		<div>
-		todo
+		The data containing the animation
 		</div> 
 		
-		<h3>[property:todo hierarchy]</h3>
+		<h3>[property:Array hierarchy]</h3>
 		<div>
-		todo
+		The objects that are influenced by the animation.
 		</div>
 
 		<h3>[property:number currentTime]</h3>
 		<div>
-		todo
+		The time elapsed since the last start/restart of the animation.
 		</div>
 		
 		<h3>[property:number timeScale]</h3>
 		<div>
-		todo
+		How much to scale the speed of the animation. Defaults to 1.
 		</div>
 
 		<h3>[property:boolean isPlaying]</h3>
 		<div>
-		todo
+		Indicates whether the animation is playing. This shouldn't be adapted by user code.
 		</div>
 		
 		<h3>[property:boolean isPaused]</h3>
 		<div>
-		todo
+		Indicates whether the animation is paused. This shouldn't be adapted by user code.
 		</div>
 		
 		<h3>[property:boolean loop]</h3>
 		<div>
-		todo
+		Set to make the animation restart when the animation ends.
 		</div> 
 		
-		<h3>[property:boolean JITCompile]</h3>
-		<div>
-		todo
-		</div>
-
 		<h2>Methods</h2>
 		
-		<h3>[method:todo play]()</h3>
+		<h3>[method:null play]([page:Number startTime])</h3>
 		<div>
-		todo
+		Starts the animation at the startTime (in seconds) of the animation.
 		</div>
 
-		<h3>[method:todo pause]()</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[method:todo stop]()</h3>
-		<div>
-		todo
-		</div>
-
-		<h3>[method:todo update]([page:todo deltaTimeMS])</h3>
-		<div>
-		deltaTimeMS -- todo
-		</div>
+		<h3>[method:null stop]()</h3>
 		<div>
-		todo
+		Stops the animation.
 		</div>
 
-		<h3>[method:todo interpolateCatmullRom]([page:todo points], [page:todo scale])</h3>
+		<h3>[method:null update]([page:Float deltaTime])</h3>
 		<div>
-		points -- todo <br />
-		scale -- todo
+		deltaTime -- The change in time in seconds
 		</div>
 		<div>
-		todo
+		Updates the keyframe animation
 		</div>
 
-		<h3>[method:todo getNextKeyWith]([page:todo sid], [page:todo h], [page:todo key])</h3>
+		<h3>[method:Object getNextKeyWith]([page:String sid], [page:Integer h], [page:Integer key])</h3>
 		<div>
-		sid -- todo <br />
-		h -- todo <br />
-		key -- todo
+		sid -- The string id <br />
+		h -- The index in the heirarchy to use<br />
+		key -- The index of which key to start from
 		</div>
 		<div>
-		todo
+		Used internally to traverse the animation
 		</div>
 
-		<h3>[method:todo getPrevKeyWith]([page:todo sid], [page:todo h], [page:todo key])</h3>
+		<h3>[method:Object getPrevKeyWith]([page:String sid], [page:Integer h], [page:Integer key])</h3>
 		<div>
-		sid -- todo <br />
-		h -- todo <br />
-		key -- todo
+		sid -- The string id <br />
+		h -- The index in the heirarchy to use<br />
+		key -- The index of which key to start from
 		</div>
 		<div>
-		todo
+		Used internally to traverse the animation
 		</div>
-		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
 	</body>

+ 5 - 2
docs/api/extras/core/Curve.html

@@ -9,7 +9,10 @@
 	<body>
 		<h1>[name]</h1>
 
-		<div class="desc">An extensible curve object which contains methods for interpolation.</div>
+		<div class="desc">
+		An abstract base class for creating a curve object that contains methods for interpolation.
+		For an array of Curves see [page:CurvePath].
+		</div>
 		
 		<h2>Examples</h2>
 		
@@ -27,7 +30,7 @@
 		<h2>Methods</h2>
 
 		<h3>[method:Vector getPoint]( t )</h3>
-		<div>Returns a vector for point t of the curve where t is between 0 and 1</div>
+		<div>Returns a vector for point t of the curve where t is between 0 and 1. Must be implemented in the extending class.</div>
 
 		<h3>[method:Vector getPointAt]( u )</h3>
 		<div>Returns a vector for point at relative position in curve according to arc length</div>

+ 48 - 45
docs/api/extras/core/CurvePath.html

@@ -11,7 +11,10 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">
+		An abstract base class further extending [page:Curve]. A CurvePath is simply an array of connected curves,
+		but retains the api of a curve.
+		</div>
 
 
 		<h2>Constructor</h2>
@@ -19,7 +22,7 @@
 
 		<h3>[name]()</h3>
 		<div>
-		todo
+		The constructor take no parameters.
 		</div>
 
 
@@ -28,107 +31,107 @@
 
 		<h3>[property:array curves]</h3>
 		<div>
-		todo
+		The array of [page:Curve]s
 		</div> 
 
 		<h3>[property:array bends]</h3>
 		<div>
-		todo
+		An array of [page:Curve]s used to transform and bend the curve using [page:CurvePath.getWrapPoints].
 		</div> 
 
 		<h3>[property:boolean autoClose]</h3>
 		<div>
-		todo
+		Whether or not to automatically close the path.
 		</div> 
 
 		<h2>Methods</h2>
 
 
-		<h3>[method:todo getWrapPoints]([page:todo oldPts], [page:todo path])</h3>
+		<h3>[method:Array getWrapPoints]([page:Array vertices], [page:Curve curve])</h3>
 		<div>
-		oldPts -- todo <br />
-		path -- todo
+		vertices -- An array of [page:Vector2]s to modify<br />
+		curve -- An array of 2d [page:Curve]s
 		</div>
 		<div>
-		todo
+		Modifies the array of vertices by warping it by the curve. The curve parameter also accepts objects with similar
+		interfaces such as [page:CurvePath], [page:Path], [page:SplineCurve], etc. Returns the original vertices after
+		modification.
 		</div>
 
-		<h3>[method:todo createPointsGeometry]([page:todo divisions])</h3>
+		<h3>[method:null addWrapPath]([page:Curve curve])</h3>
 		<div>
-		divisions -- todo
+		curve -- A [page:Curve] or object with a similar interface.
 		</div>
 		<div>
-		todo
+		Pushes a curve onto the bends array.
 		</div>
 
-		<h3>[method:todo addWrapPath]([page:todo bendpath])</h3>
+		<h3>[method:Geometry createGeometry]([page:Vector3 points])</h3>
 		<div>
-		bendpath -- todo
+		points -- An array of [page:Vector3]s
 		</div>
 		<div>
-		todo
+		Creates a geometry from points
 		</div>
 
-		<h3>[method:todo createGeometry]([page:todo points])</h3>
+		<h3>[method:Geometry createPointsGeometry]([page:Integer divisions])</h3>
 		<div>
-		points -- todo
+		divisions -- How many segments to create with [page:Vector3]s. Defaults to 12.
 		</div>
 		<div>
-		todo
+		Creates a [page:Geometry] object comprised of [page:Vector3]s
 		</div>
-
-		<h3>[method:todo add]([page:todo curve])</h3>
+		
+		<h3>[method:Geometry createSpacedPointsGeometry]([page:Integer divisions])</h3>
 		<div>
-		curve -- todo
+		divisions -- How many segments to create with [page:Vector3]s. Defaults to 12.
 		</div>
 		<div>
-		todo
+		Creates a [page:Geometry] object comprised of [page:Vector3]s that are equidistant.
 		</div>
+		
 
-		<h3>[method:todo getTransformedSpacedPoints]([page:todo segments], [page:todo bends])</h3>
+		<h3>[method:null add]([page:Curve curve])</h3>
 		<div>
-		segments -- todo <br />
-		bends -- todo
+		curve -- The [page:Curve curve] to add
 		</div>
 		<div>
-		todo
+		Pushes a curve onto the curves array.
 		</div>
 
-		<h3>[method:todo createSpacedPointsGeometry]([page:todo divisions])</h3>
+		<h3>[method:null closePath]()</h3>
 		<div>
-		divisions -- todo
-		</div>
-		<div>
-		todo
+		Adds a curve to close the path.
 		</div>
 
-		<h3>[method:todo closePath]()</h3>
+		<h3>[method:Object getBoundingBox]()</h3>
 		<div>
-		todo
+		Returns an object with the keys minX, minY, maxX, maxY, (if 3d: maxZ, minZ)
 		</div>
 
-		<h3>[method:todo getBoundingBox]()</h3>
+		<h3>[method:Float getCurveLengths]()</h3>
 		<div>
-		todo
+		Adds together the length of the curves
 		</div>
 
-		<h3>[method:todo getCurveLengths]()</h3>
+		<h3>[method:Array getTransformedPoints]([page:Integer segments], [page:Array bends])</h3>
 		<div>
-		todo
+		segments -- The number of segments to create using the getPoints()<br />
+		bends -- (optional) An array of [page:Curve]s used to transform the points. Defaults to this.bends if blank.
 		</div>
-
-		<h3>[method:todo getTransformedPoints]([page:todo segments], [page:todo bends])</h3>
 		<div>
-		segments -- todo <br />
-		bends -- todo
+		Uses this CurvePath to generate a series of points transformed by the curves in the bends array. Returns an
+		array of [page:Vector2]s.
 		</div>
+		
+		<h3>[method:Array getTransformedSpacedPoints]([page:Integer segments], [page:Array bends])</h3>
 		<div>
-		todo
+		segments -- The number of segments to create using the getPoints()<br />
+		bends -- (optional) Defaults to this.bends if blank. An array of [page:Curve]s used to transform the points.
 		</div>
-
-		<h3>[method:todo checkConnection]()</h3>
 		<div>
-		todo
+		Uses this CurvePath to generate a series equidistant points that are then transformed by the curves in the bends.
+	    Returns an array of [page:Vector2]s.
 		</div>
 
 		<h2>Source</h2>

+ 15 - 12
docs/api/extras/core/Gyroscope.html

@@ -11,23 +11,26 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
-
+		<div class="desc">An Object3d that rotates independently of its parent objects, but keeps the position and scale.</div>
+
+		<code>
+		var root = new THREE.Group();
+		var gyro = new THREE.Gyroscope();
+		var cube = new THREE.Mesh( geometry, material );
+		
+		root.add( gyro )
+		gyro.add( cube )
+		
+		cube.position.x = 10;
+		root.rotation.y = Math.PI;
+		
+		// The cube will have changed its position but maintained its orientation
+		</code>
 
 		<h2>Constructor</h2>
 
 
 		<h3>[name]()</h3>
-		<div>
-		todo
-		</div>
-
-
-		<h2>Properties</h2>
-
-
-		<h2>Methods</h2>
-
 
 		<h2>Source</h2>
 

+ 64 - 21
docs/api/extras/core/Path.html

@@ -11,13 +11,16 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">A 2d path representation, comprising of points, lines, and cubes,  similar to the html5 2d canvas api. It extends CurvePath.</div>
+		<div class="desc">
+		A 2d path representation, comprising of points, lines, and cubes, similar to the html5 2d canvas api.
+		It extends [page:CurvePath].
+		</div>
 
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo points])</h3>
+		<h3>[name]([page:Array points])</h3>
 		<div>
 		points -- array of Vector2
 		</div>
@@ -36,40 +39,80 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:todo fromPoints]( vectors )</h3>
+		<h3>[method:null fromPoints]( [page:Array vector2s] )</h3>
 		<div>
 		Adds to the Path from the points. The first vector defines the offset. After that the lines get defined.
 		</div>
 
-		<h3>[method:todo moveTo]( x, y )</h3>
+		<h3>[method:null moveTo]( [page:Float x], [page:Float y] )</h3>
 		<div>This moves the offset to x and y</div>
 
-		<h3>[method:todo lineTo]( x, y )</h3>
+		<h3>[method:null lineTo]( [page:Float x], [page:Float y] )</h3>
 		<div>This creates a line from the offset to X and Y and updates the offset to X and Y.</div>
 
-		<h3>[method:todo quadraticCurveTo]( aCPx, aCPy, aX, aY )</h3>
-		<div>This creates a quadratic curve from the offset to aX and aY with aCPx and aCPy as control point and updates the offset to aX and aY.</div>
+		<h3>[method:null quadraticCurveTo]( [page:Float cpX], [page:Float cpY], [page:Float x], [page:Float y] )</h3>
+		<div>This creates a quadratic curve from the offset to x and y with cpX and cpY as control point and updates the offset to x and y.</div>
 
-		<h3>[method:todo bezierCurveTo]( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY )</h3>
-		<div>This creates a bezier curve from the offset to aX and aY with aCP1x, aCP1y and aCP1x, aCP1y  as control points and updates the offset to aX and aY.</div>
+		<h3>[method:null bezierCurveTo]( [page:Float cp1X], [page:Float cp1Y], [page:Float cp2X], [page:Float cp2Y], [page:Float x], [page:Float y] )</h3>
+		<div>This creates a bezier curve from the last offset to x and y with cp1X, cp1Y and cp1X, cp1Y as control points and updates the offset to x and y.</div>
 
-		<h3>.splineThru ( pts /*Array of Vector*/ ) </h3>
-		<div>todo</div>
+		<h3>[method:null splineThru] ( [page:Array points] ) </h3>
+		<div>points - An array of [page:Vector2]s</div>
+		<div>Connects a new [page:SplineCurve] onto the path.</div>
 
-		<h3>[method:todo arc]( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise )</h3>
-		<div>todo</div>
+		<h3>[method:null arc]( [page:Float x], [page:Float y], [page:Float radius], [page:Float startAngle], [page:Float endAngle], [page:Float clockwise] )</h3>
+		<div>
+		x, y -- The center of the arc offset from the last call
+		radius -- The radius of the arc
+		startAngle -- The start angle in radians
+		endAngle -- The end angle in radians
+		clockwise -- Sweep the arc clockwise. Defaults to false
+		</div>
+		<div>Draw an arc offset from the last call</div>
 
-		<h3>[method:todo absarc]( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise )</h3>
-		<div>todo</div>
+		<h3>[method:null absarc]( [page:Float x], [page:Float y], [page:Float radius], [page:Float startAngle], [page:Float endAngle], [page:Float clockwise] )</h3>
+		<div>
+		x, y -- The absolute center of the arc
+		radius -- The radius of the arc
+		startAngle -- The start angle in radians
+		endAngle -- The end angle in radians
+		clockwise -- Sweep the arc clockwise. Defaults to false
+		</div>
+		<div>Draw an arc absolutely positioned</div>
 
-		<h3>[method:todo ellipse]( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise )</h3>
-		<div>todo</div>
+		<h3>[method:null ellipse]( [page:Float x], [page:Float y], [page:Float xRadius], [page:Float yRadius], [page:Float startAngle], [page:Float endAngle], [page:Float clockwise] )</h3>
+		<div>
+		x, y -- The center of the ellipse offset from the last call
+		xRadius -- The radius of the ellipse in the x axis
+		yRadius -- The radius of the ellipse in the y axis
+		startAngle -- The start angle in radians
+		endAngle -- The end angle in radians
+		clockwise -- Sweep the ellipse clockwise. Defaults to false
+		</div>
+		<div>Draw an ellipse offset from the last call</div>
 
-		<h3>[method:todo absellipse]( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise )</h3>
-		<div>todo</div>
+		<h3>[method:null absellipse]( [page:Float x], [page:Float y], [page:Float xRadius], [page:Float yRadius], [page:Float startAngle], [page:Float endAngle], [page:Float clockwise] )</h3>
+		<div>
+		x, y -- The absolute center of the ellipse
+		xRadius -- The radius of the ellipse in the x axis
+		yRadius -- The radius of the ellipse in the y axis
+		startAngle -- The start angle in radians
+		endAngle -- The end angle in radians
+		clockwise -- Sweep the ellipse clockwise. Defaults to false
+		</div>
+		<div>Draw an ellipse absolutely positioned</div>
 
-		<h3>[method:todo toShapes]()</h3>
-		<div>todo</div>
+		<h3>[method:Array toShapes]( [page:Boolean isCCW], [page:Boolean noHoles] )</h3>
+		<div>
+		isCCW -- Changes how solids and holes are generated<br/>
+		noHoles -- Whether or not to generate holes
+		</div>
+		<div>
+		Converts the Path into an array of Shapes. By default solid shapes are defined clockwise (CW) and holes are defined counterclockwise (CCW). If isCCW is set to true,
+		then those are flipped. If the paramater noHoles is set to true then all paths are set as solid shapes and isCCW is ignored.
+		<br/>
+		
+		</div>
 
 
 		<h2>Source</h2>

+ 51 - 23
docs/api/extras/core/Shape.html

@@ -11,16 +11,43 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">Defines a 2d shape plane using paths.</div>
+		<div class="desc">
+		Defines an arbitrary 2d shape plane using paths with optional holes. It can be used with [page:ExtrudeGeometry],
+		[page:ShapeGeometry], to get points, or to get triangulated faces.
+		</div>
+		
+		<code>
+		var heartShape = new THREE.Shape();
+
+		heartShape.moveTo( 25, 25 );
+		heartShape.bezierCurveTo( 25, 25, 20, 0, 0, 0 );
+		heartShape.bezierCurveTo( 30, 0, 30, 35,30,35 );
+		heartShape.bezierCurveTo( 30, 55, 10, 77, 25, 95 );
+		heartShape.bezierCurveTo( 60, 77, 80, 55, 80, 35 );
+		heartShape.bezierCurveTo( 80, 35, 80, 0, 50, 0 );
+		heartShape.bezierCurveTo( 35, 0, 25, 25, 25, 25 );
+		
+		var extrudeSettings = { amount: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };
+		
+		var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
+
+		var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial() );
+		</code>
+		
+		<h2>Examples</h2>
+		
+		<div>
+		[example:webgl_geometry_shapes geometry / shapes ]<br/>
+		[example:webgl_geometry_extrude_shapes geometry / extrude / shapes ]<br/>
+		[example:webgl_geometry_extrude_shapes2 geometry / extrude / shapes2 ]<br/>
+		[example:webgl_particles_shapes particles / shapes ]
+		</div>
 
 
 		<h2>Constructor</h2>
 
 
 		<h3>[name]()</h3>
-		<div>
-		todo
-		</div>
 
 
 		<h2>Properties</h2>
@@ -28,66 +55,67 @@
 
 		<h3>[property:array holes]</h3>
 		<div>
-		todo
+		An array of [page:Path paths] that define the holes in the shape.
 		</div> 
 
 		<h2>Methods</h2>
 
 
-		<h3>[method:todo makeGeometry]([page:todo options])</h3>
+		<h3>[method:ShapeGeometry makeGeometry]([page:Object options])</h3>
 		<div>
-		options -- The options passed as the second argument to [page:ShapeGeometry ShapeGeometry]
+		options -- This is passed as the second argument to [page:ShapeGeometry ShapeGeometry]
 		</div>
 		<div>
 		Convenience method to return ShapeGeometry
 		</div>
 
-		<h3>[method:todo extractAllPoints]([page:todo divisions])</h3>
+		<h3>[method:Array extractAllPoints]([page:Integer divisions])</h3>
 		<div>
-		divisions -- todo
+		divisions -- The number of divisions to create on the shape
 		</div>
 		<div>
 		Get points of shape and holes (keypoints based on segments parameter)
 		</div>
 
-		<h3>[method:todo extrude]([page:todo options])</h3>
+		<h3>[method:ExtrudeGeometry extrude]([page:Object options])</h3>
 		<div>
-		options -- The options passed as the second argument to [page:ExtrudeGeometry ExtrudeGeometry]
+		options -- This is passed as the second argument to [page:ExtrudeGeometry ExtrudeGeometry]
 		</div>
 		<div>
 		Convenience method to return ExtrudeGeometry
 		</div>
 
-		<h3>[method:todo extractPoints]([page:todo divisions])</h3>
+		<h3>[method:Object extractPoints]([page:Integer divisions])</h3>
 		<div>
-		divisions -- todo
+		divisions -- The number of divisions to create on the shape
 		</div>
 		<div>
-		todo
+		Returns an object with a *shape* and *holes* property that each reference an array of [page:Vector2 Vector2s].
 		</div>
 
-		<h3>[method:todo extractAllSpacedPoints]([page:todo divisions])</h3>
+		<h3>[method:Object extractAllSpacedPoints]([page:Integer divisions])</h3>
 		<div>
-		divisions -- todo
+		divisions -- The number of divisions to create on the shape
 		</div>
 		<div>
-		todo
+		Returns an object with a *shape* and *holes* property that each reference an array of [page:Vector2 Vector2s]. The
+		points will all be equally spaced along the shape.
 		</div>
 
-		<h3>[method:todo getPointsHoles]([page:todo divisions])</h3>
+		<h3>[method:Array getPointsHoles]([page:Integer divisions])</h3>
 		<div>
-		divisions -- todo
+		divisions -- The number of divisions to create on the shape
 		</div>
 		<div>
-		Get points of holes
+		Get an array of [page Vector2 Vector2s] that represent the holes in the shape.
 		</div>
 
-		<h3>[method:todo getSpacedPointsHoles]([page:todo divisions])</h3>
+		<h3>[method:Array getSpacedPointsHoles]([page:Integer divisions])</h3>
 		<div>
-		divisions -- todo
+		divisions -- The number of divisions to create on the shape
 		</div>
 		<div>
-		Get points of holes (spaced by regular distance)
+		Get an array of equally spaced [page Vector2 Vector2s] that represent the holes in the shape.
 		</div>
 
 		<h2>Source</h2>

+ 12 - 3
docs/api/extras/geometries/BoxGeometry.html

@@ -13,6 +13,8 @@
 
 		<div class="desc">BoxGeometry is the quadrilateral primitive geometry class. It is typically used for creating a cube or irregular quadrilateral of the dimensions provided with the 'width', 'height', and 'depth' constructor arguments.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#BoxGeometry'></iframe>
+
 		<h2>Example</h2>
 
 		<code>var geometry = new THREE.BoxGeometry( 1, 1, 1 );
@@ -37,9 +39,16 @@
 
 		<h2>Properties</h2>
 
-		<div>
-		Each of the constructor parameters is accessible as a property of the same name. Any modification of these properties after instantiation does not change the geometry.
-		</div>
+		<h3>.parameters</h3>
+ 		<div>
+			<p>Using the above example code above as our basis:</p>
+			<code>
+				geometry.parameters; // outputs an object {width: 1, height: 1, depth: 1, widthSegments: undefined, heightSegments: undefined}
+				cube.geometry.parameters; // as above
+				cube.geometry.parameters.width; // === 1
+				cube.geometry.parameters.widthSegments // === undefined.
+			</code>
+ 		</div>
 
 		<h2>Source</h2>
 

+ 2 - 0
docs/api/extras/geometries/CircleGeometry.html

@@ -16,6 +16,8 @@
 		
 		<h2>Example</h2>
 
+		<iframe src='../../../scenes/geometry-browser.html#CircleGeometry'></iframe>
+
 		<code>var material = new THREE.MeshBasicMaterial({
 			color: 0x0000ff
 		});

+ 2 - 0
docs/api/extras/geometries/CylinderGeometry.html

@@ -15,6 +15,8 @@
 
 
 		<h2>Example</h2>
+
+		<iframe src='../../../scenes/geometry-browser.html#CylinderGeometry'></iframe>
 		
 		<code>var geometry = new THREE.CylinderGeometry( 5, 5, 20, 32 );
 		var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );

+ 2 - 0
docs/api/extras/geometries/DodecahedronGeometry.html

@@ -13,6 +13,8 @@
 
 		<div class="desc">A class for generating a dodecahedron geometries.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#DodecahedronGeometry'></iframe>
+
 
 		<h2>Constructor</h2>
 

+ 1 - 0
docs/api/extras/geometries/IcosahedronGeometry.html

@@ -12,6 +12,7 @@
 
 		<div class="desc">A class for generating an icosahedron geometry.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#IcosahedronGeometry'></iframe>
 
 		<h2>Constructor</h2>
 

+ 1 - 0
docs/api/extras/geometries/OctahedronGeometry.html

@@ -12,6 +12,7 @@
 
 		<div class="desc">A class for generating an octahedron geometry.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#OctahedronGeometry'></iframe>
 
 		<h2>Constructor</h2>
 

+ 2 - 0
docs/api/extras/geometries/PlaneGeometry.html

@@ -15,6 +15,8 @@
 
 
 		<h2>Example</h2>
+
+		<iframe src='../../../scenes/geometry-browser.html#PlaneGeometry'></iframe>
 		
 		<code>var geometry = new THREE.PlaneGeometry( 5, 20, 32 );
 		var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );

+ 3 - 1
docs/api/extras/geometries/RingGeometry.html

@@ -13,6 +13,8 @@
 
 		<div class="desc">A class for generating a two-dimensional ring geometry.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#RingGeometry'></iframe>
+
 		<h2>Example</h2>
 		
 		<code>var geometry = new THREE.RingGeometry( 1, 5, 32 );
@@ -29,7 +31,7 @@
 		innerRadius — Default is 0, but it doesn't work right when innerRadius is set to 0.<br />
 		outerRadius — Default is 50. <br />
 		thetaSegments — Number of segments.  A higher number means the ring will be more round.  Minimum is 3.  Default is 8. <br />
-		phiSegments — Minimum is 3.  Default is 8.<br />
+		phiSegments — Minimum is 1.  Default is 8.<br />
 		thetaStart — Starting angle. Default is 0. <br />
 		thetaLength — Central angle.  Default is Math.PI * 2.
 		</div>

+ 13 - 12
docs/api/extras/geometries/ShapeGeometry.html

@@ -16,19 +16,20 @@
 		<h2>Example</h2>
 		
 		
-		<code>var rectLength = 120, rectWidth = 40;
+		<code>
+var rectLength = 120, rectWidth = 40;
 		
-		var rectShape = new THREE.Shape();
-		rectShape.moveTo( 0,0 );
-		rectShape.lineTo( 0, rectWidth );
-		rectShape.lineTo( rectLength, rectWidth );
-		rectShape.lineTo( rectLength, 0 );
-		rectShape.lineTo( 0, 0 );
-		
-		var rectGeom = new THREE.ShapeGeometry( rectShape );
-		var rectMesh = new THREE.Mesh( rectGeom, new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ) ;		
-		
-		scene.add( rectMesh );
+var rectShape = new THREE.Shape();
+rectShape.moveTo( 0,0 );
+rectShape.lineTo( 0, rectWidth );
+rectShape.lineTo( rectLength, rectWidth );
+rectShape.lineTo( rectLength, 0 );
+rectShape.lineTo( 0, 0 );
+
+var rectGeom = new THREE.ShapeGeometry( rectShape );
+var rectMesh = new THREE.Mesh( rectGeom, new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ) ;		
+	
+scene.add( rectMesh );
 		</code>
 	
 		<h2>Constructor</h2>

+ 2 - 0
docs/api/extras/geometries/SphereGeometry.html

@@ -14,6 +14,8 @@
 		<div class="desc">A class for generating sphere geometries</div>
 
 		<h2>Example</h2>
+
+		<iframe src='../../../scenes/geometry-browser.html#SphereGeometry'></iframe>
 		
 		<code>var geometry = new THREE.SphereGeometry( 5, 32, 32 );
 		var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );

+ 2 - 0
docs/api/extras/geometries/TetrahedronGeometry.html

@@ -13,6 +13,8 @@
 
 		<div class="desc">A class for generating a tetrahedron geometries.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#TetrahedronGeometry'></iframe>
+
 
 		<h2>Constructor</h2>
 

+ 86 - 4
docs/api/extras/geometries/TextGeometry.html

@@ -11,8 +11,16 @@
 		
 		<h1>[name]</h1>
 
-		<div class="desc">This object creates an 3D object of text as a single object.</div>
+		<div class="desc">This object creates a 3D object of text as a single object.</div>
 
+		<h2>Example</h2>
+		
+		<iframe src='../../../scenes/geometry-browser.html#TextGeometry'></iframe>
+		
+		<div>
+		[example:webgl_geometry_text geometry / text ]<br/>
+		[example:webgl_geometry_text2 geometry / text2 ]
+		</div>
 
 		<h2>Constructor</h2>
 
@@ -20,11 +28,11 @@
 		<h3>[name]([page:String text], [page:Object parameters])</h3>
 		<div>
 		text — The text that needs to be shown. <br />
-		parameters — Object that can contain the following parameters.
+		parameters — Object that can contains the following parameters.
 		<ul>
 			<li>size — Float. Size of the text.</li>
 			<li>height — Float. Thickness to extrude text.  Default is 50.</li>
-			<li>curveSegments — Integer. Number of points on the curves.</li>
+			<li>curveSegments — Integer. Number of points on the curves. Default is 12.</li>
 			<li>font — String. Font name.</li>
 			<li>weight — String. Font weight (normal, bold).</li>
 			<li>style —  String. Font style (normal, italics).</li>
@@ -34,7 +42,81 @@
 		</ul>
 		</div>
 		
-
+		<h2>Available Fonts</h2>
+		
+		<div>
+		TextGeometry uses <a href='http://gero3.github.io/facetype.js/' target="_top">typeface.js</a> generated fonts.
+		Some existing fonts can be found located in <b>/examples/fonts</b> and must be included in the page.
+		</div>
+		<table>
+			<tr>
+				<th>Font</th>
+				<th>Weight</th>
+				<th>Style</th>
+				<th>File Path</th>
+			</tr>
+			<tr>
+				<td>helvetiker</td>
+				<td>normal</td>
+				<td>normal</td>
+				<td>/examples/fonts/helvetiker_regular.typeface.js</td>
+			</tr>
+			<tr>
+				<td>helvetiker</td>
+				<td>bold</td>
+				<td>normal</td>
+				<td>/examples/fonts/helvetiker_bold.typeface.js</td>
+			</tr>
+			<tr>
+				<td>optimer</td>
+				<td>normal</td>
+				<td>normal</td>
+				<td>/examples/fonts/optimer_regular.typeface.js</td>
+			</tr>
+			<tr>
+				<td>optimer</td>
+				<td>bold</td>
+				<td>normal</td>
+				<td>/examples/fonts/optimer_bold.typeface.js</td>
+			</tr>
+			<tr>
+				<td>gentilis</td>
+				<td>normal</td>
+				<td>normal</td>
+				<td>/examples/fonts/gentilis_regular.typeface.js</td>
+			</tr>
+			<tr>
+				<td>gentilis</td>
+				<td>bold</td>
+				<td>normal</td>
+				<td>/examples/fonts/gentilis_bold.typeface.js</td>
+			</tr>
+			<tr>
+				<td>driod sans</td>
+				<td>normal</td>
+				<td>normal</td>
+				<td>/examples/fonts/droid/droid_sans_regular.typeface.js</td>
+			</tr>
+			<tr>
+				<td>driod sans</td>
+				<td>bold</td>
+				<td>normal</td>
+				<td>/examples/fonts/droid/droid_sans_bold.typeface.js</td>
+			</tr>
+			<tr>
+				<td>droid serif</td>
+				<td>normal</td>
+				<td>normal</td>
+				<td>/examples/fonts/droid/droid_serif_regular.typeface.js</td>
+			</tr>
+			<tr>
+				<td>droid serif</td>
+				<td>bold</td>
+				<td>normal</td>
+				<td>/examples/fonts/droid/droid_serif_bold.typeface.js</td>
+			</tr>
+		</table>
+		
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 1 - 0
docs/api/extras/geometries/TorusGeometry.html

@@ -13,6 +13,7 @@
 
 		<div class="desc">A class for generating torus geometries</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#TorusGeometry'></iframe>
 
 		<h2>Example</h2>
 		

+ 2 - 0
docs/api/extras/geometries/TorusKnotGeometry.html

@@ -13,6 +13,8 @@
 
 		<div class="desc">Creates a torus knot, the particular shape of which is defined by a pair of coprime integers, p and q.  If p and q are not coprime, the result will be a torus link.</div>
 
+		<iframe src='../../../scenes/geometry-browser.html#TorusKnotGeometry'></iframe>
+
 
 		<h2>Example</h2>
 		

+ 7 - 15
docs/api/extras/helpers/PointLightHelper.html

@@ -11,7 +11,7 @@
 		
 		<h1>[name]</h1>
 
-		<div class="desc">This display a helper for a pointLight </div>
+		<div class="desc">This displays a helper object for a [page:PointLight] </div>
 
 
 		<h2>Example</h2>
@@ -29,34 +29,26 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]([page:todo light], [page:todo sphereSize])</h3>
+		<h3>[name]([page:PointLight light], [page:Float sphereSize])</h3>
 		<div>
-		light -- todo <br />
-		sphereSize -- todo
-		</div>
-		<div>
-		todo
+		light -- The [page:PointLight] to display. <br />
+		sphereSize -- The size of the sphere helper
 		</div>
 
 
 		<h2>Properties</h2>
 
-		<h3>[property:Mesh lightSphere]</h3>
-		<div>
-		todo
-		</div> 
-
 		<h3>[property:PointLight light]</h3>
 		<div>
-		todo
+		The [page:PointLight] that is being represented.
 		</div> 
 
 
 		<h2>Methods</h2>
 
-		<h3>[method:todo update]()</h3>
+		<h3>[method:null update]()</h3>
 		<div>
-		todo
+		Updates the light helper.
 		</div>
 
 		<h2>Source</h2>

+ 18 - 19
docs/api/extras/helpers/SpotLightHelper.html

@@ -11,46 +11,45 @@
 		
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">This displays a cylinder helper object for a [page:SpotLight] </div>
+
+
+		<h2>Example</h2>
+
+		<code>
+		var spotLight = new THREE.SpotLight( 0xffffff );
+		spotLight.position.set( 10, 10, 10 );
+		scene.add( spotLight );
+		
+		var spotLightHelper = new THREE.SpotLightHelper( spotLight );
+		scene.add( spotLightHelper );
+		</code>
 
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo light], [page:todo sphereSize])</h3>
-		<div>
-		light -- todo <br />
-		sphereSize -- todo
-		</div>
+		<h3>[name]([page:SpotLight light])</h3>
 		<div>
-		todo
+		light -- The [page:SpotLight] to display
 		</div>
 		
 
 		<h2>Properties</h2>
 
 
-		<h3>[property:Mesh lightSphere]</h3>
-		<div>
-		todo
-		</div> 
-
 		<h3>[property:SpotLight light]</h3>
 		<div>
-		todo
+		The [page:SpotLight]
 		</div> 
 
-		<h3>[property:Mesh lightCone]</h3>
-		<div>
-		todo
-		</div> 
 
 		<h2>Methods</h2>
 		
 
-		<h3>[method:todo update]()</h3>
+		<h3>[method:null update]()</h3>
 		<div>
-		todo
+		Updates the light helper.
 		</div>
 
 		<h2>Source</h2>

+ 73 - 56
docs/api/extras/objects/MorphBlendMesh.html

@@ -11,148 +11,165 @@
 		
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">A mesh that can blend together multiple animated morph targets.</div>
+
+		<h2>Example</h2>
+		<div>
+		[example:webgl_morphtargets_md2_control morphtargets / md2 / controll]
 
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo geometry], [page:todo material])</h3>
-		<div>
-		geometry -- todo <br />
-		material -- todo
-		</div>
+		<h3>[name]([page:Geometry geometry], [page:Material material])</h3>
 		<div>
-		todo
+		geometry — An instance of [page:Geometry].<br />
+		material — An instance of [page:Material] (optional).
 		</div>
-		
 
 		<h2>Properties</h2>
 
 
 		<h3>[property:object animationsMap]</h3>
 		<div>
-		todo
+		An object of named animations as added by [page:MorphBlendMesh.createAnimation].
 		</div> 
 
 		<h3>[property:array animationsList]</h3>
 		<div>
-		todo
+		The list of animations as added by [page:MorphBlendMesh.createAnimation].
 		</div> 
 
 		<h2>Methods</h2>
 
 
 
-		<h3>[method:todo setAnimationWeight]([page:todo name], [page:todo weight])</h3>
+		<h3>[method:null setAnimationWeight]([page:String name], [page:Float weight])</h3>
 		<div>
-		name -- todo <br />
-		weight -- todo
+		name -- The name of the animation<br />
+		weight -- Weight of the animation, typically 0-1
 		</div>
 		<div>
-		todo
+		Set the weight of how much this animation will apply to the overall morph. 0 is off, 1 is full weight.
 		</div>
 
-		<h3>[method:todo setAnimationFPS]([page:todo name], [page:todo fps])</h3>
+		<h3>[method:null setAnimationFPS]([page:String name], [page:Float fps])</h3>
 		<div>
-		name -- todo <br />
-		fps -- todo
+		name -- The name of the animation <br />
+		fps -- The number of frames (morphTargets) per second
 		</div>
 		<div>
-		todo
+		A frame is typically 1 morph target.
 		</div>
 
-		<h3>[method:todo createAnimation]([page:todo name], [page:todo start], [page:todo end], [page:todo fps])</h3>
+		<h3>[method:null createAnimation]([page:String name], [page:Integer start], [page:Integer end], [page:Float fps])</h3>
 		<div>
-		name -- todo <br />
-		start -- todo <br />
-		end -- todo <br />
-		fps -- todo
+		name -- The name of the animation <br />
+		start -- The starting frame (morph)<br />
+		end -- The ending frame (morph)<br />
+		fps -- How many frames (morphs) to play per second
 		</div>
 		<div>
-		todo
+		Creates an animation object that gets added to both the [page:MorphBlendMesh.animationsMap animationsMap] and
+		[page:MorphBlendMesh.animationsList animationsList].<br/><br/>
+		
+		Animation object:<br/><br/>
+		startFrame -- Starting frame<br/>
+		endFrame -- Ending frame<br/>
+		length -- The number of frames<br/>
+		fps -- The frames per second<br/>
+		duration -- The length of the animation in seconds<br/>
+		lastFrame -- The previous frame that was played<br/>
+		currentFrame -- The current frame<br/>
+		active -- Whether or not the animation is being played<br/>
+		time -- The time in seconds of the animation<br/>
+		direction -- Which way to play the animation<br/>
+		weight -- The weight of the animation<br/>
+		directionBackwards -- Is playing backwards<br/>
+		mirroredLoop -- Loop back and forth
 		</div>
 
-		<h3>[method:todo playAnimation]([page:todo name])</h3>
+		<h3>[method:null playAnimation]([page:String name])</h3>
 		<div>
-		name -- todo
+		name -- The name of the animation
 		</div>
 		<div>
-		todo
+		Sets the animation to active and animation time to 0
 		</div>
 
-		<h3>[method:todo update]([page:todo delta])</h3>
+		<h3>[method:null update]([page:Float delta])</h3>
 		<div>
-		delta -- todo
+		delta -- Time in seconds
 		</div>
 		<div>
-		todo
+		Updates and plays the animation
 		</div>
 
-		<h3>[method:todo autoCreateAnimations]([page:todo fps])</h3>
+		<h3>[method:null autoCreateAnimations]([page:Float fps])</h3>
 		<div>
-		fps -- todo
+		fps -- Frames per second
 		</div>
 		<div>
-		todo
+		Goes through the geometry's morphTargets and generates animations based on the morphTargets' names. Names
+		are of the form "walk_01", "walk_02", "walk_03", etc or "run001", "run002", "run003".
 		</div>
 
-		<h3>[method:todo setAnimationDuration]([page:todo name], [page:todo duration])</h3>
+		<h3>[method:null setAnimationDuration]([page:String name], [page:Float duration])</h3>
 		<div>
-		name -- todo <br />
-		duration -- todo
+		name -- The name of the animation <br />
+		duration -- How long in seconds to play the animation
 		</div>
 		<div>
-		todo
+		Updates the animation object with proper values to update the duration.
 		</div>
 
-		<h3>[method:todo setAnimationDirectionForward]([page:todo name])</h3>
+		<h3>[method:null setAnimationDirectionForward]([page:String name])</h3>
 		<div>
-		name -- todo
+		name -- The name of the animation
 		</div>
 		<div>
-		todo
+		Sets the animation to play forwards
 		</div>
 
-		<h3>[method:todo getAnimationDuration]([page:todo name])</h3>
+		<h3>[method:null setAnimationDirectionBackward]([page:String name])</h3>
 		<div>
-		name -- todo
+		name -- The name of the animation
 		</div>
 		<div>
-		todo
+		Sets the animation to play backwards
 		</div>
 
-		<h3>[method:todo getAnimationTime]([page:todo name])</h3>
+		<h3>[method:Float getAnimationDuration]([page:String name])</h3>
 		<div>
-		name -- todo
+		name -- The name of the animation
 		</div>
 		<div>
-		todo
+		Returns the duration in seconds of the animation. Returns -1 if it can't be found.
 		</div>
 
-		<h3>[method:todo setAnimationDirectionBackward]([page:todo name])</h3>
+		<h3>[method:Float getAnimationTime]([page:String name])</h3>
 		<div>
-		name -- todo
+		name -- The name of the animation
 		</div>
 		<div>
-		todo
+		Returns the current time position of the animation.
 		</div>
 
-		<h3>[method:todo setAnimationTime]([page:todo name], [page:todo time])</h3>
+		<h3>[method:null setAnimationTime]([page:String name], [page:Float time])</h3>
 		<div>
-		name -- todo <br />
-		time -- todo
+		name -- The name of the animation <br />
+		time -- The time in seconds
 		</div>
 		<div>
-		todo
+		Sets the current time position of the animation
 		</div>
 
-		<h3>[method:todo stopAnimation]([page:todo name])</h3>
+		<h3>[method:null stopAnimation]([page:String name])</h3>
 		<div>
-		name -- todo
+		name -- The name of the animation
 		</div>
 		<div>
-		todo
+		Stops the playback of the animation
 		</div>
 
 		<h2>Source</h2>

+ 20 - 2
docs/api/lights/AmbientLight.html

@@ -17,7 +17,13 @@
 
 
 		<h2>Example</h2>
-
+		<div>[example:canvas_camera_orthographic camera / orthographic ]</div>
+		<div>[example:canvas_interactive_voxelpainter interactive / voxelpainter ]</div>
+		<div>[example:canvas_materials materials ]</div>
+		<div>[example:canvas_sandbox sandbox ]</div>
+		<div>[example:webgl_animation_cloth animation / cloth ]</div>
+		<div>[example:webgl_animation_skinning_blending animation / skinning / blending ]</div>
+		
 <code>var light = new THREE.AmbientLight( 0x404040 ); // soft white light
 scene.add( light );</code>
 
@@ -32,7 +38,19 @@ scene.add( light );</code>
 		This creates an Ambientlight with a color.
 		</div>
 
-
+		<h2>Methods</h2>
+		
+		<h3>[method:AmbientLight clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of Ambientlight.
+		</div>
+		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return Ambientlight data in JSON format.
+		</div>
+		
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 5 - 2
docs/api/lights/AreaLight.html

@@ -15,6 +15,10 @@
 
 
 		<h2>Example</h2>
+		<h2>Example</h2>
+		
+		<div>[example:webgldeferred_arealights arealights ]</div>
+		
 		<code>areaLight1 = new THREE.AreaLight( 0xffffff, 1 );
 areaLight1.position.set( 0.0001, 10.0001, -18.5001 );
 areaLight1.rotation.set( -0.74719, 0.0001, 0.0001 );
@@ -80,8 +84,7 @@ scene.add( areaLight1 );</code>
 		<div>
 		Sets or gets the attention of the light in linear space. This increases the attenuation quadratic with the distance from the light.
 		</div> 
-
-
+		
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 27 - 4
docs/api/lights/DirectionalLight.html

@@ -17,7 +17,16 @@
 
 
 		<h2>Example</h2>
-
+		<div>[example:canvas_morphtargets_horse morphtargets / horse ]</div>
+		<div>[example:misc_controls_fly controls / fly ]</div>
+		<div>[example:misc_lights_test lights / test ]</div>
+		<div>[example:vr_cubes cubes ]</div>
+		<div>[example:webgl_effects_parallaxbarrier effects / parallaxbarrier ]</div>
+		<div>[example:webgl_effects_stereo effects / stereo ]</div>
+		<div>[example:webgl_geometry_extrude_splines geometry / extrude / splines ]</div>
+		<div>[example:webgl_materials_bumpmap materials / bumpmap ]</div>
+		<div>[example:webgl_materials_cubemap_balls_reflection materials / cubemap / balls / reflection ]</div>
+		
 		<code>// White directional light at half intensity shining from the top.
 
 var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
@@ -58,7 +67,13 @@ scene.add( directionalLight );</code>
 			If set to *true* light will only cast shadow but not contribute any lighting (as if *intensity* was 0 but cheaper to compute).<br />
 			Default — *false*.
 		</div>
-
+		
+		<h3>[property:Boolean castShadow]</h3>
+		<div>
+			If set to true light will cast dynamic shadows. Warning: This is expensive and requires tweaking to get shadows looking right.<br />
+			Default — *false*.
+		</div>
+		
 		<h3>[property:Float shadowCameraNear]</h3>
 		<div>
 			Orthographic shadow camera frustum parameter.<br />
@@ -199,8 +214,16 @@ scene.add( directionalLight );</code>
 		</div> 
 		
 		<h2>Methods</h2>
-
-
+		<h3>[method:DirectionalLight clone]()</h3>
+		<div>
+		It returns a clone of DirectionalLight.
+		</div>
+		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return DirectionalLight data in JSON format.
+		</div>
+		
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 23 - 1
docs/api/lights/HemisphereLight.html

@@ -12,6 +12,18 @@
 		<h1>[name]</h1>
 
 		<div class="desc">A light source positioned directly above the scene.</div>
+		
+		<h2>Example</h2>
+		
+		<div>[example:webgl_lights_hemisphere lights / hemisphere ]</div>
+		<div>[example:misc_controls_pointerlock controls / pointerlock ]</div>
+		<div>[example:webgl_decals decals ]</div>
+		<div>[example:webgl_loader_collada_kinematics loader / collada / kinematics ]</div>
+		<div>[example:webgl_materials_lightmap materials / lightmap ]</div>
+		<div>[example:webgl_shaders_ocean shaders / ocean ]</div>
+		
+<code>var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
+scene.add( light );</code>
 
 		<h2>Constructor</h2>
 
@@ -38,7 +50,17 @@
 		</div>
 
 		<h2>Methods</h2>
-
+		
+		<h3>[method:HemisphereLight clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of HemisphereLight.
+		</div>
+		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return HemisphereLight data in JSON format.
+		</div>
 
 		<h2>Source</h2>
 

+ 9 - 1
docs/api/lights/Light.html

@@ -32,7 +32,15 @@
 		<div>
 		Color of the light.<br />
 		</div>
-
+		
+		<h2>Methods</h2>
+		
+		<h3>[method:Light clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of Light.
+		</div>
+		
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 30 - 2
docs/api/lights/PointLight.html

@@ -18,6 +18,16 @@
 
 		<h2>Example</h2>
 
+		<div>[example:canvas_lights_pointlights lights / pointlights ]</div>
+		<div>[example:webgl_lights_pointlights lights / pointlights ]</div>
+		<div>[example:webgl_lights_pointlights2 lights / pointlights2 ]</div>
+		<div>[example:webgldeferred_animation animation ]</div>
+		<div>[example:webgldeferred_pointlights pointlights ]</div>
+		<div>[example:webgl_effects_anaglyph effects / anaglyph ]</div>
+		<div>[example:webgl_geometry_large_mesh geometry / large / mesh ]</div>
+		<div>[example:webgl_geometry_text geometry / text ]</div>
+		<div>[example:webgl_lensflares lensflares ]</div>
+		
 		<code>var light = new THREE.PointLight( 0xff0000, 1, 100 );
 light.position.set( 50, 50, 50 );
 scene.add( light );</code>
@@ -26,11 +36,12 @@ scene.add( light );</code>
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:Integer hex], [page:Float intensity], [page:Number distance])</h3>
+		<h3>[name]([page:Integer hex], [page:Float intensity], [page:Number distance], [page:Float decay])</h3>
 		<div>
 		[page:Integer hex] — Numeric value of the RGB component of the color. <br />
 		[page:Float intensity] — Numeric value of the light's strength/intensity. <br />
-		[page:Number distance] -- The distance of the light where the intensity is 0. When distance is 0, then the distance is endless. 
+		[page:Number distance] -- The distance of the light where the intensity is 0. When distance is 0, then the distance is endless. <br />
+		[page:Float decay] -- The amount the light dims along the distance of the light.
 		</div>
 		<div>
 		Creates a light at a specific position in the scene.  The light shines in all directions (roughly similar to a light bulb.)
@@ -52,7 +63,24 @@ scene.add( light );</code>
 			Default — *0.0*.
 		</div>
 		
+		<h3>[property:Float decay]</h3>
+		<div>
+			The amount the light dims along the distance of the light<br />
+			Default — *1*.
+		</div>
+		
 		<h2>Methods</h2>
+		
+		<h3>[method:PointLight clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of PointLight.
+		</div>
+		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return PointLight data in JSON format.
+		</div>
 
 
 		<h2>Source</h2>

+ 48 - 19
docs/api/lights/SpotLight.html

@@ -17,39 +17,52 @@
 
 
 		<h2>Example</h2>
+		<div>[example:webgl_interactive_cubes_gpu interactive / cubes / gpu ]</div>
+		<div>[example:webgl_interactive_draggablecubes interactive / draggablecubes ]</div>
+		<div>[example:webgl_materials_bumpmap_skin materials / bumpmap / skin ]</div>
+		<div>[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]</div>
+		<div>[example:webgl_morphtargets_md2 morphtargets / md2 ]</div>
+		<div>[example:webgl_shading_physical shading / physical ]</div>
+		
+		<code>
+		// white spotlight shining from the side, casting shadow
 
-		<code>// white spotlight shining from the side, casting shadow
+		var spotLight = new THREE.SpotLight( 0xffffff );
+		spotLight.position.set( 100, 1000, 100 );
 
-var spotLight = new THREE.SpotLight( 0xffffff );
-spotLight.position.set( 100, 1000, 100 );
+		spotLight.castShadow = true;
 
-spotLight.castShadow = true;
+		spotLight.shadowMapWidth = 1024;
+		spotLight.shadowMapHeight = 1024;
 
-spotLight.shadowMapWidth = 1024;
-spotLight.shadowMapHeight = 1024;
+		spotLight.shadowCameraNear = 500;
+		spotLight.shadowCameraFar = 4000;
+		spotLight.shadowCameraFov = 30;
 
-spotLight.shadowCameraNear = 500;
-spotLight.shadowCameraFar = 4000;
-spotLight.shadowCameraFov = 30;
+		scene.add( spotLight );
+		</code>
 
-scene.add( spotLight );</code>
+		<div>
+		[example:webgl_materials_bumpmap materials / bumpmap]<br/>
+		[example:webgl_shading_physical shading / physical]<br/>
+		[example:webgl_shadowmap shadowmap]<br/>
+		[example:webgl_shadowmap_viewer shadowmap / performance]<br/>
+		[example:webgl_shadowmap_viewer shadowmap / viewer]
+		</div>
 
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:Integer hex], [page:Float intensity], [page:todo distance], [page:todo angle], [page:todo exponent])</h3>
+		<h3>[name]([page:Integer hex], [page:Float intensity], [page:Float distance], [page:Radians angle], [page:Float exponent], [page:Float decay])</h3>
 		<div>
 		[page:Integer hex] — Numeric value of the RGB component of the color. <br />
 		[page:Float intensity] — Numeric value of the light's strength/intensity. <br />
-		distance -- Maximum distance from origin where light will shine whose intensity is attenuated linearly based on distance from origin. <br />
-		angle -- Maximum angle of light dispersion from its direction whose upper bound is Math.PI/2.  <br />
-		exponent -- todo
+		[page:Float distance] -- Maximum distance from origin where light will shine whose intensity is attenuated linearly based on distance from origin. <br />
+		[page:Radians angle] -- Maximum angle of light dispersion from its direction whose upper bound is Math.PI/2.<br />
+		[page:Float exponent] -- Rapidity of the falloff of light from its target direction.<br />
+		[page:Float decay] -- The amount the light dims along the distance of the light.
 		</div>
-		<div>
-		todo
-		</div>
-		
 
 		<h2>Properties</h2>
 
@@ -79,9 +92,16 @@ scene.add( spotLight );</code>
 	
 		<h3>[property:Float exponent]</h3>
 		<div>
-			Rapidity of the falloff of light from its target direction.<br />
+			Rapidity of the falloff of light from its target direction. A lower value spreads out the light, while a higher
+			focuses it towards the center.<br />
 			Default — *10.0*.
 		</div>
+		
+		<h3>[property:Float decay]</h3>
+		<div>
+			The amount the light dims along the distance of the light<br />
+			Default — *1*.
+		</div>
 	
 		<h3>[property:Boolean castShadow]</h3>
 		<div>
@@ -164,7 +184,16 @@ scene.add( spotLight );</code>
 		</div> 
 
 		<h2>Methods</h2>		
+		<h3>[method:SpotLight clone]()</h3>
+		<div>
+		<br />
+		It returns a clone of SpotLight.
+		</div>
 		
+		<h3>[method:JSON toJSON]()</h3>
+		<div>
+		Return SpotLight data in JSON format.
+		</div>
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 9 - 9
docs/api/materials/MeshBasicMaterial.html

@@ -24,8 +24,8 @@
 		<div>parameters is an object with one or more properties defining the material's appearance.</div>
 		<div>
 		color — geometry color in hexadecimal. Default is 0xffffff.<br />
-		map — Sets the texture map. Default is null <br />
-		lightMap — Set light map. Default is null.<br />
+		map — Set texture map. Default is null <br />
+		aoMap — Set ambient occlusion map. Default is null <br />
 		specularMap — Set specular map. Default is null.<br />
 		alphaMap — Set alpha map. Default is null.<br />
 		envMap — Set env map. Default is null.<br />
@@ -45,8 +45,13 @@
 		<h3>[property:Integer color]</h3>
 		<div>Sets the color of the geometry. Default is 0xffffff.</div>
 		
-		<h3>[property:Texture lightMap]</h3>
-		<div>Set light map. Default is null.</div>
+		<h3>[property:Texture map]</h3>
+		<div>
+		Set texture map. Default is  null.
+		</div>
+
+		<h3>[property:Texture aoMap]</h3>
+		<div>Set ambient occlusion map. Default is null.</div>
 
 		<h3>[property:Texture specularMap]</h3>
 		<div>Set specular map. Default is null.</div>
@@ -90,11 +95,6 @@
 		<h3>[property:Boolean morphTargets]</h3>
 		<div>Define whether the material uses morphTargets. Default is false.</div>	
 		
-		<h3>[property:Texture map]</h3>
-		<div>
-		Sets the texture map. Default is  null.
-		</div> 
-
 		<h3>[property:number combine]</h3>
 		<div>
 		How to combine the result of the surface's color with the environment map, if any.

+ 0 - 18
docs/api/materials/MeshLambertMaterial.html

@@ -25,7 +25,6 @@
 		<div>
 		color — Line color in hexadecimal. Default is 0xffffff.<br />
 		map — Sets the texture map. Default is null <br />
-		lightMap — Set light map. Default is null.<br />
 		specularMap — Set specular map. Default is null.<br />
 		alphaMap — Set alpha map. Default is null.<br />
 		envMap — Set env map. Default is null.<br />
@@ -53,27 +52,10 @@
 		<div>
 		Emissive (light) color of the material, essentially a solid color unaffected by other lighting. Default is black.<br />
 		</div>
-		
-		<h3>[property:boolean wrapAround]</h3>
-		<div>
-			Define whether the diffuse lighting wraps around the model or not. This option adds a little more (tintable) light
-			onto the side of the object in relation to a light.
-		</div>
-
-		<h3>[property:Vector3 wrapRGB]</h3>
-		<div>
-			Decide how much of the wrap around values get used if the wrapAround option is set. The x, y, z values correspond
-			to the r, g, b values respectively. The typical range is of each is from 0 to 1. For example setting all of the
-			vector values to 0.5 will add a moderate amount of light to the side of the model. Changing *b* to 1 will
-			tint the light on the side to be more blue. Defaults to *(1,1,1)*.
-		</div> 
 
 		<h3>[property:Texture map]</h3>
 		<div>Set color texture map. Default is null.</div>
 
-		<h3>[property:Texture lightMap]</h3>
-		<div>Set light map. Default is null.</div>
-
 		<h3>[property:Texture specularMap]</h3>
 		<div>Since this material does not have a specular component, the specular value affects only how much of the environment map affects the surface. Default is null.</div>
 

+ 5 - 15
docs/api/materials/MeshPhongMaterial.html

@@ -24,8 +24,9 @@
 		</div>
 		<div>
 		color — geometry color in hexadecimal. Default is 0xffffff.<br />
-		map — Sets the texture map. Default is null <br />
+		map — Set texture map. Default is null <br />
 		lightMap — Set light map. Default is null.<br />
+		aoMap — Set ao map. Default is null.<br />
 		specularMap — Set specular map. Default is null.<br />
 		alphaMap — Set alpha map. Default is null.<br />
 		envMap — Set env map. Default is null.<br />
@@ -74,26 +75,15 @@
 			underlying colors.
 		</div> 
 
-		<h3>[property:boolean wrapAround]</h3>
-		<div>
-			Define whether the diffuse lighting wraps around the model or not. This option adds a little more (tintable) light
-			onto the side of the object in relation to a light.
-		</div>
-
-		<h3>[property:Vector3 wrapRGB]</h3>
-		<div>
-			Decide how much of the wrap around values get used if the wrapAround option is set. The x, y, z values correspond
-			to the r, g, b values respectively. The typical range is of each is from 0 to 1. For example setting all of the
-			vector values to 0.5 will add a moderate amount of light to the side of the model. Changing *b* to 1 will
-			tint the light on the side to be more blue. Defaults to (1,1,1).
-		</div>
-
 		<h3>[property:Texture map]</h3>
 		<div>Set color texture map. Default is null.</div>
 
 		<h3>[property:Texture lightMap]</h3>
 		<div>Set light map. Default is null.</div>
 
+		<h3>[property:Texture aoMap]</h3>
+		<div>Set ambient occlusion map. Default is null.</div>
+
 		<h3>[property:Texture bumpMap]</h3>
 		<div>
 			The texture to create a bump map. The black and white values map to the perceived depth in relation to the lights.

+ 17 - 25
docs/api/objects/Bone.html

@@ -11,44 +11,36 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">A bone which is part of a SkinnedMesh.</div>
-
+		<div class="desc">
+		A bone which is part of a [page:Skeleton]. The skeleton in turn is used by the [page:SkinnedMesh].
+		Bones are almost identical to a blank [page:Object3D].
+		</div>
+		
+		<h3>Example</h3>
+		
+		<code>
+		var root = new THREE.Bone();
+		var child = new THREE.Bone();
+		
+		root.add( child );
+		child.position.y = 5;
+		</code>
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:SkinnedMesh belongsToSkin])</h3>
-		<div>
-		belongsToSkin -- An instance of [page:SkinnedMesh].
-		</div>
+		<h3>[name]([page:SkinnedMesh skin])</h3>
 		<div>
-		This creates a new instance of a bone from the skin.
+		skin — (optional) The [page:SkinnedMesh] to which the bone belongs.
 		</div>
 
 		<h2>Properties</h2>
 
-		<h3>[property:Matrix4 skinMatrix]</h3>
-		<div>
-		The matrix of the bone.
-		</div>
-
 		<h3>[property:SkinnedMesh skin]</h3>
 		<div>
-		The skin that contains this bone.
+		An optional reference to the [page:SkinnedMesh].
 		</div>
 
-		<h2>Methods</h2>
-
-
-
-		<h3>[method:todo update]([page:Matrix4 parentSkinMatrix], [page:boolean  forceUpdate])</h3>
-		<div>
-		parentSkinMatrix -- the matrix of the parent bone.<br />
-		forceUpdate -- force the update of the skinmatrix.
-		</div>
-		<div>
-		This updates the matrix of the bone and the matrices of its children.
-		</div>
 
 		<h2>Source</h2>
 

+ 32 - 17
docs/api/objects/LOD.html

@@ -11,16 +11,33 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">Level of Detail - Show meshes with more or less geometry based on distance.</div>
 
+		<h2>Example</h2>
+		
+		<div>
+		[example:webgl_lod LOD]
+		</div>
+		
+		<code>
+		var lod = new THREE.LOD();
+		
+		//Create 5 levels of spheres
+		for( var i = 0; i < 5; i++ ) {
+			
+			var geometry = new THREE.IcosahedronGeometry( 10, 5 - i )
+			
+			new THREE.Mesh( geometry, material );
+			
+			lod.addLevel( mesh, i * 50 );
+			
+		}
+		</code>
 
 		<h2>Constructor</h2>
 
 
 		<h3>[name]()</h3>
-		<div>
-		todo
-		</div>
 
 
 		<h2>Properties</h2>
@@ -29,36 +46,34 @@
 
 		<h3>[property:array objects]</h3>
 		<div>
-		todo
+		An array of [page:Object3D Object3Ds]
 		</div>
 
 		<h2>Methods</h2>
 
 
 
-		<h3>[method:todo addLevel]([page:todo object], [page:todo distance])</h3>
+		<h3>[method:null addLevel]([page:Object3D mesh], [page:Float distance])</h3>
 		<div>
-		object -- todo <br />
-		distance -- todo
+		mesh -- The Object3D to display <br />
+		distance -- The distance at which to display this level of detail
 		</div>
 		<div>
-		todo
+		Adds a mesh that will display at a certain distance and greater. Typically the further away the distance,
+		the lower the detail on the mesh.
 		</div>
 
-		<h3>[method:todo getObjectForDistance]([page:todo distance])</h3>
-		<div>
-		distance -- todo
-		</div>
+		<h3>[method:Object3D getObjectForDistance]([page:Float distance])</h3>
 		<div>
-		todo
+		Get a reference to the first [page:Object3D] (mesh) that is greater than supplied distance.
 		</div>
 
-		<h3>[method:todo update]([page:todo camera])</h3>
+		<h3>[method:null update]([page:Camera camera])</h3>
 		<div>
-		camera -- todo
+		camera -- The current camera
 		</div>
 		<div>
-		todo
+		Update the visiblility of the level of detail based on the distance from the camera.
 		</div>
 
 		<h2>Source</h2>

+ 22 - 15
docs/api/objects/LensFlare.html

@@ -11,51 +11,58 @@
 		
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">Creates a simulated lens flare that tracks a light</div>
 
+		<h2>Example</h2>
+		
+		<div>[example:webgl_lensflares lensflares]</div>
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo texture], [page:todo size], [page:todo distance], [page:todo blending], [page:todo color])</h3>
+		<h3>[name]([page:Texture texture], [page:Float size], [page:Float distance], [page:Materials blending], [page:Color color])</h3>
 		<div>
-		texture -- todo <br />
-		size -- todo <br />
-		distance -- todo <br />
-		blending -- todo <br />
-		color -- todo
+		texture -- THREE.Texture (optional) <br />
+		size -- size in pixels (-1 = use texture.width) <br />
+		distance -- (0-1) from light source (0 = at light source) <br />
+		blending -- [page:Materials Blending Mode] - Defaults to THREE.NormalBlending <br />
+		color -- The color of the lens flare
 		</div>
 		<div>
-		todo
+		Automatically adds a lens flare to the lensFlares array if a texture is set.
 		</div>
 
-
 		<h2>Properties</h2>
 
 
 
 		<h3>[property:array lensFlares]</h3>
 		<div>
-		todo
+		The array of flares as set by [page:LensFlare.add]
 		</div> 
 
 		<h3>[property:Vector3 positionScreen]</h3>
 		<div>
-		todo
+		The position of the lens flare on the screen.
 		</div> 
 
-		<h3>[property:todo customUpdateCallback]</h3>
+		<h3>[property:Function customUpdateCallback]</h3>
 		<div>
-		todo
+		A custom update callback
 		</div> 
 
 		<h2>Methods</h2>
 
 
+		<h3>[method:null add]([page:Texture texture], [page:Float size], [page:Float distance], [page:Materials blending], [page:Color color])</h3>
+		<div>
+		Adds a lens flare. See the constructor for details on the parameters.
+		</div>
+
 
-		<h3>[method:todo updateLensFlares]()</h3>
+		<h3>[method:null updateLensFlares]()</h3>
 		<div>
-		todo
+		Updates the lens flare based on the [page:LensFlare.positionScreen positionScreen] property.
 		</div>
 
 		<h2>Source</h2>

+ 2 - 13
docs/api/objects/Line.html

@@ -11,12 +11,11 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">A line or a series of lines.</div>
+		<div class="desc">A continuous line.</div>
 
 
 		<h2>Example</h2>
 
-
 		<code>var material = new THREE.LineBasicMaterial({
 			color: 0x0000ff
 		});
@@ -35,18 +34,15 @@
 
 		<h2>Constructor</h2>
 
-
-		<h3>[name]( [page:Geometry geometry], [page:Material material], [page:Integer type] )</h3>
+		<h3>[name]( [page:Geometry geometry], [page:Material material] )</h3>
 
 		<div>
 		geometry — Vertices representing the line segment(s).<br />
 		material — Material for the line. Default is [page:LineBasicMaterial LineBasicMaterial].<br />
-		type — Connection type between vertices. Default is THREE.LineStrip.
 		</div>
 
 		<div>If no material is supplied, a randomized line material will be created and assigned to the object.</div>
 
-		<div>Also, if no type is supplied, the default (THREE.LineStrip) will be used).</div>
 
 		<h2>Properties</h2>
 
@@ -60,13 +56,6 @@
 		Material for the line.
 		</div>
 
-		<h3>[property:Integer type]</h3>
-		<div>
-		Possible values: THREE.LineStrip or THREE.LinePieces. THREE.LineStrip will draw a series of segments connecting each point (first connected to the second, the second connected to the third, and so on and so forth); and THREE.LinePieces will draw a series of pairs of segments (first connected to the second, the third connected to the fourth, and so on and so forth).</div>
-
-		<div>
-		In OpenGL terms, LineStrip is the classic GL_LINE_STRIP and LinePieces is the equivalent to GL_LINES.
-		</div>
 
 		<h2>Methods</h2>
 

+ 53 - 0
docs/api/objects/LineSegments.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<script src="../../list.js"></script>
+		<script src="../../page.js"></script>
+		<link type="text/css" rel="stylesheet" href="../../page.css" />
+	</head>
+	<body>
+		[page:Line] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">A series of lines.</div>
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [page:Geometry geometry], [page:Material material] )</h3>
+
+		<div>
+		geometry — Vertices representing the line segment(s).<br />
+		material — Material for the line. Default is [page:LineBasicMaterial LineBasicMaterial].
+		</div>
+
+		<div>If no material is supplied, a randomized line material will be created and assigned to the object.</div>
+
+
+		<h2>Properties</h2>
+
+		<h3>[property:Geometry geometry]</h3>
+		<div>
+		Vertices representing the line segment(s).
+		</div>
+
+		<h3>[property:Material material]</h3>
+		<div>
+		Material for the line.
+		</div>
+
+
+		<h2>Methods</h2>
+
+		<h3>[method:Array raycast]([page:Raycaster raycaster], [page:Array intersects])</h3>
+		<div>
+		Get intersections between a casted ray and this Line. [page:Raycaster.intersectObject] will call this method.
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 33 - 1
docs/api/objects/Mesh.html

@@ -42,6 +42,34 @@
 		<h3>[property:Material material]</h3>
 
 		<div>An instance of [page:Material], defining the object's appearance. Default is a [page:MeshBasicMaterial] with wireframe mode enabled and randomised colour.</div>
+		
+		<h3>[property:Array morphTargetForcedOrder]</h3>
+		
+		<div>
+		An array of indices specifying the order that the morphs will be applied. Undefined by
+		default, but reset to a blank array by [page:Mesh.updateMorphTargets updateMorphTargets].
+		</div>
+		
+		<h3>[property:Array morphTargetInfluences]</h3>
+		
+		<div>
+		An array of weights typically from 0-1 that specify how much of the morph is applied.
+		Undefined by default, but reset to a blank array by [page:Mesh.updateMorphTargets updateMorphTargets].
+		</div>
+		
+		<h3>[property:Array morphTargetDictionary]</h3>
+		
+		<div>
+		A dictionary of morphTargets based on the morphTarget.name property.
+		Undefined by default, but rebuilt [page:Mesh.updateMorphTargets updateMorphTargets].
+		</div>
+
+		<h3>[property:Integer morphTargetBase]</h3>
+		
+		<div>
+		Specify the index of the morph that should be used as the base morph. Replaces the positions.
+		Undefined by default, but reset to -1 (non set) by [page:Mesh.updateMorphTargets updateMorphTargets].
+		</div>
 
 
 		<h2>Methods</h2>
@@ -57,7 +85,11 @@
 
 		<h3>[method:null updateMorphTargets]()</h3>
 		<div>
-		Updates the morphtargets to have no influence on the object.
+		Updates the morphtargets to have no influence on the object. Resets the
+		[page:Mesh.morphTargetForcedOrder morphTargetForcedOrder],
+		[page:Mesh.morphTargetInfluences morphTargetInfluences],
+		[page:Mesh.morphTargetDictionary morphTargetDictionary], and
+		[page:Mesh.morphTargetBase morphTargetBase] properties.
 		</div>
 
 		<h3>[method:Array raycast]([page:Raycaster raycaster], [page:Array intersects])</h3>

+ 83 - 55
docs/api/objects/MorphAnimMesh.html

@@ -11,130 +11,158 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">Play a sequence of morphs in a smooth animation sequence.</div>
+
+		<h2>Examples</h2>
+		
+		<div>
+		[example:webgl_lights_hemisphere lights / hemisphere ]<br/>
+		[example:webgl_morphtargets_md2 morphtargets / md2 ]<br/>
+		[example:webgl_loader_json_blender loader / json / blender ]
+		</div>
+		
+		<code>
+		var meshAnim;
+		
+		loader.load( "models/animated/flamingo.js", function( geometry ) {
+			
+			meshAnim = new THREE.MorphAnimMesh( geometry, material );
+			meshAnim.duration = 1000;
+			scene.add( meshAnim );
+			
+		}
+		
+		function update() {
+
+			var delta = clock.getDelta();
+			meshAnim.updateAnimation( 1000 * delta );
+			
+		}
+		</code>
 
 
 		<h2>Constructor</h2>
 
 
-		<h3>[name]([page:todo geometry], [page:todo material])</h3>
+		<h3>[name]([page:Geometry geometry], [page:Material material])</h3>
 		<div>
-		geometry -- todo <br />
-		material -- todo
+		geometry — An instance of [page:Geometry].<br />
+		material — An instance of [page:Material] (optional).
 		</div>
-		<div>
-		todo
-		</div>
-
 
 
 		<h2>Properties</h2>
 
 
-
-		<h3>[property:boolean directionBackwards]</h3>
+		<h3>[property:Boolean directionBackwards]</h3>
 		<div>
-		todo
+		Animation is playing backwards
 		</div>
 
-		<h3>[property:number direction]</h3>
+		<h3>[property:Float direction]</h3>
 		<div>
-		todo
+		1 if playing forward, -1 if playing backwards
 		</div>
 
-		<h3>[property:number endKeyframe]</h3>
+		<h3>[property:Integer startKeyframe]</h3>
 		<div>
-		todo
+		The first keyframe (morph) of the sequence
 		</div>
 
-		<h3>[property:boolean mirroredLoop]</h3>
+		<h3>[property:Integer endKeyframe]</h3>
 		<div>
-		todo
+		The last keyframe (morph) of the sequence
 		</div>
-
-		<h3>[property:number startKeyframe]</h3>
+		
+		<h3>[property:Boolean mirroredLoop]</h3>
 		<div>
-		todo
+		Loop animation back and forth
 		</div>
 
-		<h3>[property:number lastKeyframe]</h3>
+		<h3>[property:Integer lastKeyframe]</h3>
 		<div>
-		todo
+		The index of the last keyframe played.
 		</div>
-
-		<h3>[property:number length]</h3>
+		
+		<h3>[property:Integer currentKeyframe]</h3>
 		<div>
-		todo
+		The index of the current frame being played.
 		</div>
 
-		<h3>[property:number time]</h3>
+		<h3>[property:Integer length]</h3>
 		<div>
-		todo
+		The number of frames (morphs)
 		</div>
 
-		<h3>[property:number duration]</h3>
+		<h3>[property:Float time]</h3>
 		<div>
-		todo
+		The current playback position of the animation in milliseconds.
 		</div>
 
-		<h3>[property:number currentKeyframe]</h3>
+		<h3>[property:Float duration]</h3>
 		<div>
-		todo
+		The length of the animation in milliseconds.
 		</div>
 
 		<h2>Methods</h2>
 
 
 
-		<h3>[method:todo setDirectionForward]()</h3>
+		<h3>[method:null setDirectionForward]()</h3>
 		<div>
-		todo
+		Sets the animation to play forwards
 		</div>
 
-		<h3>[method:todo playAnimation]([page:todo label], [page:todo fps])</h3>
-		<div>
-		label -- todo <br />
-		fps -- todo
-		</div>
+		<h3>[method:null setDirectionBackward]()</h3>
 		<div>
-		todo
+		Set the animation to play backwards.
 		</div>
 
-		<h3>[method:todo setFrameRange]([page:todo start], [page:todo end])</h3>
+		<h3>[method:null playAnimation]([page:String label], [page:Float fps])</h3>
 		<div>
-		start -- todo <br />
-		end -- todo
+		label -- The label of the animation <br />
+		fps -- The frames per second (in seconds)
 		</div>
 		<div>
-		todo
+		Starts playing a labeled animation. Animations are defined by calling [page:MorphAnimMesh.parseAnimations parseAnimations].
 		</div>
 
-		<h3>[method:todo setDirectionBackward]()</h3>
+		<h3>[method:null setFrameRange]([page:Integer start], [page:Integer end])</h3>
+		<div>
+		start -- The starting frame (morph) index <br />
+		end -- The ending frame (morph) index
+		</div>
 		<div>
-		todo
+		Sets the range of morphs to be played
 		</div>
 
-		<h3>[method:todo parseAnimations]()</h3>
+		<h3>[method:null parseAnimations]()</h3>
 		<div>
-		todo
+		Goes through the geometry's morphTargets and generates animations based on the morphTargets' names (morph.name). Names
+		are of the form "walk_01", "walk_02", "walk_03", etc or "run001", "run002", "run003". The animation label is then
+		the part before the underscore and number, so "walk" or "run" in the examples.<br/><br/>
+		
+		This function changes the underlying geometry object by adding the animations property to it. This property
+		is set to an object of key/pair values, with the key being the label and the value being an object with
+		a start and end property that represents the frame index.
 		</div>
 
-		<h3>[method:todo updateAnimation]([page:todo delta])</h3>
+		<h3>[method:null updateAnimation]([page:Float delta])</h3>
 		<div>
-		delta -- todo
+		delta -- The change in time in milliseconds
 		</div>
 		<div>
-		todo
+		Update the morphTargetInfluences array on the MorphAnimMesh.
 		</div>
 
-		<h3>[method:todo setAnimationLabel]([page:todo label], [page:todo start], [page:todo end])</h3>
+		<h3>[method:null setAnimationLabel]([page:String label], [page:Integer start], [page:Integer end])</h3>
 		<div>
-		label -- todo <br />
-		start -- todo <br />
-		end -- todo
+		label -- The name of the animation <br />
+		start -- The starting frame index <br />
+		end -- The ending frame index
 		</div>
 		<div>
-		todo
+		Defines an animation. Sets the geometry.animations[label] to be an object with the start and end properties.
 		</div>
 
 		<h2>Source</h2>

+ 128 - 0
docs/api/objects/Skeleton.html

@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<script src="../../list.js"></script>
+		<script src="../../page.js"></script>
+		<link type="text/css" rel="stylesheet" href="../../page.css" />
+	</head>
+	<body>
+		
+		<h1>[name]</h1>
+
+		<div class="desc">
+		Use an array of [page:Bone bones] to create a skeleton that can be used by a [page:SkinnedMesh].
+		WebGL only.
+		</div>
+
+		<h2>Example</h2>
+
+		<code>
+		// Create a simple "arm"
+		
+		var bones = [];
+		
+		var shoulder = new THREE.Bone();
+		var elbow = new THREE.Bone();
+		var hand = new THREE.Bone();
+		
+		shoulder.add( elbow );
+		elbow.add( hand );
+
+		bones.push( shoulder );
+		bones.push( elbow );
+		bones.push( hand );
+		
+		shoulder.position.y = -5;
+		elbow.position.y = 0;
+		hand.position.y = 5;
+		
+		var armSkeleton = THREE.Skeleton( bones );
+		
+		// See THREE.SkinnedMesh for an example of usage with a mesh
+		</code>
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:Array bones], [page:Array boneInverses], [page:Boolean useVertexTexture]  )</h3>
+		<div>
+		bones — The array of [page:bone bones]<br/>
+		boneInverses — (optional) An array of [page:Matrix4 Matrix4s]<br/>
+		useVertexTexture — (optional) Whether or not to use a vertex texture in the shader.
+		</div>
+		<div>
+		The constructor automatically sets up all of the properties below.
+		</div>
+
+
+		<h2>Properties</h2>
+		
+		<h3>[property:Array bones]</h3>
+		<div>
+		The array of [page:bone bones]
+		</div>
+		
+		
+		<h3>[property:Boolean useVertexTexture]</h3>
+		<div>
+		Whether or not to use a vertex texture in the shader, set in the constructor. Not all devices
+		support floating point pixel textures. If this option is set then the bone matrices will be packed into
+		a texture and sent to the shader. This method allows a much larger set of bones to be used. Otherwise
+		the vertex shader will use uniforms, which do not allow for as many bones to be used. The exact
+		numbers vary between devices.
+		</div>
+		
+		
+		<h3>[property:Array boneInverses]</h3>
+		<div>
+		An array of [page:Matrix4 Matrix4s] that represent the inverse of the matrixWorld of the individual bones.
+		</div>
+		
+		
+		<h3>[property:Integer boneTextureWidth]</h3>
+		<div>
+		The width of the vertex data texture.
+		</div>
+		
+		
+		<h3>[property:Integer boneTextureHeight]</h3>
+		<div>
+		The height of the vertex data texture.
+		</div>
+		
+		
+		<h3>[property:Float32Array boneMatrices]</h3>
+		<div>
+		The array buffer holding the bone data when using a vertex texture.
+		</div>
+		
+		
+		<h3>[property:DataTexture boneTexture]</h3>
+		<div>
+		The [page:DataTexture] holding the bone data when using a vertex texture.
+		</div>
+		
+
+		<h2>Methods</h2>
+
+		<h3>[method:null calculateInverses]()</h3>
+		<div>Generates the boneInverses.</div>
+		
+		
+		<h3>[method:null pose]()</h3>
+		<div>Returns the skeleton to the base pose.</div>
+		
+		
+		<h3>[method:null update]()</h3>
+		<div>
+		Updates the [page:Float32Array boneMatrices] and [page:DataTexture boneTexture] after changing the bones.
+		This is called automatically by the [page:WebGLRenderer] if the skeleton is used with a [page:SkinnedMesh].
+		</div>
+		
+		
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 76 - 12
docs/api/objects/SkinnedMesh.html

@@ -11,27 +11,60 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">An 3d object that has bones data. These Bones can then be used to animate the vertices of the object.</div>
-
+		<div class="desc">A mesh that has a [page:Skeleton] with [page:Bone bones] that can then be used to animate the vertices of the geometry.</div>
+
+		<h2>Example</h2>
+		
+		<iframe src='../../scenes/bones-browser.html'></iframe>
+		
+		<code>
+		var geometry = new THREE.CylinderGeometry( 5, 5, 5, 5, 15, 5, 30 );
+		
+		//Create the skin indices and skin weights
+		for ( var i = 0; i < geometry.vertices.length; i ++ ) {
+			
+			// Imaginary functions to calculate the indices and weights
+			var skinIndex = calculateSkinIndex( geometry.vertices, i );
+			var skinWeight = calculateSkinWeight( geometry.vertices, i );
+			
+			// Ease between each bone
+			geometry.skinIndices.push( new THREE.Vector4( skinIndex, skinIndex + 1, 0, 0 ) );
+			geometry.skinWeights.push( new THREE.Vector4( 1 - skinWeight, skinWeight, 0, 0 ) );		
+			
+		}
+		
+		var mesh = THREE.SkinnedMesh( geometry, material );
+
+		// See example from THREE.Skeleton for the armSkeleton
+		var rootBone = armSkeleton.bones[ 0 ];
+		mesh.add( rootBone );
+		
+		// Bind the skeleton to the mesh
+		mesh.bind( armSkeleton );
+		
+		// Update the inverse matrices in the skeleton to reflect the newly bound skeleton
+		armSkeleton.calculateInverses();
+		
+		// Move the bones and manipulate the model
+		armSkeleton.bones[ 0 ].rotation.x = -0.1;
+		armSkeleton.bones[ 1 ].rotation.x = 0.2;
+		</code>
+		
 
 		<h2>Constructor</h2>
 
 
 		<h3>[name]([page:Geometry geometry], [page:Material material], [page:boolean useVertexTexture])</h3>
 		<div>
-                geometry — An instance of [page:Geometry].<br />
-                material — An instance of [page:Material] (optional).<br />
-		useVertexTexture -- Defines wether a vertex texture can be used (optional).
-		</div>
-		<div>
-		This Creates a new instance of skinnedMesh.
+        geometry — An instance of [page:Geometry]. [page:Geometry.skinIndices] and [page:Geometry.skinWeights] should be set.<br />
+        material — An instance of [page:Material] (optional).<br />
+		useVertexTexture -- Defines whether a vertex texture can be used (optional).
 		</div>
 
 
 		<h2>Properties</h2>
 
 
-
 		<h3>[property:array bones]</h3>
 		<div>
 		This contains the array of bones for this mesh. These should be set in the constructor.
@@ -52,19 +85,50 @@
 		This array of matrices contains the matrices of the bones. These get calculated in the constructor.
 		</div>
 
+		<h3>[property:string bindMode]</h3>
+		<div>
+		Either "attached" or "detached". "attached" uses the [page:SkinnedMesh.matrixWorld] property for the base transform
+		matrix of the bones. "detached" uses the [page:SkinnedMesh.bindMatrix].
+		</div>
+		
+		<h3>[property:Matrix4 bindMatrix]</h3>
+		<div>
+		The base matrix that is used for the bound bone transforms.
+		</div>
+
+		<h3>[property:Matrix4 inverseBindMatrix]</h3>
+		<div>
+		The inverse of the bindMatrix.
+		</div>
+
 		<h2>Methods</h2>
 
+		<h3>[method:null bind]([page:Skeleton skeleton], [page:Matrix4 bindMatrix])</h3>
+		<div>
+		skeleton — [page:Skeleton]<br/>
+		bindMatrix — [page:Matrix4] that represents the base transform of the skeleton
+		</div>
+		<div>
+		Bind a skeleton to the skinned mesh. The bindMatrix gets saved to .bindMatrix property and the .bindMatrixInverse
+		gets calculated.
+		</div>
+		
+		<h3>[method:null normalizeSkinWeights]()</h3>
+		<div>
+		Normalizes the [page:Geometry.skinWeights] vectors. Does not affect [page:BufferGeometry].
+		</div>
+
 		<h3>[method:null pose]()</h3>
 		<div>
-		This method sets the skinnedmesh in the rest pose.
+		This method sets the skinned mesh in the rest pose.
 		</div>
 
 		<h3>[method:Bone addBone]([page:Bone bone])</h3>
 		<div>
-		bone -- This is the bone that needs to be added. (optional)
+		bone  This is the bone that needs to be added. (optional)
 		</div>
 		<div>
-		This method adds the bone to the skinnedmesh when it is provided. It creates a new bone and adds that when no bone is given.
+		This method adds the bone to the skinned mesh when it is provided. It creates a new bone and adds that when no bone is given.
 		</div>
 
 		<h2>Source</h2>

+ 9 - 9
docs/api/renderers/webgl/WebGLProgram.html

@@ -109,12 +109,6 @@
 
 		<h2>Properties</h2>
 
-		<h3>[property:Object uniforms]</h3>
-		<div></div> 
-
-		<h3>[property:Object attributes]</h3>
-		<div></div> 
-
 		<h3>[property:String id]</h3>
 		<div></div> 
 
@@ -133,11 +127,17 @@
 		<h3>[property:WebGLShader fragmentShader]</h3>
 		<div></div> 
 
-
 		<h2>Methods</h2>
 		
-		<h3>none</h3>
-		<div></div>
+		<h3>[method:Object getUniforms]()</h3>
+		<div>
+		Returns a name-value mapping of all active uniform locations.
+		</div>
+
+		<h3>[method:Object getAttributes]()</h3>
+		<div>
+		Returns a name-value mapping of all active vertex attribute locations.
+		</div>
 
 		<h2>Source</h2>
 

+ 23 - 21
docs/api/renderers/webgl/WebGLShader.html

@@ -9,36 +9,38 @@
 	<body>
 		<h1>[name]</h1>
 
-		<div class="desc">todo</div>
+		<div class="desc">A lower level function to compile either a vertex or fragment shader.</div>
 
 		<h2>Example</h2>
 
-		<code>todo</code>
-
-		<h2>Constructor</h2>
-
-
-		<h3>todo</h3>
-		<div></div>
+		<code>
+		var gl = renderer.context;
+		
+		var glVertexShader = new THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexSourceCode );
+		var glFragmentShader = new THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentSourceCode );
+		
+		var program = gl.createProgram();
+		
+		gl.attachShader( program, glVertexShader );
+		gl.attachShader( program, glFragmentShader );
+		
+		gl.linkProgram( program );
+		</code>
 
+		<h2>Function</h2>
 
-		<h2>Properties</h2>
+		<h3>[page:WebGLShader objects]([page:WebGLContext gl], [page:WebGLEnum type], [page:String source])</h3>
 
-		<h3>todo</h3>
 		<div>
-		todo
-		</div> 
-
-
-		<h2>Methods</h2>
-		
-
-		<h3>todo</h3>
-		<div>todo</div>
+		gl -- The current WebGL context
+		type -- The WebGL type, either gl.VERTEX_SHADER or gl.FRAGMENT_SHADER
+		source -- The source code for the shader
+		</div>
 		<div>
-		todo
+		This will compile an individual shader, but won't link it to be a complete [page:WebGLProgram]. Note: this
+		is a function so the new operator should not be used.
 		</div>
-		
+
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 58 - 0
docs/api/textures/CubeTexture.html

@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<script src="../../list.js"></script>
+		<script src="../../page.js"></script>
+		<link type="text/css" rel="stylesheet" href="../../page.css" />
+	</head>
+	<body>
+		[page:Texture] &rarr;
+		
+		<h1>[name]</h1>
+
+		<div class="desc">Creates a cube texture made up of six images.</div>
+
+		<h2>Example</h2>
+
+		<code>
+		var path = "textures/cube/pisa/";
+		var format = '.png';
+		var urls = [
+			path + 'px' + format, path + 'nx' + format,
+			path + 'py' + format, path + 'ny' + format,
+			path + 'pz' + format, path + 'nz' + format
+		];
+
+		var textureCube = THREE.ImageUtils.loadTextureCube( urls );
+		var material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } );
+		</code>
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )</h3>
+		
+		<div>
+		CubeTexture is almost equivalent in functionality and usage to [page:Texture]. The only differences are that the
+		images are an array of 6 images as opposed to a single image, and the mapping options are
+		[page:Textures THREE.CubeReflectionMapping] (default) or [page:Textures THREE.CubeRefractionMapping]<br />
+		<br />
+		Generally [page:ImageUtils.loadTextureCube] is used instead of using CubeTexture directly.
+		</div>
+
+
+		<h2>Properties</h2>
+
+		<h3>See [page:Texture]</h3>
+
+		<h2>Methods</h2>
+		
+
+		<h3>See [page:Texture]</h3>
+		
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 1 - 1
docs/index.html

@@ -406,7 +406,7 @@
 			if ( window.location.hash.length > 0 ) goToHash();
 
 			console.log([
-				'   __     __',
+				'    __     __',
 				' __/ __\\  / __\\__   ____   _____   _____',
 				'/ __/  /\\/ /  /___\\/ ____\\/ _____\\/ _____\\',
 				'\\/_   __/ /   _   / /  __/ / __  / / __  /_   __   _____',

+ 4 - 2
docs/list.js

@@ -110,11 +110,13 @@ var list = {
 			[ "Bone", "api/objects/Bone" ],
 			[ "LensFlare", "api/objects/LensFlare" ],
 			[ "Line", "api/objects/Line" ],
+			[ "LineSegments", "api/objects/LineSegments" ],
 			[ "LOD", "api/objects/LOD" ],
 			[ "Mesh", "api/objects/Mesh" ],
 			[ "MorphAnimMesh", "api/objects/MorphAnimMesh" ],
 			[ "PointCloud", "api/objects/PointCloud" ],
 			[ "SkinnedMesh", "api/objects/SkinnedMesh" ],
+			[ "Skeleton", "api/objects/Skeleton" ],
 			[ "Sprite", "api/objects/Sprite" ]
 		],
 
@@ -150,6 +152,7 @@ var list = {
 		],
 
 		"Textures": [
+			[ "CubeTexture", "api/textures/CubeTexture" ],
 			[ "CompressedTexture", "api/textures/CompressedTexture" ],
 			[ "DataTexture", "api/textures/DataTexture" ],
 			[ "Texture", "api/textures/Texture" ]
@@ -165,8 +168,7 @@ var list = {
 		"Extras / Animation": [
 			[ "Animation", "api/extras/animation/Animation" ],
 			[ "AnimationHandler", "api/extras/animation/AnimationHandler" ],
-			[ "KeyFrameAnimation", "api/extras/animation/KeyFrameAnimation" ],
-			[ "AnimationMorphTarget", "api/extras/animation/AnimationMorphTarget" ]
+			[ "KeyFrameAnimation", "api/extras/animation/KeyFrameAnimation" ]
 		],
 
 		"Extras / Core": [

+ 2 - 0
docs/manual/introduction/Creating-a-scene.html

@@ -21,8 +21,10 @@
 		<div>Before you can use Three.js, you need somewhere to display it. Save the following HTML to a file on your computer, along with a copy of <a href="http://threejs.org/build/three.min.js">three.min.js</a> in the js/ directory, and open it in your browser.</div>
 
 		<code>
+		&lt;!DOCTYPE html&gt;
 		&lt;html&gt;
 			&lt;head&gt;
+				&lt;meta charset=utf-8&gt;
 				&lt;title&gt;My first Three.js app&lt;/title&gt;
 				&lt;style&gt;
 					body { margin: 0; }

+ 10 - 9
docs/manual/introduction/Matrix-transformations.html

@@ -18,19 +18,20 @@
 		There are two ways to update an object's transformation:
 		<ol>
 			<li>
-				Modify the object's *position*, *quaternion*, and *scale* properties, then ask Three.js to recompute the object's matrix from these properties:
+				Modify the object's *position*, *quaternion*, and *scale* properties, and let Three.js recompute
+				the object's matrix from these properties:
 				<code>
-				object.position = start_position;
-				object.quaternion = quaternion;
-				object.updateMatrix();
+				object.position.copy(start_position);
+				object.quaternion.copy(quaternion);
 				</code>
-				Calling the *updateMatrix* method forces the object's matrix to be recomputed from *position*, *quaternion*, and *scale*. You can also set 
+				By default, the *matrixAutoUpdate* property is set true, and the matrix will be automatically recalculated.
+				If the object is static, or you wish to manually control when recalculation occurs, better performance can be obtained by setting the property false:
 				<code>
-				object.matrixAutoUpdate = true;
+				object.matrixAutoUpdate = false;
 				</code>
-				in lieu of calling *updateMatrix*. This will force the matrix to be recomputed every frame; for static objects, you should therefore set 
+				And after changing any properties, manually update the matrix:
 				<code>
-				object.matrixAutoUpdate = false;
+				object.updateMatrix();
 				</code>
 			</li>
 			<li>
@@ -61,4 +62,4 @@
 		</p>
 
 	</body>
-</html>
+</html>

+ 1 - 1
docs/page.js

@@ -35,7 +35,7 @@ var onDocumentLoad = function ( event ) {
 	text = text.replace(/\*([\w|\d|\"|\-|\(][\w|\d|\ |\-|\/|\+|\-|\(|\)|\=|\,|\.\"]*[\w|\d|\"|\)]|\w)\*/gi, "<strong>$1</strong>" ); // *
 
 	text = text.replace(/\[example:([\w\_]+)\]/gi, "[example:$1 $1]" ); // [example:name] to [example:name title]
-	text = text.replace(/\[example:([\w\_]+) ([\w\:\/\.\-\_ \s]+)\]/gi, "<a href=\"../../../examples/#$1\"  target=\"_blank\">$2</a>" ); // [example:name title]
+	text = text.replace(/\[example:([\w\_]+) ([\w\:\/\.\-\_ \s]+)\]/gi, "<a href=\"../../../../examples/#$1\"  target=\"_blank\">$2</a>" ); // [example:name title]
 
 
 	document.body.innerHTML = text;

+ 272 - 0
docs/scenes/bones-browser.html

@@ -0,0 +1,272 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<title>Three.js Bones Browser</title>
+		<style>
+			@font-face {
+				font-family: 'inconsolata';
+				src: url('../files/inconsolata.woff') format('woff');
+				font-weight: normal;
+				font-style: normal;
+			}
+			
+			body {
+				margin:0;
+				font-family: 'inconsolata';
+				font-size: 15px;
+				line-height: 18px;
+				overflow: hidden;
+			}
+			
+			canvas { width: 100%; height: 100% }
+			
+			#newWindow {
+				display: block;
+				position: absolute;
+				bottom: 0.3em;
+				left: 0.5em;
+				color: #fff;
+			}
+		</style>
+	</head>
+	<body>
+		
+		<a id='newWindow' href='./bones-browser.html' target='_blank'>Open in New Window</a>
+		
+		<script src="../../build/three.min.js"></script>
+		<script src='../../examples/js/libs/dat.gui.min.js'></script>
+		<script src="../../examples/js/controls/OrbitControls.js"></script>
+		
+		<script>
+
+			var gui, scene, camera, renderer, orbit, ambientLight, lights, mesh, bones, skeletonHelper;
+
+			var state = {
+
+				animateBones : false
+
+			};
+
+			function initScene () {
+
+				gui = new dat.GUI();
+				scene = new THREE.Scene();
+				camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 200 );
+				camera.position.z = 30;
+				camera.position.y = 30;
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				orbit = new THREE.OrbitControls( camera, renderer.domElement );
+				orbit.noZoom = true;
+
+				ambientLight = new THREE.AmbientLight( 0x000000 );
+				scene.add( ambientLight );
+
+				lights = [];
+				lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
+				lights[ 1 ] = new THREE.PointLight( 0xffffff, 1, 0 );
+				lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );
+
+				lights[ 0 ].position.set( 0, 200, 0 );
+				lights[ 1 ].position.set( 100, 200, 100 );
+				lights[ 2 ].position.set( -100, -200, -100 );
+
+				scene.add( lights[ 0 ] );
+				scene.add( lights[ 1 ] );
+				scene.add( lights[ 2 ] );
+
+				window.addEventListener( 'resize', function () {
+
+					camera.aspect = window.innerWidth / window.innerHeight;
+					camera.updateProjectionMatrix();
+
+					renderer.setSize( window.innerWidth, window.innerHeight );
+
+				}, false );
+
+				initBones();
+				setupDatGui();
+
+			}
+
+			function createGeometry ( sizing ) {
+
+				var geometry = new THREE.CylinderGeometry(
+					5,                       // radiusTop
+					5,                       // radiusBottom
+					sizing.height,           // height
+					8,                       // radiusSegments
+					sizing.segmentCount * 3, // heightSegments
+					true                     // openEnded
+				);
+
+				for ( var i = 0; i < geometry.vertices.length; i ++ ) {
+
+					var vertex = geometry.vertices[ i ];
+					var y = ( vertex.y + sizing.halfHeight );
+
+					var skinIndex = Math.floor( y / sizing.segmentHeight );
+					var skinWeight = ( y % sizing.segmentHeight ) / sizing.segmentHeight;
+
+					geometry.skinIndices.push( new THREE.Vector4( skinIndex, skinIndex + 1, 0, 0 ) );
+					geometry.skinWeights.push( new THREE.Vector4( 1 - skinWeight, skinWeight, 0, 0 ) );
+
+				}
+
+				return geometry;
+
+			};
+
+			function createBones ( sizing ) {
+
+				bones = [];
+
+				var prevBone = new THREE.Bone();
+				bones.push( prevBone );
+				prevBone.position.y = -sizing.halfHeight;
+
+				for ( var i = 0; i < sizing.segmentCount; i ++ ) {
+
+					var bone = new THREE.Bone();
+					bone.position.y = sizing.segmentHeight;
+					bones.push( bone );
+					prevBone.add( bone );
+					prevBone = bone;
+
+				}
+
+				return bones;
+
+			};
+
+			function createMesh ( geometry, bones ) {
+
+				var material = new THREE.MeshPhongMaterial( {
+					skinning : true,
+					color: 0x156289,
+					emissive: 0x072534,
+					side: THREE.DoubleSide,
+					shading: THREE.FlatShading
+				} );
+
+				var mesh = new THREE.SkinnedMesh( geometry,	material );
+				var skeleton = new THREE.Skeleton( bones );
+	
+				mesh.add( bones[ 0 ] );
+	
+				mesh.bind( skeleton );
+				skeleton.calculateInverses();
+
+				skeletonHelper = new THREE.SkeletonHelper( mesh );
+				skeletonHelper.material.linewidth = 2;
+				scene.add( skeletonHelper );
+
+				return mesh;
+	
+			};
+
+			function setupDatGui () {
+	
+				var folder = gui.addFolder( "General Options" );
+	
+				folder.add( state, "animateBones" );
+				folder.__controllers[ 0 ].name( "Animate Bones" );
+
+				folder.add( mesh, "pose" );
+				folder.__controllers[ 1 ].name( ".pose()" );
+	
+				var bones = mesh.skeleton.bones;
+	
+				for ( var i = 0; i < bones.length; i ++ ) {
+		
+					var bone = bones[ i ];
+		
+					folder = gui.addFolder( "Bone " + i );
+	
+					folder.add( bone.position, 'x', -10 + bone.position.x, 10 + bone.position.x );
+					folder.add( bone.position, 'y', -10 + bone.position.y, 10 + bone.position.y );
+					folder.add( bone.position, 'z', -10 + bone.position.z, 10 + bone.position.z );
+
+					folder.add( bone.rotation, 'x', -Math.PI * 0.5, Math.PI * 0.5 );
+					folder.add( bone.rotation, 'y', -Math.PI * 0.5, Math.PI * 0.5 );
+					folder.add( bone.rotation, 'z', -Math.PI * 0.5, Math.PI * 0.5 );
+		
+					folder.add( bone.scale, 'x', 0, 2 );
+					folder.add( bone.scale, 'y', 0, 2 );
+					folder.add( bone.scale, 'z', 0, 2 );
+
+					folder.__controllers[ 0 ].name( "position.x" );
+					folder.__controllers[ 1 ].name( "position.y" );
+					folder.__controllers[ 2 ].name( "position.z" );
+		
+					folder.__controllers[ 3 ].name( "rotation.x" );
+					folder.__controllers[ 4 ].name( "rotation.y" );
+					folder.__controllers[ 5 ].name( "rotation.z" );
+		
+					folder.__controllers[ 6 ].name( "scale.x" );
+					folder.__controllers[ 7 ].name( "scale.y" );
+					folder.__controllers[ 8 ].name( "scale.z" );
+		
+				}
+				
+			}
+
+			function initBones () {
+	
+				var segmentHeight = 8;
+				var segmentCount = 4;
+				var height = segmentHeight * segmentCount;
+				var halfHeight = height * 0.5;
+
+				var sizing = {
+					segmentHeight : segmentHeight,
+					segmentCount : segmentCount,
+					height : height,
+					halfHeight : halfHeight
+				};
+
+				var geometry = createGeometry( sizing );
+				var bones = createBones( sizing );
+				mesh = createMesh( geometry, bones );
+
+				mesh.scale.multiplyScalar( 1 );
+				scene.add( mesh );
+
+			};
+
+			function render () {
+	
+				requestAnimationFrame( render );
+
+				var time = Date.now() * 0.001;
+	
+				var bone = mesh;
+
+	
+				//Wiggle the bones
+				if ( state.animateBones ) {
+		
+					for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {
+		
+						mesh.skeleton.bones[ i ].rotation.z = Math.sin( time ) * 2 / mesh.skeleton.bones.length;
+		
+					}
+		
+				}
+
+				skeletonHelper.update();
+	
+				renderer.render( scene, camera );
+	
+			};
+
+			initScene();
+			render();
+			
+		</script>
+	</body>
+</html>

+ 144 - 0
docs/scenes/geometry-browser.html

@@ -0,0 +1,144 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<title>Three.js Geometry Browser</title>
+		<style>
+			@font-face {
+				font-family: 'inconsolata';
+				src: url('../files/inconsolata.woff') format('woff');
+				font-weight: normal;
+				font-style: normal;
+			}
+			
+			body {
+				margin:0;
+				font-family: 'inconsolata';
+				font-size: 15px;
+				line-height: 18px;
+				overflow: hidden;
+			}
+			
+			canvas { width: 100%; height: 100% }
+			
+			#newWindow {
+				display: block;
+				position: absolute;
+				bottom: 0.3em;
+				left: 0.5em;
+				color: #fff;
+			}
+		</style>
+	</head>
+	<body>
+		
+		<a id='newWindow' href='./geometry-browser.html' target='_blank'>Open in New Window</a>
+		
+		<script src="../../build/three.min.js"></script>
+		<script src='../../examples/js/libs/dat.gui.min.js'></script>
+		<script src="../../examples/js/controls/OrbitControls.js"></script>
+		<script src="../../examples/fonts/gentilis_bold.typeface.js"></script>
+		<script src="../../examples/fonts/gentilis_regular.typeface.js"></script>
+		<script src="../../examples/fonts/optimer_bold.typeface.js"></script>
+		<script src="../../examples/fonts/optimer_regular.typeface.js"></script>
+		<script src="../../examples/fonts/helvetiker_bold.typeface.js"></script>
+		<script src="../../examples/fonts/helvetiker_regular.typeface.js"></script>
+		<script src="../../examples/fonts/droid/droid_serif_regular.typeface.js"></script>
+		<script src="../../examples/fonts/droid/droid_serif_bold.typeface.js"></script>
+		
+		<script src='js/geometry.js'></script>
+		
+		<script>
+			
+			document.getElementById('newWindow').href += window.location.hash;
+			
+			var gui = new dat.GUI();
+			var scene = new THREE.Scene();
+			var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 50 );
+			camera.position.z = 30;
+			
+			var renderer = new THREE.WebGLRenderer({antialias: true});
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			document.body.appendChild( renderer.domElement );
+			
+			var orbit = new THREE.OrbitControls( camera, renderer.domElement );
+			orbit.noZoom = true;
+
+			var ambientLight = new THREE.AmbientLight( 0x000000 );
+			scene.add( ambientLight );
+
+			var lights = [];
+			lights[0] = new THREE.PointLight( 0xffffff, 1, 0 );
+			lights[1] = new THREE.PointLight( 0xffffff, 1, 0 );
+			lights[2] = new THREE.PointLight( 0xffffff, 1, 0 );
+			
+			lights[0].position.set( 0, 200, 0 );
+			lights[1].position.set( 100, 200, 100 );
+			lights[2].position.set( -100, -200, -100 );
+
+			scene.add( lights[0] );
+			scene.add( lights[1] );
+			scene.add( lights[2] );
+
+			var mesh = new THREE.Object3D()
+			
+			mesh.add( new THREE.LineSegments(
+				
+				new THREE.Geometry(),
+				
+				new THREE.LineBasicMaterial({
+					color: 0xffffff,
+					transparent: true,
+					opacity: 0.5
+				})
+				
+			));
+			
+			mesh.add( new THREE.Mesh(
+				
+				new THREE.Geometry(),
+				
+				new THREE.MeshPhongMaterial({
+					color: 0x156289,
+					emissive: 0x072534,
+					side: THREE.DoubleSide,
+					shading: THREE.FlatShading
+				})
+				
+			));
+			
+			var options = chooseFromHash( mesh );
+			
+			scene.add( mesh );
+			
+			var prevFog = false;
+			
+			var render = function () {
+				
+				requestAnimationFrame( render );
+
+				var time = Date.now() * 0.001;
+
+				if( !options.fixed ) {
+					mesh.rotation.x += 0.005;
+					mesh.rotation.y += 0.005;
+				}
+
+				renderer.render( scene, camera );
+				
+			};
+			
+			window.addEventListener( 'resize', function () {
+				
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				
+			}, false );
+
+			render();
+			
+		</script>
+	</body>
+</html>

+ 549 - 0
docs/scenes/js/geometry.js

@@ -0,0 +1,549 @@
+/**
+ * @author TatumCreative (Greg Tatum) / http://gregtatum.com/
+ */
+
+var twoPi = Math.PI * 2;
+
+var constants = {
+
+	combine: {
+
+		"THREE.MultiplyOperation" : THREE.MultiplyOperation,
+		"THREE.MixOperation" : THREE.MixOperation,
+		"THREE.AddOperation" : THREE.AddOperation
+
+	},
+
+	side : {
+
+		"THREE.FrontSide" : THREE.FrontSide,
+		"THREE.BackSide" : THREE.BackSide,
+		"THREE.DoubleSide" : THREE.DoubleSide
+
+	},
+
+	shading : {
+
+		"THREE.NoShading" : THREE.NoShading,
+		"THREE.FlatShading" : THREE.FlatShading,
+		"THREE.SmoothShading" : THREE.SmoothShading
+
+	},
+
+	colors : {
+
+		"THREE.NoColors" : THREE.NoColors,
+		"THREE.FaceColors" : THREE.FaceColors,
+		"THREE.VertexColors" : THREE.VertexColors
+
+	},
+
+	blendingMode : {
+
+		"THREE.NoBlending" : THREE.NoBlending,
+		"THREE.NormalBlending" : THREE.NormalBlending,
+		"THREE.AdditiveBlending" : THREE.AdditiveBlending,
+		"THREE.SubtractiveBlending" : THREE.SubtractiveBlending,
+		"THREE.MultiplyBlending" : THREE.MultiplyBlending,
+		"THREE.CustomBlending" : THREE.CustomBlending
+
+	},
+
+	equations : {
+
+		"THREE.AddEquation" : THREE.AddEquation,
+		"THREE.SubtractEquation" : THREE.SubtractEquation,
+		"THREE.ReverseSubtractEquation" : THREE.ReverseSubtractEquation
+
+	},
+
+	destinationFactors : {
+
+		"THREE.ZeroFactor" : THREE.ZeroFactor,
+		"THREE.OneFactor" : THREE.OneFactor,
+		"THREE.SrcColorFactor" : THREE.SrcColorFactor,
+		"THREE.OneMinusSrcColorFactor" : THREE.OneMinusSrcColorFactor,
+		"THREE.SrcAlphaFactor" : THREE.SrcAlphaFactor,
+		"THREE.OneMinusSrcAlphaFactor" : THREE.OneMinusSrcAlphaFactor,
+		"THREE.DstAlphaFactor" : THREE.DstAlphaFactor,
+		"THREE.OneMinusDstAlphaFactor" : THREE.OneMinusDstAlphaFactor
+
+	},
+
+	sourceFactors : {
+
+		"THREE.DstColorFactor" : THREE.DstColorFactor,
+		"THREE.OneMinusDstColorFactor" : THREE.OneMinusDstColorFactor,
+		"THREE.SrcAlphaSaturateFactor" : THREE.SrcAlphaSaturateFactor
+
+	}
+
+}
+
+function updateGroupGeometry( mesh, geometry ) {
+	
+	mesh.children[0].geometry.dispose();
+	mesh.children[1].geometry.dispose();
+	
+	mesh.children[0].geometry = new THREE.WireframeGeometry( geometry );
+	mesh.children[1].geometry = geometry;
+	
+	//these do not update nicely together if shared
+}
+
+var guis = {
+
+	BoxGeometry : function( mesh ) {
+
+		var data = {
+			width : 15,
+			height : 15,
+			depth : 15,
+			widthSegments : 1,
+			heightSegments : 1,
+			depthSegments : 1
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.BoxGeometry(
+					data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments
+				)
+			);
+			
+		}
+
+		var folder = gui.addFolder('THREE.BoxGeometry');
+		
+		folder.add( data, 'width', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'height', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'depth', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'widthSegments', 1, 10 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'heightSegments', 1, 10 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'depthSegments', 1, 10 ).step(1).onChange( generateGeometry );
+		
+		generateGeometry();
+	},
+
+	CylinderGeometry : function( mesh ) {
+
+		var data = {
+			radiusTop : 5,
+			radiusBottom : 5,
+			height : 10,
+			radiusSegments : 8,
+			heightSegments : 1,
+			openEnded : false,
+			thetaStart : 0,
+			thetaLength : twoPi,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.CylinderGeometry(
+					data.radiusTop,
+					data.radiusBottom,
+					data.height,
+					data.radiusSegments,
+					data.heightSegments,
+					data.openEnded,
+					data.thetaStart,
+					data.thetaLength
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.CylinderGeometry');
+		
+		folder.add( data, 'radiusTop', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'radiusBottom', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'height', 1, 50 ).onChange( generateGeometry );
+		folder.add( data, 'radiusSegments', 3, 64 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'heightSegments', 1, 64 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'openEnded' ).onChange( generateGeometry );
+		folder.add( data, 'thetaStart', 0, twoPi ).onChange( generateGeometry );
+		folder.add( data, 'thetaLength', 0, twoPi ).onChange( generateGeometry );
+		
+		
+		generateGeometry();
+	},
+
+	CircleGeometry : function( mesh ) {
+
+		var data = {
+			radius : 10,
+			segments : 32,
+			thetaStart : 0,
+			thetaLength : twoPi,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.CircleGeometry(
+					data.radius, data.segments, data.thetaStart, data.thetaLength
+				)
+			);
+			
+		}
+
+		var folder = gui.addFolder('THREE.CircleGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry );
+		folder.add( data, 'segments', 0, 128 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'thetaStart', 0, twoPi ).onChange( generateGeometry );
+		folder.add( data, 'thetaLength', 0, twoPi ).onChange( generateGeometry );
+		
+		generateGeometry();
+	},
+	
+	DodecahedronGeometry : function() {
+
+		var data = {
+			radius : 10,
+			detail : 0,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.DodecahedronGeometry(
+					data.radius, data.detail
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.DodecahedronGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry )
+		folder.add( data, 'detail', 0, 5 ).step(1).onChange( generateGeometry )
+		
+		generateGeometry()
+		
+	},
+	
+	IcosahedronGeometry : function() {
+
+		var data = {
+			radius : 10,
+			detail : 0,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.IcosahedronGeometry(
+					data.radius, data.detail
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.IcosahedronGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry )
+		folder.add( data, 'detail', 0, 5 ).step(1).onChange( generateGeometry )
+		
+		generateGeometry()
+		
+	},
+	
+	OctahedronGeometry : function() {
+
+		var data = {
+			radius : 10,
+			detail : 0,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.OctahedronGeometry(
+					data.radius, data.detail
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.OctahedronGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry )
+		folder.add( data, 'detail', 0, 5 ).step(1).onChange( generateGeometry )
+		
+		generateGeometry()
+		
+	},
+
+	PlaneGeometry : function( mesh ) {
+
+		var data = {
+			width : 10,
+			height : 10,
+			widthSegments : 1,
+			heightSegments : 1
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.PlaneGeometry(
+					data.width, data.height, data.widthSegments, data.heightSegments
+				)
+			);
+			
+		}
+
+		var folder = gui.addFolder('THREE.PlaneGeometry');
+		
+		folder.add( data, 'width', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'height', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'widthSegments', 1, 30 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'heightSegments', 1, 30 ).step(1).onChange( generateGeometry );
+		
+		generateGeometry();
+	},
+
+	RingGeometry : function( mesh ) {
+
+		var data = {
+			innerRadius : 5,
+			outerRadius : 10,
+			thetaSegments : 8,
+			phiSegments : 8,
+			thetaStart : 0,
+			thetaLength : twoPi,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.RingGeometry(
+					data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength
+				)
+			);
+			
+		}
+
+		var folder = gui.addFolder('THREE.RingGeometry');
+		
+		folder.add( data, 'innerRadius', 0, 30 ).onChange( generateGeometry );
+		folder.add( data, 'outerRadius', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'thetaSegments', 1, 30 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'phiSegments', 1, 30 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'thetaStart', 0, twoPi ).onChange( generateGeometry );
+		folder.add( data, 'thetaLength', 0, twoPi ).onChange( generateGeometry );
+		
+		generateGeometry();
+	},
+	
+	SphereGeometry : function( mesh ) {
+
+		var data = {
+			radius : 15,
+			widthSegments : 8,
+			heightSegments : 6,
+			phiStart : 0,
+			phiLength : twoPi,
+			thetaStart : 0,
+			thetaLength : Math.PI,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.SphereGeometry(
+					data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength
+				)
+			);
+			
+		}
+
+		var folder = gui.addFolder('THREE.SphereGeometry');
+		
+		folder.add( data, 'radius', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'widthSegments', 3, 32 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'heightSegments', 2, 32 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'phiStart', 0, twoPi ).onChange( generateGeometry );
+		folder.add( data, 'phiLength', 0, twoPi ).onChange( generateGeometry );
+		folder.add( data, 'thetaStart', 0, twoPi ).onChange( generateGeometry );
+		folder.add( data, 'thetaLength', 0, twoPi ).onChange( generateGeometry );
+		
+		generateGeometry();
+	},
+	
+	TetrahedronGeometry : function() {
+
+		var data = {
+			radius : 10,
+			detail : 0,
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.TetrahedronGeometry(
+					data.radius, data.detail
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.TetrahedronGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry )
+		folder.add( data, 'detail', 0, 5 ).step(1).onChange( generateGeometry )
+		
+		generateGeometry()
+		
+	},
+	
+	TextGeometry : function( mesh ) {
+
+		var data = {
+			text : "TextGeometry",
+			size : 5,
+			height : 2,
+			curveSegments : 12,
+			font : "helvetiker",
+			weight : "normal",
+			style : "normal",
+			bevelEnabled : false,
+			bevelThickness : 1,
+			bevelSize : 0.5
+		};
+		
+		var fonts = [
+			"helvetiker",
+			"optimer",
+			"gentilis",
+			"droid serif"
+		]
+		
+		var weights = [
+			"normal", "bold"
+		]
+		
+		function generateGeometry() {
+			
+			var geometry = new THREE.TextGeometry( data.text, data )
+			
+			geometry.center()
+			
+			updateGroupGeometry( mesh, geometry );
+			
+		}
+		
+		//Hide the wireframe
+		mesh.children[0].visible = false;
+
+		var folder = gui.addFolder('THREE.TextGeometry');
+		
+		folder.add( data, 'text' ).onChange( generateGeometry );
+		folder.add( data, 'size', 1, 30 ).onChange( generateGeometry );
+		folder.add( data, 'height', 1, 20 ).onChange( generateGeometry );
+		folder.add( data, 'curveSegments', 1, 20 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'font', fonts ).onChange( generateGeometry );
+		folder.add( data, 'weight', weights ).onChange( generateGeometry );
+		// folder.add( data, 'style', 1, 1 ).onChange( generateGeometry );
+		folder.add( data, 'bevelEnabled' ).onChange( generateGeometry );
+		folder.add( data, 'bevelThickness', 0.1, 3 ).onChange( generateGeometry );
+		folder.add( data, 'bevelSize', 0.1, 3 ).onChange( generateGeometry );
+		
+		generateGeometry();
+	},
+	
+	TorusGeometry : function( mesh ) {
+
+		var data = {
+			radius : 10,
+			tube : 3,
+			radialSegments : 16,
+			tubularSegments : 100,
+			arc : twoPi
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.TorusGeometry(
+					data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.TorusGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry );
+		folder.add( data, 'tube', 0.1, 10 ).onChange( generateGeometry );
+		folder.add( data, 'radialSegments', 2, 30 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'tubularSegments', 3, 200 ).step(1).onChange( generateGeometry );
+		folder.add( data, 'arc', 0.1, twoPi ).onChange( generateGeometry );
+		
+		generateGeometry();
+
+	},
+	
+	TorusKnotGeometry : function( mesh ) {
+
+		var data = {
+			radius : 10,
+			tube : 3,
+			radialSegments : 64,
+			tubularSegments : 8,
+			p : 2,
+			q : 3,
+			heightScale : 1
+		};
+		
+		function generateGeometry() {
+			
+			updateGroupGeometry( mesh, 
+				new THREE.TorusKnotGeometry(
+					data.radius, data.tube, data.radialSegments, data.tubularSegments,
+					data.p, data.q, data.heightScale
+				)
+			)
+			
+		}
+
+		var folder = gui.addFolder('THREE.TorusGeometry');
+		
+		folder.add( data, 'radius', 1, 20 ).onChange( generateGeometry )
+		folder.add( data, 'tube', 0.1, 10 ).onChange( generateGeometry )
+		folder.add( data, 'radialSegments', 3, 300 ).step(1).onChange( generateGeometry )
+		folder.add( data, 'tubularSegments', 3, 20 ).step(1).onChange( generateGeometry )
+		folder.add( data, 'p', 1, 20 ).step(1).onChange( generateGeometry )
+		folder.add( data, 'q', 1, 20 ).step(1).onChange( generateGeometry )
+		folder.add( data, 'heightScale', 1, 20 ).onChange( generateGeometry )
+		
+		generateGeometry()
+
+	}
+	
+}
+
+function chooseFromHash ( mesh ) {
+
+	var selectedGeometry = window.location.hash.substring(1) || "TorusGeometry";
+
+	if ( guis[ selectedGeometry ] !== undefined ) {
+
+	    guis[ selectedGeometry ]( mesh );
+
+	}
+
+	if ( selectedGeometry === 'TextGeometry' ) {
+
+	    return { fixed : true };
+
+	}
+
+	//No configuration options
+	return {};
+
+}

+ 0 - 5
docs/scenes/js/material.js

@@ -324,7 +324,6 @@ function guiMeshBasicMaterial ( gui, mesh, material, geometry ) {
 		color : material.color.getHex(),
 		envMaps : envMapKeys,
 		map : textureMapKeys,
-		lightMap : textureMapKeys,
 		specularMap : textureMapKeys,
 		alphaMap : textureMapKeys
 	};
@@ -340,7 +339,6 @@ function guiMeshBasicMaterial ( gui, mesh, material, geometry ) {
 
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
 	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'lightMap', textureMapKeys ).onChange( updateTexture( material, 'lightMap', textureMaps ) );
 	folder.add( data, 'specularMap', textureMapKeys ).onChange( updateTexture( material, 'specularMap', textureMaps ) );
 	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
 	folder.add( material, 'morphTargets' ).onChange( updateMorphs( mesh, material ) );
@@ -365,7 +363,6 @@ function guiMeshNormalMaterial ( gui, mesh, material, geometry ) {
 
 	var folder = gui.addFolder('THREE.MeshNormalMaterial');
 
-	folder.add( material, 'shading', constants.shading).onChange( needsUpdate( material, geometry ) );
 	folder.add( material, 'wireframe' );
 	folder.add( material, 'wireframeLinewidth', 0, 10 );
 	folder.add( material, 'morphTargets' ).onChange( updateMorphs( mesh, material ) );
@@ -396,7 +393,6 @@ function guiMeshLambertMaterial ( gui, mesh, material, geometry ) {
 		emissive : material.emissive.getHex(),
 		envMaps : envMapKeys,
 		map : textureMapKeys,
-		lightMap : textureMapKeys,
 		specularMap : textureMapKeys,
 		alphaMap : textureMapKeys
 	};
@@ -416,7 +412,6 @@ function guiMeshLambertMaterial ( gui, mesh, material, geometry ) {
 
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
 	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'lightMap', textureMapKeys ).onChange( updateTexture( material, 'lightMap', textureMaps ) );
 	folder.add( data, 'specularMap', textureMapKeys ).onChange( updateTexture( material, 'specularMap', textureMaps ) );
 	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
 	folder.add( material, 'morphTargets' ).onChange( updateMorphs( mesh, material ) );

+ 64 - 29
editor/examples/arkanoid.app.json

@@ -1,26 +1,29 @@
 {
+	"project": {
+		"vr": false
+	},
 	"camera": {
 		"metadata": {
-			"version": 4.3,
+			"version": 4.4,
 			"type": "Object",
-			"generator": "ObjectExporter"
+			"generator": "Object3D.toJSON"
 		},
 		"object": {
-			"uuid": "8EFB9C06-6312-4975-B04A-C9E4549BE348",
+			"uuid": "E41E9F54-8B31-4D1F-8D09-AF5E802E9A22",
 			"type": "PerspectiveCamera",
 			"name": "Camera",
+			"matrix": [0.9392361044883728,-2.8092810300250903e-9,-0.3432718515396118,0,-0.14778217673301697,0.902585506439209,-0.404351145029068,0,0.3098321855068207,0.43051064014434814,0.847740888595581,0,142.32125854492188,202.75485229492188,389.40936279296875,1],
 			"fov": 50,
 			"aspect": 1.536388140161725,
 			"near": 0.1,
-			"far": 100000,
-			"matrix": [0.9392361044883728,-1.2050817232989175e-8,-0.34327182173728943,0,-0.15080472826957703,0.8983326554298401,-0.41262128949165344,0,0.30837228894233704,0.4393158257007599,0.8437464833259583,0,142.32125854492188,202.75485229492188,389.40936279296875,1]
+			"far": 100000
 		}
 	},
 	"scene": {
 		"metadata": {
-			"version": 4.3,
+			"version": 4.4,
 			"type": "Object",
-			"generator": "ObjectExporter"
+			"generator": "Object3D.toJSON"
 		},
 		"geometries": [
 			{
@@ -31,6 +34,16 @@
 				"widthSegments": 1,
 				"heightSegments": 1
 			},
+			{
+				"uuid": "EEDF0A9A-D174-44E4-9C2F-A2F5BB8BE7F5",
+				"type": "CylinderGeometry",
+				"radiusTop": 5,
+				"radiusBottom": 5,
+				"height": 20,
+				"radialSegments": 32,
+				"heightSegments": 1,
+				"openEnded": false
+			},
 			{
 				"uuid": "7149652B-DBD7-4CB7-A600-27A9AC005C95",
 				"type": "BoxGeometry",
@@ -53,9 +66,9 @@
 				"thetaLength": 3.14
 			},
 			{
-				"uuid": "A1D8B049-74DD-4B08-B5BC-8DCEC9AF0222",
+				"uuid": "EFBF641D-F092-462E-B7FB-0BFAD1591EFC",
 				"type": "BoxGeometry",
-				"width": 40,
+				"width": 20,
 				"height": 10,
 				"depth": 10,
 				"widthSegments": 1,
@@ -72,13 +85,19 @@
 				"shininess": 30
 			},
 			{
-				"uuid": "CFBEAD4C-6695-4A99-887B-8161E2947225",
+				"uuid": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655",
 				"type": "MeshPhongMaterial",
 				"color": 16777215,
 				"emissive": 0,
 				"specular": 1118481,
 				"shininess": 30
 			},
+			{
+				"uuid": "D98FC4D1-169E-420A-92EA-20E55009A46D",
+				"type": "MeshBasicMaterial",
+				"wireframe": true,
+				"color": 63744
+			},
 			{
 				"uuid": "043B208C-1F83-42C6-802C-E0E35621C27C",
 				"type": "MeshPhongMaterial",
@@ -89,11 +108,9 @@
 			},
 			{
 				"uuid": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB",
-				"type": "MeshPhongMaterial",
-				"color": 13486790,
-				"emissive": 0,
-				"specular": 1118481,
-				"shininess": 30
+				"type": "MeshBasicMaterial",
+				"wireframe": true,
+				"color": 63744
 			}],
 		"object": {
 			"uuid": "31517222-A9A7-4EAF-B5F6-60751C0BABA3",
@@ -105,51 +122,69 @@
 					"uuid": "EBBB1E63-6318-4752-AE2E-440A4E0B3EF3",
 					"type": "Mesh",
 					"name": "Ground",
+					"matrix": [1,0,0,0,0,0.0007960614748299122,-0.9999997019767761,0,0,0.9999997019767761,0.0007960614748299122,0,0,0,0,1],
 					"geometry": "8F05A1F2-3877-478B-8DFC-F572AC61AB3A",
-					"material": "2F69AF3A-DDF5-4BBA-87B5-80159F90DDBF",
-					"matrix": [1,0,0,0,0,0.000796250649727881,-0.9999997019767761,0,0,0.9999997019767761,0.000796250649727881,0,0,0,0,1]
+					"material": "2F69AF3A-DDF5-4BBA-87B5-80159F90DDBF"
 				},
 				{
 					"uuid": "6EE2E764-43E0-48E0-85F2-E0C8823C20DC",
 					"type": "DirectionalLight",
 					"name": "DirectionalLight 1",
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,100,200,150,1],
 					"color": 16777215,
-					"intensity": 1,
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,100,200,150,1]
+					"intensity": 1
 				},
 				{
 					"uuid": "38219749-1E67-45F2-AB15-E64BA0940CAD",
 					"type": "Mesh",
 					"name": "Brick",
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,0,1],
+					"children": [
+						{
+							"uuid": "711A5955-8F17-4A8B-991A-7604D27E6FA0",
+							"type": "Mesh",
+							"name": "Cylinder",
+							"matrix": [0.0007962009985931218,0.0007962677045725286,0.9999995231628418,0,-0.9999997615814209,3.462185702574061e-7,0.0007962677045725286,0,2.210134084634774e-7,-0.9999997615814209,0.0007962008821777999,0,0,0,0,1],
+							"geometry": "EEDF0A9A-D174-44E4-9C2F-A2F5BB8BE7F5",
+							"material": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655"
+						}],
 					"geometry": "7149652B-DBD7-4CB7-A600-27A9AC005C95",
-					"material": "CFBEAD4C-6695-4A99-887B-8161E2947225",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,0,1]
+					"material": "D98FC4D1-169E-420A-92EA-20E55009A46D"
 				},
 				{
 					"uuid": "18FFA67C-F893-4E7A-8A76-8D996DEBE0C6",
 					"type": "Mesh",
 					"name": "Ball",
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,35.54999923706055,1],
 					"geometry": "CABCC711-1331-4D4C-9FF6-409299F10C68",
-					"material": "043B208C-1F83-42C6-802C-E0E35621C27C",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,35.54999923706055,1]
+					"material": "043B208C-1F83-42C6-802C-E0E35621C27C"
 				},
 				{
 					"uuid": "6D660D49-39B8-40C3-95F6-E4E007AA8D79",
 					"type": "Mesh",
 					"name": "Paddle",
-					"geometry": "A1D8B049-74DD-4B08-B5BC-8DCEC9AF0222",
-					"material": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB",
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,0,5,159.54217529296875,1]
+					"matrix": [2,0,0,0,0,1,0,0,0,0,1,0,0,5,159.5399932861328,1],
+					"children": [
+						{
+							"uuid": "4F5F884C-9E1B-45E6-8F1E-4D538A46D8CB",
+							"type": "Mesh",
+							"name": "Cylinder",
+							"matrix": [0.0007962009985931218,0.0007962677045725286,0.9999995231628418,0,-0.9999997615814209,3.462185702574061e-7,0.0007962677045725286,0,2.210134084634774e-7,-0.9999997615814209,0.0007962008821777999,0,0,0,0,1],
+							"geometry": "EEDF0A9A-D174-44E4-9C2F-A2F5BB8BE7F5",
+							"material": "3B9DE64D-E1C8-4C24-9F73-3A9E10E3E655"
+						}],
+					"geometry": "EFBF641D-F092-462E-B7FB-0BFAD1591EFC",
+					"material": "40EC9BDA-91C0-4671-937A-2BCB6DA7EEBB"
 				},
 				{
 					"uuid": "B0BEAF69-8B5D-4D87-ADCA-FDE83A02762D",
 					"type": "PointLight",
 					"name": "PointLight 2",
+					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-116.54356384277344,69.48957061767578,-206.8248291015625,1],
 					"color": 16777215,
 					"intensity": 1,
 					"distance": 0,
-					"decay": 1,
-					"matrix": [1,0,0,0,0,1,0,0,0,0,1,0,-116.54356384277344,69.48957061767578,-206.8248291015625,1]
+					"decay": 1
 				}]
 		}
 	},
@@ -162,7 +197,7 @@
 		"31517222-A9A7-4EAF-B5F6-60751C0BABA3": [
 			{
 				"name": "Game Logic",
-				"source": "var ball = this.getObjectByName( 'Ball' );\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = - 0.5;\ndirection.normalize();\n\nvar speed = new THREE.Vector3();\n\n//\n\nvar group = new THREE.Group();\nthis.add( group );\n\nvar paddle = this.getObjectByName( 'Paddle' );\ngroup.add( paddle );\n\nvar brick = this.getObjectByName( 'Brick' );\n\nfor ( var j = 0; j < 8; j ++ ) {\n\n\tvar material = new THREE.MeshPhongMaterial( { color: Math.random() * 0xffffff } );\n\n\tfor ( var i = 0; i < 12; i ++ ) {\n\t\t\n\t\tvar object = brick.clone();\n\t\tobject.material = material;\n\t\tobject.position.x = i * 22 - 120;\n\t\tobject.position.z = j * 14 - 120;\n\t\tgroup.add( object );\n\t\t\n\t}\n\t\n}\n\nbrick.visible = false;\n\n//\n\nvar raycaster = new THREE.Raycaster();\n\nfunction update( event ) {\n\t\n\tif ( ball.position.x < - 150 || ball.position.x > 150 ) direction.x = - direction.x;\n\tif ( ball.position.z < - 200 || ball.position.z > 200 ) direction.z = - direction.z;\n\n\tball.position.x = Math.max( - 150, Math.min( 150, ball.position.x ) );\n\tball.position.z = Math.max( - 200, Math.min( 200, ball.position.z ) );\n\t\n\tball.position.add( speed.copy( direction ).multiplyScalar( event.delta / 4 ) );\n\t\n\traycaster.set( ball.position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( group.children );\n\t\n\tif ( intersections.length > 0 ) {\n\t\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 5 ) {\n\t\t\t\n\t\t\tif ( intersection.object !== paddle ) {\n\n\t\t\t\tgroup.remove( intersection.object );\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n}"
+				"source": "var ball = this.getObjectByName( 'Ball' );\n\nvar direction = new THREE.Vector3();\ndirection.x = Math.random() - 0.5;\ndirection.z = - 0.5;\ndirection.normalize();\n\nvar speed = new THREE.Vector3();\n\n//\n\nvar group = new THREE.Group();\nthis.add( group );\n\nvar paddle = this.getObjectByName( 'Paddle' );\npaddle.material.visible = false;\ngroup.add( paddle );\n\nvar brick = this.getObjectByName( 'Brick' );\n\nfor ( var j = 0; j < 8; j ++ ) {\n\n\tvar material = new THREE.MeshPhongMaterial( { color: Math.random() * 0xffffff } );\n\n\tfor ( var i = 0; i < 12; i ++ ) {\n\t\t\n\t\tvar object = brick.clone();\n\t\tobject.position.x = i * 22 - 120;\n\t\tobject.position.z = j * 14 - 120;\n\t\tgroup.add( object );\n\n\t\tvar cylinder = object.getObjectByName( 'Cylinder' );\n\t\tcylinder.material = material;\n\n\t}\n\t\n}\n\nbrick.visible = false;\nbrick.material.visible = false;\n\n//\n\nvar raycaster = new THREE.Raycaster();\n\nfunction update( event ) {\n\t\n\tif ( ball.position.x < - 150 || ball.position.x > 150 ) direction.x = - direction.x;\n\tif ( ball.position.z < - 200 || ball.position.z > 200 ) direction.z = - direction.z;\n\n\tball.position.x = Math.max( - 150, Math.min( 150, ball.position.x ) );\n\tball.position.z = Math.max( - 200, Math.min( 200, ball.position.z ) );\n\t\n\tball.position.add( speed.copy( direction ).multiplyScalar( event.delta / 4 ) );\n\t\n\traycaster.set( ball.position, direction );\n\t\n\tvar intersections = raycaster.intersectObjects( group.children );\n\t\n\tif ( intersections.length > 0 ) {\n\t\n\t\tvar intersection = intersections[ 0 ];\n\t\t\n\t\tif ( intersection.distance < 5 ) {\n\t\t\t\n\t\t\tif ( intersection.object !== paddle ) {\n\n\t\t\t\tgroup.remove( intersection.object );\n\t\t\t\t\n\t\t\t}\n\t\t\t\n\t\t\tdirection.reflect( intersection.face.normal );\n\t\t\t\n\t\t}\n\t\t\n\t}\n\n}"
 			}]
 	}
 }

+ 38 - 5
editor/index.html

@@ -40,8 +40,30 @@
 		<link rel="stylesheet" href="js/libs/codemirror/codemirror.css">
 		<link rel="stylesheet" href="js/libs/codemirror/theme/monokai.css">
 		<script src="js/libs/codemirror/codemirror.js"></script>
+
 		<script src="js/libs/codemirror/mode/javascript.js"></script>
 		<script src="js/libs/esprima.js"></script>
+		<script src="js/libs/jsonlint.js"></script>
+
+		<script src="js/libs/codemirror/mode/glsl.js"></script>
+
+		<link rel="stylesheet" href="js/libs/codemirror/addon/dialog.css">
+		<link rel="stylesheet" href="js/libs/codemirror/addon/show-hint.css">
+		<link rel="stylesheet" href="js/libs/codemirror/addon/tern.css">
+		<script src="js/libs/codemirror/addon/dialog.js"></script>
+		<script src="js/libs/codemirror/addon/show-hint.js"></script>
+		<script src="js/libs/codemirror/addon/tern.js"></script>
+		<script src="js/libs/acorn/acorn.js"></script>
+		<script src="js/libs/acorn/acorn_loose.js"></script>
+		<script src="js/libs/acorn/walk.js"></script>
+		<script src="js/libs/ternjs/polyfill.js"></script>
+		<script src="js/libs/ternjs/signal.js"></script>
+		<script src="js/libs/ternjs/tern.js"></script>
+		<script src="js/libs/ternjs/def.js"></script>
+		<script src="js/libs/ternjs/comment.js"></script>
+		<script src="js/libs/ternjs/infer.js"></script>
+		<script src="js/libs/ternjs/doc_comment.js"></script>
+		<script src="js/libs/tern-threejs/threejs.js"></script>
 
 		<script src="js/libs/jszip.min.js"></script>
 		<script src="js/libs/sortable.min.js"></script>
@@ -109,12 +131,12 @@
 			var viewport = new Viewport( editor );
 			document.body.appendChild( viewport.dom );
 
-			var script = new Script( editor );
-			document.body.appendChild( script.dom );
-
 			var player = new Player( editor );
 			document.body.appendChild( player.dom );
 
+			var script = new Script( editor );
+			document.body.appendChild( script.dom );
+
 			var toolbar = new Toolbar( editor );
 			document.body.appendChild( toolbar.dom );
 
@@ -124,8 +146,10 @@
 			var sidebar = new Sidebar( editor );
 			document.body.appendChild( sidebar.dom );
 
+			/*
 			var dialog = new UI.Dialog();
 			document.body.appendChild( dialog.dom );
+			*/
 
 			//
 
@@ -232,8 +256,17 @@
 
 				switch ( event.keyCode ) {
 
-					case 8: // prevent browser back
-						event.preventDefault();
+					case 8:
+						event.preventDefault(); // prevent browser back
+
+						var object = editor.selected;
+
+						if ( confirm( 'Delete ' + object.name + '?' ) === false ) return;
+
+						var parent = object.parent;
+						editor.removeObject( object );
+						editor.select( parent );
+
 						break;
 
 				}

+ 1 - 1
editor/js/Menubar.Edit.js

@@ -41,7 +41,7 @@ Menubar.Edit = function ( editor ) {
 	option.setClass( 'option' );
 	option.setTextContent( 'Delete' );
 	option.onClick( function () {
-	
+
 		var object = editor.selected;
 
 		if ( confirm( 'Delete ' + object.name + '?' ) === false ) return;

+ 4 - 1
editor/js/Menubar.File.js

@@ -278,12 +278,15 @@ Menubar.File = function ( editor ) {
 
 	//
 
+	var link = document.createElement( 'a' );
+	link.style.display = 'none';
+	document.body.appendChild( link ); // Firefox workaround, see #6594
+
 	var exportString = function ( output, filename ) {
 
 		var blob = new Blob( [ output ], { type: 'text/plain' } );
 		var objectURL = URL.createObjectURL( blob );
 
-		var link = document.createElement( 'a' );
 		link.href = objectURL;
 		link.download = filename || 'data.json';
 		link.target = '_blank';

+ 188 - 30
editor/js/Script.js

@@ -43,7 +43,9 @@ var Script = function ( editor ) {
 	header.add( close );
 
 	var delay;
+	var currentMode;
 	var currentScript;
+	var currentObject;
 
 	var codemirror = CodeMirror( container.dom, {
 		value: '',
@@ -51,7 +53,10 @@ var Script = function ( editor ) {
 		matchBrackets: true,
 		indentWithTabs: true,
 		tabSize: 4,
-		indentUnit: 4
+		indentUnit: 4,
+		hintOptions: {
+			completeSingle: false
+		}
 	} );
 	codemirror.setOption( 'theme', 'monokai' );
 	codemirror.on( 'change', function () {
@@ -61,17 +66,51 @@ var Script = function ( editor ) {
 
 			var value = codemirror.getValue();
 
-			if ( validate( value ) ) {
+			if ( ! validate( value ) ) return;
+
+			if ( typeof( currentScript ) === 'object' ) {
 
 				currentScript.source = value;
 				signals.scriptChanged.dispatch( currentScript );
+				return;
+			}
+
+			switch ( currentScript ) {
+
+				case 'vertexShader':
+
+					currentObject.vertexShader = value;
+					break;
+
+				case 'fragmentShader':
+
+					currentObject.fragmentShader = value;
+					break;
+
+				case 'programInfo':
+
+					var json = JSON.parse( value );
+					currentObject.defines = json.defines;
+					currentObject.uniforms = json.uniforms;
+					currentObject.attributes = json.attributes;
 
 			}
 
-		}, 300 );
+			currentObject.needsUpdate = true;
+			signals.materialChanged.dispatch( currentObject );
+
+		}, 200 );
 
 	});
 
+	// prevent backspace from deleting objects
+	var wrapper = codemirror.getWrapperElement();
+	wrapper.addEventListener( 'keydown', function ( event ) {
+
+		event.stopPropagation();
+
+	} );
+
 	// validate
 
 	var errorLines = [];
@@ -79,7 +118,7 @@ var Script = function ( editor ) {
 
 	var validate = function ( string ) {
 
-		var syntax, errors;
+		var errors;
 
 		return codemirror.operation( function () {
 
@@ -97,48 +136,84 @@ var Script = function ( editor ) {
 
 			//
 
-			try {
+			switch ( currentMode ) {
+
+				case 'javascript':
+
+					try {
+
+						var syntax = esprima.parse( string, { tolerant: true } );
+						errors = syntax.errors;
+
+					} catch ( error ) {
+
+						errors = [
+
+							{ lineNumber: error.lineNumber,message: error.message }
+						];
+
+					}
+
+					for ( var i = 0; i < errors.length; i ++ ) {
+
+						var error = errors[ i ];
+						error.message = error.message.replace(/Line [0-9]+: /, '');
+
+					}
+
+					break;
+
+				case 'json':
+
+					errors = [];
+
+					jsonlint.parseError = function ( message, info ) {
+
+						message = message.split('\n')[3];
+
+						errors.push({
+							lineNumber: info.loc.first_line,
+							message: message
+						});
+
+					};
+
+					try {
+
+						jsonlint.parse( string );
+
+					} catch ( error ) {
 
-				syntax = esprima.parse( string, { tolerant: true } );
-				errors = syntax.errors;
+						// ignore failed error recovery
 
-				for ( var i = 0; i < errors.length; i ++ ) {
+					}
 
-					var error = errors[ i ];
+					break;
 
-					var message = document.createElement( 'div' );
-					message.className = 'esprima-error';
-					message.textContent = error.message.replace(/Line [0-9]+: /, '');
+				case 'glsl':
 
-					var lineNumber = error.lineNumber - 1;
-					errorLines.push( lineNumber );
+					// TODO validate GLSL (compiling shader?)
 
-					codemirror.addLineClass( lineNumber, 'background', 'errorLine' );
+				default:
 
-					var widget = codemirror.addLineWidget(
-						lineNumber,
-						message
-					);
+					errors = [];
 
-					widgets.push( widget );
+			}
 
-				}
+			for ( var i = 0; i < errors.length; i ++ ) {
 
-			} catch ( error ) {
+				var error = errors[ i ];
 
 				var message = document.createElement( 'div' );
 				message.className = 'esprima-error';
-				message.textContent = error.message.replace(/Line [0-9]+: /, '');
+				message.textContent = error.message;
 
 				var lineNumber = error.lineNumber - 1;
 				errorLines.push( lineNumber );
 
 				codemirror.addLineClass( lineNumber, 'background', 'errorLine' );
 
-				var widget = codemirror.addLineWidget(
-					lineNumber,
-					message
-				);
+				var widget = codemirror.addLineWidget( lineNumber, message );
 
 				widgets.push( widget );
 
@@ -150,6 +225,43 @@ var Script = function ( editor ) {
 
 	};
 
+	// tern js autocomplete
+
+	var server = new CodeMirror.TernServer( {
+		caseInsensitive: true,
+		plugins: { threejs: null }
+	} );
+
+	codemirror.setOption( 'extraKeys', {
+		'Ctrl-Space': function(cm) { server.complete(cm); },
+		'Ctrl-I': function(cm) { server.showType(cm); },
+		'Ctrl-O': function(cm) { server.showDocs(cm); },
+		'Alt-.': function(cm) { server.jumpToDef(cm); },
+		'Alt-,': function(cm) { server.jumpBack(cm); },
+		'Ctrl-Q': function(cm) { server.rename(cm); },
+		'Ctrl-.': function(cm) { server.selectName(cm); }
+	} );
+
+	codemirror.on( 'cursorActivity', function( cm ) {
+
+		if ( currentMode !== 'javascript' ) return;
+		server.updateArgHints( cm );
+
+	} );
+
+	codemirror.on( 'keypress', function( cm, kb ) {
+
+		if ( currentMode !== 'javascript' ) return;
+		var typed = String.fromCharCode( kb.which || kb.keyCode );
+		if ( /[\w\.]/.exec( typed ) ) {
+
+			server.complete( cm );
+
+		}
+
+	} );
+
+
 	//
 
 	signals.editorCleared.add( function () {
@@ -160,12 +272,58 @@ var Script = function ( editor ) {
 
 	signals.editScript.add( function ( object, script ) {
 
-		container.setDisplay( '' );
+		var mode, name, source;
+
+		if ( typeof( script ) === 'object' ) {
+
+			mode = 'javascript';
+			name = script.name;
+			source = script.source;
+
+		} else {
+
+			switch ( script ) {
+
+				case 'vertexShader':
+
+					mode = 'glsl';
+					name = 'Vertex Shader';
+					source = object.vertexShader || "";
+
+					break;
+
+				case 'fragmentShader':
 
+					mode = 'glsl';
+					name = 'Fragment Shader';
+					source = object.fragmentShader || "";
+
+					break;
+
+				case 'programInfo':
+
+					mode = 'json';
+					name = 'Program Properties';
+					var json = {
+						defines: object.defines,
+						uniforms: object.uniforms,
+						attributes: object.attributes
+					};
+					source = JSON.stringify( json, null, '\t' );
+
+			}
+
+		}
+
+		currentMode = mode;
 		currentScript = script;
+		currentObject = object;
 
-		title.setValue( object.name + ' / ' + script.name );
-		codemirror.setValue( script.source );
+		title.setValue( object.name + ' / ' + name );
+		container.setDisplay( '' );
+		codemirror.setValue( source );
+		if (mode === 'json' ) mode = { name: 'javascript', json: true };
+		codemirror.setOption( 'mode', mode );
 
 	} );
 

+ 4 - 30
editor/js/Sidebar.Geometry.js

@@ -146,42 +146,16 @@ Sidebar.Geometry = function ( editor ) {
 
 			parameters.clear();
 
-			if ( geometry instanceof THREE.BoxGeometry ) {
+			if ( geometry.type === 'BufferGeometry' || geometry.type === 'Geometry' ) {
 
-				parameters.add( new Sidebar.Geometry.BoxGeometry( signals, object ) );
+				parameters.add( new Sidebar.Geometry.Modifiers( signals, object ) );
 
-			} else if ( geometry instanceof THREE.CircleGeometry ) {
+			} else if ( Sidebar.Geometry[ geometry.type ] !== undefined ) {
 
-				parameters.add( new Sidebar.Geometry.CircleGeometry( signals, object ) );
-
-			} else if ( geometry instanceof THREE.CylinderGeometry ) {
-
-				parameters.add( new Sidebar.Geometry.CylinderGeometry( signals, object ) );
-
-			} else if ( geometry instanceof THREE.SphereGeometry ) {
-
-				parameters.add( new Sidebar.Geometry.SphereGeometry( signals, object ) );
-
-			} else if ( geometry instanceof THREE.IcosahedronGeometry ) {
-
-				parameters.add( new Sidebar.Geometry.IcosahedronGeometry( signals, object ) );
-
-			} else if ( geometry instanceof THREE.PlaneGeometry ) {
-
-				parameters.add( new Sidebar.Geometry.PlaneGeometry( signals, object ) );
-
-			} else if ( geometry instanceof THREE.TorusGeometry ) {
-
-				parameters.add( new Sidebar.Geometry.TorusGeometry( signals, object ) );
-
-			} else if ( geometry instanceof THREE.TorusKnotGeometry ) {
-
-				parameters.add( new Sidebar.Geometry.TorusKnotGeometry( signals, object ) );
+				parameters.add( new Sidebar.Geometry[ geometry.type ]( signals, object ) );
 
 			}
 
-			parameters.add( new Sidebar.Geometry.Modifiers( signals, object ) );
-
 		} else {
 
 			container.setDisplay( 'none' );

+ 238 - 194
editor/js/Sidebar.Material.js

@@ -5,24 +5,7 @@
 Sidebar.Material = function ( editor ) {
 
 	var signals = editor.signals;
-
-	var materialClasses = {
-
-		'LineBasicMaterial': THREE.LineBasicMaterial,
-		'LineDashedMaterial': THREE.LineDashedMaterial,
-		'MeshBasicMaterial': THREE.MeshBasicMaterial,
-		'MeshDepthMaterial': THREE.MeshDepthMaterial,
-		'MeshFaceMaterial': THREE.MeshFaceMaterial,
-		'MeshLambertMaterial': THREE.MeshLambertMaterial,
-		'MeshNormalMaterial': THREE.MeshNormalMaterial,
-		'MeshPhongMaterial': THREE.MeshPhongMaterial,
-		'PointCloudMaterial': THREE.PointCloudMaterial,
-		'ShaderMaterial': THREE.ShaderMaterial,
-		'SpriteMaterial': THREE.SpriteMaterial,
-		'SpriteCanvasMaterial': THREE.SpriteCanvasMaterial,
-		'Material': THREE.Material
-
-	};
+	var currentObject;
 
 	var container = new UI.CollapsiblePanel();
 	container.setCollapsed( editor.config.getKey( 'ui/sidebar/material/collapsed' ) );
@@ -77,7 +60,6 @@ Sidebar.Material = function ( editor ) {
 		'LineDashedMaterial': 'LineDashedMaterial',
 		'MeshBasicMaterial': 'MeshBasicMaterial',
 		'MeshDepthMaterial': 'MeshDepthMaterial',
-		'MeshFaceMaterial': 'MeshFaceMaterial',
 		'MeshLambertMaterial': 'MeshLambertMaterial',
 		'MeshNormalMaterial': 'MeshNormalMaterial',
 		'MeshPhongMaterial': 'MeshPhongMaterial',
@@ -91,6 +73,40 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialClassRow );
 
+	// program
+
+	var materialProgramRow = new UI.Panel();
+	materialProgramRow.add( new UI.Text( 'Program' ).setWidth( '90px' ) );
+
+	var materialProgramInfo = new UI.Button( 'Info' );
+	materialProgramInfo.setMarginLeft( '4px' );
+	materialProgramInfo.onClick( function () {
+
+		signals.editScript.dispatch( currentObject.material, 'programInfo' );
+
+	} );
+	materialProgramRow.add( materialProgramInfo );
+
+	var materialProgramVertex = new UI.Button( 'Vertex' );
+	materialProgramVertex.setMarginLeft( '4px' );
+	materialProgramVertex.onClick( function () {
+
+		signals.editScript.dispatch( currentObject.material, 'vertexShader' );
+
+	} );
+	materialProgramRow.add( materialProgramVertex );
+
+	var materialProgramFragment = new UI.Button( 'Fragment' );
+	materialProgramFragment.setMarginLeft( '4px' );
+	materialProgramFragment.onClick( function () {
+
+		signals.editScript.dispatch( currentObject.material, 'fragmentShader' );
+
+	} );
+	materialProgramRow.add( materialProgramFragment );
+
+	container.add( materialProgramRow );
+
 	// color
 
 	var materialColorRow = new UI.Panel();
@@ -131,39 +147,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialShininessRow );
 
-	// uniforms
-
-	var materialUniformsRow = new UI.Panel();
-	var materialUniforms = new UI.TextArea().setWidth( '150px' ).setHeight( '80px' );
-	materialUniforms.setValue( '{\n  "uColor": {\n    "type": "3f",\n    "value": [1, 0, 0]\n  }\n}' ).onChange( update );
-
-	materialUniformsRow.add( new UI.Text( 'Uniforms' ).setWidth( '90px' ) );
-	materialUniformsRow.add( materialUniforms );
-
-	container.add( materialUniformsRow );
-
-	// vertex shader
-
-	var materialVertexShaderRow = new UI.Panel();
-	var materialVertexShader = new UI.TextArea().setWidth( '150px' ).setHeight( '80px' );
-	materialVertexShader.setValue( 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}' ).onChange( update );
-
-	materialVertexShaderRow.add( new UI.Text( 'Vertex Shader' ).setWidth( '90px' ) );
-	materialVertexShaderRow.add( materialVertexShader );
-
-	container.add( materialVertexShaderRow );
-
-	// fragment shader
-
-	var materialFragmentShaderRow = new UI.Panel();
-	var materialFragmentShader = new UI.TextArea().setWidth( '150px' ).setHeight( '80px' );
-	materialFragmentShader.setValue( 'uniform vec3 uColor;\n\nvoid main() {\n\tgl_FragColor = vec4( uColor, 1.0 );\n}' ).onChange( update );
-
-	materialFragmentShaderRow.add( new UI.Text( 'Fragment Shader' ).setWidth( '90px' ) );
-	materialFragmentShaderRow.add( materialFragmentShader );
-
-	container.add( materialFragmentShaderRow );
-
 	// vertex colors
 
 	var materialVertexColorsRow = new UI.Panel();
@@ -214,18 +197,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialAlphaMapRow );
 
-	// light map
-
-	var materialLightMapRow = new UI.Panel();
-	var materialLightMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialLightMap = new UI.Texture().onChange( update );
-
-	materialLightMapRow.add( new UI.Text( 'Light Map' ).setWidth( '90px' ) );
-	materialLightMapRow.add( materialLightMapEnabled );
-	materialLightMapRow.add( materialLightMap );
-
-	container.add( materialLightMapRow );
-
 	// bump map
 
 	var materialBumpMapRow = new UI.Panel();
@@ -278,6 +249,32 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialEnvMapRow );
 
+	// light map
+
+	var materialLightMapRow = new UI.Panel();
+	var materialLightMapEnabled = new UI.Checkbox( false ).onChange( update );
+	var materialLightMap = new UI.Texture().onChange( update );
+
+	materialLightMapRow.add( new UI.Text( 'Light Map' ).setWidth( '90px' ) );
+	materialLightMapRow.add( materialLightMapEnabled );
+	materialLightMapRow.add( materialLightMap );
+
+	container.add( materialLightMapRow );
+
+	// ambient occlusion map
+
+	var materialAOMapRow = new UI.Panel();
+	var materialAOMapEnabled = new UI.Checkbox( false ).onChange( update );
+	var materialAOMap = new UI.Texture().onChange( update );
+	var materialAOScale = new UI.Number( 1 ).setRange( 0, 1 ).setWidth( '30px' ).onChange( update );
+
+	materialAOMapRow.add( new UI.Text( 'AO Map' ).setWidth( '90px' ) );
+	materialAOMapRow.add( materialAOMapEnabled );
+	materialAOMapRow.add( materialAOMap );
+	materialAOMapRow.add( materialAOScale );
+
+	container.add( materialAOMapRow );
+
 	// side
 
 	var materialSideRow = new UI.Panel();
@@ -365,7 +362,7 @@ Sidebar.Material = function ( editor ) {
 
 	function update() {
 
-		var object = editor.selected;
+		var object = currentObject;
 
 		var geometry = object.geometry;
 		var material = object.material;
@@ -385,10 +382,16 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material instanceof materialClasses[ materialClass.getValue() ] === false ) {
+			if ( material instanceof THREE[ materialClass.getValue() ] === false ) {
+
+				material = new THREE[ materialClass.getValue() ]();
 
-				material = new materialClasses[ materialClass.getValue() ]();
 				object.material = material;
+				// TODO Copy other references in the scene graph
+				// keeping name and UUID then.
+				// Also there should be means to create a unique
+				// copy for the current object explicitly and to
+				// attach the current material to other objects.
 
 			}
 
@@ -416,24 +419,6 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material.uniforms !== undefined ) {
-
-				material.uniforms = JSON.parse( materialUniforms.getValue() );
-
-			}
-
-			if ( material.vertexShader !== undefined ) {
-
-				material.vertexShader = materialVertexShader.getValue();
-
-			}
-
-			if ( material.fragmentShader !== undefined ) {
-
-				material.fragmentShader = materialFragmentShader.getValue();
-
-			}
-
 			if ( material.vertexColors !== undefined ) {
 
 				var vertexColors = parseInt( materialVertexColors.getValue() );
@@ -493,25 +478,6 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			/*
-			if ( material.lightMap !== undefined ) {
-
-				var lightMapEnabled = materialLightMapEnabled.getValue() === true;
-
-				if ( objectHasUvs )  {
-
-					material.lightMap = lightMapEnabled ? materialLightMap.getValue() : null;
-					material.needsUpdate = true;
-
-				} else {
-
-					if ( lightMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-			*/
-
 			if ( material.bumpMap !== undefined ) {
 
 				var bumpMapEnabled = materialBumpMapEnabled.getValue() === true;
@@ -574,6 +540,42 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+
+			if ( material.lightMap !== undefined ) {
+
+				var lightMapEnabled = materialLightMapEnabled.getValue() === true;
+
+				if ( objectHasUvs ) {
+
+					material.lightMap = specularMapEnabled ? materialLightMap.getValue() : null;
+					material.needsUpdate = true;
+
+				} else {
+
+					if ( lightMapEnabled ) textureWarning = true;
+
+				}
+
+			}
+
+			if ( material.aoMap !== undefined ) {
+
+				var aoMapEnabled = materialAOMapEnabled.getValue() === true;
+
+				if ( objectHasUvs ) {
+
+					material.aoMap = aoMapEnabled ? materialAOMap.getValue() : null;
+					material.aoMapIntensity = materialAOScale.getValue();
+					material.needsUpdate = true;
+
+				} else {
+
+					if ( aoMapEnabled ) textureWarning = true;
+
+				}
+
+			}
+
 			if ( material.side !== undefined ) {
 
 				material.side = parseInt( materialSide.getValue() );
@@ -616,7 +618,7 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			updateRows();
+			refreshUi(false);
 
 			signals.materialChanged.dispatch( material );
 
@@ -630,7 +632,9 @@ Sidebar.Material = function ( editor ) {
 
 	};
 
-	function updateRows() {
+	//
+
+	function setRowVisibility() {
 
 		var properties = {
 			'name': materialNameRow,
@@ -638,18 +642,17 @@ Sidebar.Material = function ( editor ) {
 			'emissive': materialEmissiveRow,
 			'specular': materialSpecularRow,
 			'shininess': materialShininessRow,
-			'uniforms': materialUniformsRow,
-			'vertexShader': materialVertexShaderRow,
-			'fragmentShader': materialFragmentShaderRow,
+			'vertexShader': materialProgramRow,
 			'vertexColors': materialVertexColorsRow,
 			'skinning': materialSkinningRow,
 			'map': materialMapRow,
 			'alphaMap': materialAlphaMapRow,
-			'lightMap': materialLightMapRow,
 			'bumpMap': materialBumpMapRow,
 			'normalMap': materialNormalMapRow,
 			'specularMap': materialSpecularMapRow,
 			'envMap': materialEnvMapRow,
+			'lightMap': materialLightMapRow,
+			'aoMap': materialAOMapRow,
 			'side': materialSideRow,
 			'shading': materialShadingRow,
 			'blending': materialBlendingRow,
@@ -658,7 +661,7 @@ Sidebar.Material = function ( editor ) {
 			'wireframe': materialWireframeRow
 		};
 
-		var material = editor.selected.material;
+		var material = currentObject.material;
 
 		for ( var property in properties ) {
 
@@ -668,183 +671,224 @@ Sidebar.Material = function ( editor ) {
 
 	};
 
-	// events
 
-	signals.objectSelected.add( function ( object ) {
+	function refreshUi(resetTextureSelectors) {
 
-		if ( object && object.material ) {
+		var material = currentObject.material;
 
-			container.setDisplay( '' );
+		if ( material.uuid !== undefined ) {
 
-			var material = object.material;
+			materialUUID.setValue( material.uuid );
 
-			if ( material.uuid !== undefined ) {
+		}
 
-				materialUUID.setValue( material.uuid );
+		if ( material.name !== undefined ) {
 
-			}
+			materialName.setValue( material.name );
 
-			if ( material.name !== undefined ) {
+		}
 
-				materialName.setValue( material.name );
+		materialClass.setValue( material.type );
 
-			}
+		if ( material.color !== undefined ) {
 
-			materialClass.setValue( material.type );
+			materialColor.setHexValue( material.color.getHexString() );
 
-			if ( material.color !== undefined ) {
+		}
 
-				materialColor.setHexValue( material.color.getHexString() );
+		if ( material.emissive !== undefined ) {
 
-			}
+			materialEmissive.setHexValue( material.emissive.getHexString() );
 
-			if ( material.emissive !== undefined ) {
+		}
 
-				materialEmissive.setHexValue( material.emissive.getHexString() );
+		if ( material.specular !== undefined ) {
 
-			}
+			materialSpecular.setHexValue( material.specular.getHexString() );
 
-			if ( material.specular !== undefined ) {
+		}
 
-				materialSpecular.setHexValue( material.specular.getHexString() );
+		if ( material.shininess !== undefined ) {
 
-			}
+			materialShininess.setValue( material.shininess );
 
-			if ( material.shininess !== undefined ) {
+		}
 
-				materialShininess.setValue( material.shininess );
+		if ( material.vertexColors !== undefined ) {
 
-			}
+			materialVertexColors.setValue( material.vertexColors );
 
-			if ( material.uniforms !== undefined ) {
+		}
 
-				materialUniforms.setValue( JSON.stringify( material.uniforms, null, '  ' ) );
+		if ( material.skinning !== undefined ) {
 
-			}
+			materialSkinning.setValue( material.skinning );
 
-			if ( material.vertexShader !== undefined ) {
+		}
 
-				materialVertexShader.setValue( material.vertexShader );
+		if ( material.map !== undefined ) {
 
-			}
+			materialMapEnabled.setValue( material.map !== null );
 
-			if ( material.fragmentShader !== undefined ) {
+			if ( material.map !== null || resetTextureSelectors ) {
 
-				materialFragmentShader.setValue( material.fragmentShader );
+				materialMap.setValue( material.map );
 
 			}
 
-			if ( material.vertexColors !== undefined ) {
+		}
 
-				materialVertexColors.setValue( material.vertexColors );
+		if ( material.alphaMap !== undefined ) {
 
-			}
+			materialAlphaMapEnabled.setValue( material.alphaMap !== null );
 
-			if ( material.skinning !== undefined ) {
+			if ( material.alphaMap !== null || resetTextureSelectors ) {
 
-				materialSkinning.setValue( material.skinning );
+				materialAlphaMap.setValue( material.alphaMap );
 
 			}
 
-			if ( material.map !== undefined ) {
+		}
 
-				materialMapEnabled.setValue( material.map !== null );
-				materialMap.setValue( material.map );
+		if ( material.bumpMap !== undefined ) {
 
-			}
+			materialBumpMapEnabled.setValue( material.bumpMap !== null );
 
-			if ( material.alphaMap !== undefined ) {
+			if ( material.bumpMap !== null || resetTextureSelectors ) {
 
-				materialAlphaMapEnabled.setValue( material.alphaMap !== null );
-				materialAlphaMap.setValue( material.alphaMap );
+				materialBumpMap.setValue( material.bumpMap );
 
 			}
 
-			/*
-			if ( material.lightMap !== undefined ) {
+			materialBumpScale.setValue( material.bumpScale );
 
-				materialLightMapEnabled.setValue( material.lightMap !== null );
-				materialLightMap.setValue( material.lightMap );
+		}
 
-			}
-			*/
+		if ( material.normalMap !== undefined ) {
 
-			if ( material.bumpMap !== undefined ) {
+			materialNormalMapEnabled.setValue( material.normalMap !== null );
 
-				materialBumpMapEnabled.setValue( material.bumpMap !== null );
-				materialBumpMap.setValue( material.bumpMap );
-				materialBumpScale.setValue( material.bumpScale );
+			if ( material.normalMap !== null || resetTextureSelectors ) {
+
+				materialNormalMap.setValue( material.normalMap );
 
 			}
 
-			if ( material.normalMap !== undefined ) {
+		}
 
-				materialNormalMapEnabled.setValue( material.normalMap !== null );
-				materialNormalMap.setValue( material.normalMap );
+		if ( material.specularMap !== undefined ) {
 
-			}
+			materialSpecularMapEnabled.setValue( material.specularMap !== null );
 
-			if ( material.specularMap !== undefined ) {
+			if ( material.specularMap !== null || resetTextureSelectors ) {
 
-				materialSpecularMapEnabled.setValue( material.specularMap !== null );
 				materialSpecularMap.setValue( material.specularMap );
 
 			}
 
-			if ( material.envMap !== undefined ) {
+		}
+
+		if ( material.envMap !== undefined ) {
+
+			materialEnvMapEnabled.setValue( material.envMap !== null );
+
+			if ( material.envMap !== null || resetTextureSelectors ) {
 
-				materialEnvMapEnabled.setValue( material.envMap !== null );
 				materialEnvMap.setValue( material.envMap );
-				materialReflectivity.setValue( material.reflectivity );
 
 			}
 
-			if ( material.side !== undefined ) {
+			materialReflectivity.setValue( material.reflectivity );
 
-				materialSide.setValue( material.side );
+		}
 
-			}
+		if ( material.lightMap !== undefined ) {
 
-			if ( material.shading !== undefined ) {
+			materialLightMapEnabled.setValue( material.lightMap !== null );
+
+			if ( material.lightMap !== null || resetTextureSelectors ) {
 
-				materialShading.setValue( material.shading );
+				materialLightMap.setValue( material.lightMap );
 
 			}
 
-			if ( material.blending !== undefined ) {
+		}
 
-				materialBlending.setValue( material.blending );
+		if ( material.aoMap !== undefined ) {
 
-			}
+			materialAOMapEnabled.setValue( material.aoMap !== null );
 
-			if ( material.opacity !== undefined ) {
+			if ( material.aoMap !== null || resetTextureSelectors ) {
 
-				materialOpacity.setValue( material.opacity );
+				materialAOMap.setValue( material.aoMap );
 
 			}
 
-			if ( material.transparent !== undefined ) {
+			materialAOScale.setValue( material.aoMapIntensity );
 
-				materialTransparent.setValue( material.transparent );
+		}
 
-			}
+		if ( material.side !== undefined ) {
 
-			if ( material.wireframe !== undefined ) {
+			materialSide.setValue( material.side );
 
-				materialWireframe.setValue( material.wireframe );
+		}
 
-			}
+		if ( material.shading !== undefined ) {
 
-			if ( material.wireframeLinewidth !== undefined ) {
+			materialShading.setValue( material.shading );
 
-				materialWireframeLinewidth.setValue( material.wireframeLinewidth );
+		}
 
-			}
+		if ( material.blending !== undefined ) {
+
+			materialBlending.setValue( material.blending );
 
-			updateRows();
+		}
+
+		if ( material.opacity !== undefined ) {
+
+			materialOpacity.setValue( material.opacity );
+
+		}
+
+		if ( material.transparent !== undefined ) {
+
+			materialTransparent.setValue( material.transparent );
+
+		}
+
+		if ( material.wireframe !== undefined ) {
+
+			materialWireframe.setValue( material.wireframe );
+
+		}
+
+		if ( material.wireframeLinewidth !== undefined ) {
+
+			materialWireframeLinewidth.setValue( material.wireframeLinewidth );
+
+		}
+
+		setRowVisibility();
+
+	}
+
+	// events
+
+	signals.objectSelected.add( function ( object ) {
+
+		if ( object && object.material ) {
+
+			var objectChanged = object !== currentObject;
+
+			currentObject = object;
+			refreshUi(objectChanged);
+			container.setDisplay( '' );
 
 		} else {
 
+			currentObject = null;
 			container.setDisplay( 'none' );
 
 		}

+ 7 - 0
editor/js/Sidebar.Script.js

@@ -45,6 +45,13 @@ Sidebar.Script = function ( editor ) {
 		scriptsContainer.clear();
 
 		var object = editor.selected;
+
+		if ( object === null ) {
+
+			return;
+
+		}
+
 		var scripts = editor.scripts[ object.uuid ];
 
 		if ( scripts !== undefined ) {

+ 1 - 1
editor/js/Viewport.js

@@ -89,7 +89,7 @@ var Viewport = function ( editor ) {
 
 		raycaster.setFromCamera( mouse, camera );
 
-		if ( object instanceof Array ) {
+		if ( Array.isArray( object ) ) {
 
 			return raycaster.intersectObjects( object );
 

File diff suppressed because it is too large
+ 877 - 0
editor/js/libs/acorn/acorn.js


+ 1299 - 0
editor/js/libs/acorn/acorn_loose.js

@@ -0,0 +1,1299 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.acorn || (g.acorn = {})).loose = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+"use strict";
+
+var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { "default": obj }; };
+
+exports.parse_dammit = parse_dammit;
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+// Acorn: Loose parser
+//
+// This module provides an alternative parser (`parse_dammit`) that
+// exposes that same interface as `parse`, but will try to parse
+// anything as JavaScript, repairing syntax error the best it can.
+// There are circumstances in which it will raise an error and give
+// up, but they are very rare. The resulting AST will be a mostly
+// valid JavaScript AST (as per the [Mozilla parser API][api], except
+// that:
+//
+// - Return outside functions is allowed
+//
+// - Label consistency (no conflicts, break only to existing labels)
+//   is not enforced.
+//
+// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
+//   the parser got too confused to return anything meaningful.
+//
+// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
+//
+// The expected use for this is to *first* try `acorn.parse`, and only
+// if that fails switch to `parse_dammit`. The loose parser might
+// parse badly indented code incorrectly, so **don't** use it as
+// your default parser.
+//
+// Quite a lot of acorn.js is duplicated here. The alternative was to
+// add a *lot* of extra cruft to that file, making it less readable
+// and slower. Copying and editing the code allowed me to make
+// invasive changes and simplifications without creating a complicated
+// tangle.
+
+var acorn = _interopRequireWildcard(require(".."));
+
+var _state = require("./state");
+
+var LooseParser = _state.LooseParser;
+
+require("./tokenize");
+
+require("./parseutil");
+
+require("./statement");
+
+require("./expression");
+
+exports.LooseParser = _state.LooseParser;
+
+acorn.defaultOptions.tabSize = 4;
+
+function parse_dammit(input, options) {
+  var p = new LooseParser(input, options);
+  p.next();
+  return p.parseTopLevel();
+}
+
+acorn.parse_dammit = parse_dammit;
+acorn.LooseParser = LooseParser;
+
+},{"..":2,"./expression":3,"./parseutil":4,"./state":5,"./statement":6,"./tokenize":7}],2:[function(require,module,exports){
+"use strict";
+
+module.exports = typeof window != "undefined" ? window.acorn : require(("suppress", "./acorn"));
+
+},{}],3:[function(require,module,exports){
+"use strict";
+
+var LooseParser = require("./state").LooseParser;
+
+var isDummy = require("./parseutil").isDummy;
+
+var tt = require("..").tokTypes;
+
+var lp = LooseParser.prototype;
+
+lp.checkLVal = function (expr) {
+  if (!expr) return expr;
+  switch (expr.type) {
+    case "Identifier":
+    case "MemberExpression":
+    case "ObjectPattern":
+    case "ArrayPattern":
+    case "RestElement":
+    case "AssignmentPattern":
+      return expr;
+
+    default:
+      return this.dummyIdent();
+  }
+};
+
+lp.parseExpression = function (noIn) {
+  var start = this.storeCurrentPos();
+  var expr = this.parseMaybeAssign(noIn);
+  if (this.tok.type === tt.comma) {
+    var node = this.startNodeAt(start);
+    node.expressions = [expr];
+    while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn));
+    return this.finishNode(node, "SequenceExpression");
+  }
+  return expr;
+};
+
+lp.parseParenExpression = function () {
+  this.pushCx();
+  this.expect(tt.parenL);
+  var val = this.parseExpression();
+  this.popCx();
+  this.expect(tt.parenR);
+  return val;
+};
+
+lp.parseMaybeAssign = function (noIn) {
+  var start = this.storeCurrentPos();
+  var left = this.parseMaybeConditional(noIn);
+  if (this.tok.type.isAssign) {
+    var node = this.startNodeAt(start);
+    node.operator = this.tok.value;
+    node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left);
+    this.next();
+    node.right = this.parseMaybeAssign(noIn);
+    return this.finishNode(node, "AssignmentExpression");
+  }
+  return left;
+};
+
+lp.parseMaybeConditional = function (noIn) {
+  var start = this.storeCurrentPos();
+  var expr = this.parseExprOps(noIn);
+  if (this.eat(tt.question)) {
+    var node = this.startNodeAt(start);
+    node.test = expr;
+    node.consequent = this.parseMaybeAssign();
+    node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent();
+    return this.finishNode(node, "ConditionalExpression");
+  }
+  return expr;
+};
+
+lp.parseExprOps = function (noIn) {
+  var start = this.storeCurrentPos();
+  var indent = this.curIndent,
+      line = this.curLineStart;
+  return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line);
+};
+
+lp.parseExprOp = function (left, start, minPrec, noIn, indent, line) {
+  if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left;
+  var prec = this.tok.type.binop;
+  if (prec != null && (!noIn || this.tok.type !== tt._in)) {
+    if (prec > minPrec) {
+      var node = this.startNodeAt(start);
+      node.left = left;
+      node.operator = this.tok.value;
+      this.next();
+      if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
+        node.right = this.dummyIdent();
+      } else {
+        var rightStart = this.storeCurrentPos();
+        node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line);
+      }
+      this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
+      return this.parseExprOp(node, start, minPrec, noIn, indent, line);
+    }
+  }
+  return left;
+};
+
+lp.parseMaybeUnary = function (noIn) {
+  if (this.tok.type.prefix) {
+    var node = this.startNode(),
+        update = this.tok.type === tt.incDec;
+    node.operator = this.tok.value;
+    node.prefix = true;
+    this.next();
+    node.argument = this.parseMaybeUnary(noIn);
+    if (update) node.argument = this.checkLVal(node.argument);
+    return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+  } else if (this.tok.type === tt.ellipsis) {
+    var node = this.startNode();
+    this.next();
+    node.argument = this.parseMaybeUnary(noIn);
+    return this.finishNode(node, "SpreadElement");
+  }
+  var start = this.storeCurrentPos();
+  var expr = this.parseExprSubscripts();
+  while (this.tok.type.postfix && !this.canInsertSemicolon()) {
+    var node = this.startNodeAt(start);
+    node.operator = this.tok.value;
+    node.prefix = false;
+    node.argument = this.checkLVal(expr);
+    this.next();
+    expr = this.finishNode(node, "UpdateExpression");
+  }
+  return expr;
+};
+
+lp.parseExprSubscripts = function () {
+  var start = this.storeCurrentPos();
+  return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart);
+};
+
+lp.parseSubscripts = function (base, start, noCalls, startIndent, line) {
+  for (;;) {
+    if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) {
+      if (this.tok.type == tt.dot && this.curIndent == startIndent) --startIndent;else return base;
+    }
+
+    if (this.eat(tt.dot)) {
+      var node = this.startNodeAt(start);
+      node.object = base;
+      if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) node.property = this.dummyIdent();else node.property = this.parsePropertyAccessor() || this.dummyIdent();
+      node.computed = false;
+      base = this.finishNode(node, "MemberExpression");
+    } else if (this.tok.type == tt.bracketL) {
+      this.pushCx();
+      this.next();
+      var node = this.startNodeAt(start);
+      node.object = base;
+      node.property = this.parseExpression();
+      node.computed = true;
+      this.popCx();
+      this.expect(tt.bracketR);
+      base = this.finishNode(node, "MemberExpression");
+    } else if (!noCalls && this.tok.type == tt.parenL) {
+      this.pushCx();
+      var node = this.startNodeAt(start);
+      node.callee = base;
+      node.arguments = this.parseExprList(tt.parenR);
+      base = this.finishNode(node, "CallExpression");
+    } else if (this.tok.type == tt.backQuote) {
+      var node = this.startNodeAt(start);
+      node.tag = base;
+      node.quasi = this.parseTemplate();
+      base = this.finishNode(node, "TaggedTemplateExpression");
+    } else {
+      return base;
+    }
+  }
+};
+
+lp.parseExprAtom = function () {
+  var node = undefined;
+  switch (this.tok.type) {
+    case tt._this:
+    case tt._super:
+      var type = this.tok.type === tt._this ? "ThisExpression" : "Super";
+      node = this.startNode();
+      this.next();
+      return this.finishNode(node, type);
+
+    case tt.name:
+      var start = this.storeCurrentPos();
+      var id = this.parseIdent();
+      return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id;
+
+    case tt.regexp:
+      node = this.startNode();
+      var val = this.tok.value;
+      node.regex = { pattern: val.pattern, flags: val.flags };
+      node.value = val.value;
+      node.raw = this.input.slice(this.tok.start, this.tok.end);
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case tt.num:case tt.string:
+      node = this.startNode();
+      node.value = this.tok.value;
+      node.raw = this.input.slice(this.tok.start, this.tok.end);
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case tt._null:case tt._true:case tt._false:
+      node = this.startNode();
+      node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true;
+      node.raw = this.tok.type.keyword;
+      this.next();
+      return this.finishNode(node, "Literal");
+
+    case tt.parenL:
+      var parenStart = this.storeCurrentPos();
+      this.next();
+      var inner = this.parseExpression();
+      this.expect(tt.parenR);
+      if (this.eat(tt.arrow)) {
+        return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (isDummy(inner) ? [] : [inner]));
+      }
+      if (this.options.preserveParens) {
+        var par = this.startNodeAt(parenStart);
+        par.expression = inner;
+        inner = this.finishNode(par, "ParenthesizedExpression");
+      }
+      return inner;
+
+    case tt.bracketL:
+      node = this.startNode();
+      this.pushCx();
+      node.elements = this.parseExprList(tt.bracketR, true);
+      return this.finishNode(node, "ArrayExpression");
+
+    case tt.braceL:
+      return this.parseObj();
+
+    case tt._class:
+      return this.parseClass();
+
+    case tt._function:
+      node = this.startNode();
+      this.next();
+      return this.parseFunction(node, false);
+
+    case tt._new:
+      return this.parseNew();
+
+    case tt._yield:
+      node = this.startNode();
+      this.next();
+      if (this.semicolon() || this.canInsertSemicolon() || this.tok.type != tt.star && !this.tok.type.startsExpr) {
+        node.delegate = false;
+        node.argument = null;
+      } else {
+        node.delegate = this.eat(tt.star);
+        node.argument = this.parseMaybeAssign();
+      }
+      return this.finishNode(node, "YieldExpression");
+
+    case tt.backQuote:
+      return this.parseTemplate();
+
+    default:
+      return this.dummyIdent();
+  }
+};
+
+lp.parseNew = function () {
+  var node = this.startNode(),
+      startIndent = this.curIndent,
+      line = this.curLineStart;
+  var meta = this.parseIdent(true);
+  if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
+    node.meta = meta;
+    node.property = this.parseIdent(true);
+    return this.finishNode(node, "MetaProperty");
+  }
+  var start = this.storeCurrentPos();
+  node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line);
+  if (this.tok.type == tt.parenL) {
+    this.pushCx();
+    node.arguments = this.parseExprList(tt.parenR);
+  } else {
+    node.arguments = [];
+  }
+  return this.finishNode(node, "NewExpression");
+};
+
+lp.parseTemplateElement = function () {
+  var elem = this.startNode();
+  elem.value = {
+    raw: this.input.slice(this.tok.start, this.tok.end),
+    cooked: this.tok.value
+  };
+  this.next();
+  elem.tail = this.tok.type === tt.backQuote;
+  return this.finishNode(elem, "TemplateElement");
+};
+
+lp.parseTemplate = function () {
+  var node = this.startNode();
+  this.next();
+  node.expressions = [];
+  var curElt = this.parseTemplateElement();
+  node.quasis = [curElt];
+  while (!curElt.tail) {
+    this.next();
+    node.expressions.push(this.parseExpression());
+    if (this.expect(tt.braceR)) {
+      curElt = this.parseTemplateElement();
+    } else {
+      curElt = this.startNode();
+      curElt.value = { cooked: "", raw: "" };
+      curElt.tail = true;
+    }
+    node.quasis.push(curElt);
+  }
+  this.expect(tt.backQuote);
+  return this.finishNode(node, "TemplateLiteral");
+};
+
+lp.parseObj = function () {
+  var node = this.startNode();
+  node.properties = [];
+  this.pushCx();
+  var indent = this.curIndent + 1,
+      line = this.curLineStart;
+  this.eat(tt.braceL);
+  if (this.curIndent + 1 < indent) {
+    indent = this.curIndent;line = this.curLineStart;
+  }
+  while (!this.closes(tt.braceR, indent, line)) {
+    var prop = this.startNode(),
+        isGenerator = undefined,
+        start = undefined;
+    if (this.options.ecmaVersion >= 6) {
+      start = this.storeCurrentPos();
+      prop.method = false;
+      prop.shorthand = false;
+      isGenerator = this.eat(tt.star);
+    }
+    this.parsePropertyName(prop);
+    if (isDummy(prop.key)) {
+      if (isDummy(this.parseMaybeAssign())) this.next();this.eat(tt.comma);continue;
+    }
+    if (this.eat(tt.colon)) {
+      prop.kind = "init";
+      prop.value = this.parseMaybeAssign();
+    } else if (this.options.ecmaVersion >= 6 && (this.tok.type === tt.parenL || this.tok.type === tt.braceL)) {
+      prop.kind = "init";
+      prop.method = true;
+      prop.value = this.parseMethod(isGenerator);
+    } else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" && !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && (this.tok.type != tt.comma && this.tok.type != tt.braceR)) {
+      prop.kind = prop.key.name;
+      this.parsePropertyName(prop);
+      prop.value = this.parseMethod(false);
+    } else {
+      prop.kind = "init";
+      if (this.options.ecmaVersion >= 6) {
+        if (this.eat(tt.eq)) {
+          var assign = this.startNodeAt(start);
+          assign.operator = "=";
+          assign.left = prop.key;
+          assign.right = this.parseMaybeAssign();
+          prop.value = this.finishNode(assign, "AssignmentExpression");
+        } else {
+          prop.value = prop.key;
+        }
+      } else {
+        prop.value = this.dummyIdent();
+      }
+      prop.shorthand = true;
+    }
+    node.properties.push(this.finishNode(prop, "Property"));
+    this.eat(tt.comma);
+  }
+  this.popCx();
+  if (!this.eat(tt.braceR)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start;
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
+  }
+  return this.finishNode(node, "ObjectExpression");
+};
+
+lp.parsePropertyName = function (prop) {
+  if (this.options.ecmaVersion >= 6) {
+    if (this.eat(tt.bracketL)) {
+      prop.computed = true;
+      prop.key = this.parseExpression();
+      this.expect(tt.bracketR);
+      return;
+    } else {
+      prop.computed = false;
+    }
+  }
+  var key = this.tok.type === tt.num || this.tok.type === tt.string ? this.parseExprAtom() : this.parseIdent();
+  prop.key = key || this.dummyIdent();
+};
+
+lp.parsePropertyAccessor = function () {
+  if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent();
+};
+
+lp.parseIdent = function () {
+  var name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword;
+  if (!name) return this.dummyIdent();
+  var node = this.startNode();
+  this.next();
+  node.name = name;
+  return this.finishNode(node, "Identifier");
+};
+
+lp.initFunction = function (node) {
+  node.id = null;
+  node.params = [];
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = false;
+    node.expression = false;
+  }
+};
+
+// Convert existing expression atom to assignable pattern
+// if possible.
+
+lp.toAssignable = function (node) {
+  if (this.options.ecmaVersion >= 6 && node) {
+    switch (node.type) {
+      case "ObjectExpression":
+        node.type = "ObjectPattern";
+        var props = node.properties;
+        for (var i = 0; i < props.length; i++) {
+          this.toAssignable(props[i].value);
+        }break;
+
+      case "ArrayExpression":
+        node.type = "ArrayPattern";
+        this.toAssignableList(node.elements);
+        break;
+
+      case "SpreadElement":
+        node.type = "RestElement";
+        node.argument = this.toAssignable(node.argument);
+        break;
+
+      case "AssignmentExpression":
+        node.type = "AssignmentPattern";
+        break;
+    }
+  }
+  return this.checkLVal(node);
+};
+
+lp.toAssignableList = function (exprList) {
+  for (var i = 0; i < exprList.length; i++) {
+    this.toAssignable(exprList[i]);
+  }return exprList;
+};
+
+lp.parseFunctionParams = function (params) {
+  this.pushCx();
+  params = this.parseExprList(tt.parenR);
+  return this.toAssignableList(params);
+};
+
+lp.parseMethod = function (isGenerator) {
+  var node = this.startNode();
+  this.initFunction(node);
+  node.params = this.parseFunctionParams();
+  node.generator = isGenerator || false;
+  node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL;
+  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
+  return this.finishNode(node, "FunctionExpression");
+};
+
+lp.parseArrowExpression = function (node, params) {
+  this.initFunction(node);
+  node.params = this.toAssignableList(params);
+  node.expression = this.tok.type !== tt.braceL;
+  node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
+  return this.finishNode(node, "ArrowFunctionExpression");
+};
+
+lp.parseExprList = function (close, allowEmpty) {
+  var indent = this.curIndent,
+      line = this.curLineStart,
+      elts = [];
+  this.next(); // Opening bracket
+  while (!this.closes(close, indent + 1, line)) {
+    if (this.eat(tt.comma)) {
+      elts.push(allowEmpty ? null : this.dummyIdent());
+      continue;
+    }
+    var elt = this.parseMaybeAssign();
+    if (isDummy(elt)) {
+      if (this.closes(close, indent, line)) break;
+      this.next();
+    } else {
+      elts.push(elt);
+    }
+    this.eat(tt.comma);
+  }
+  this.popCx();
+  if (!this.eat(close)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start;
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
+  }
+  return elts;
+};
+
+},{"..":2,"./parseutil":4,"./state":5}],4:[function(require,module,exports){
+"use strict";
+
+exports.isDummy = isDummy;
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var LooseParser = require("./state").LooseParser;
+
+var _ = require("..");
+
+var Node = _.Node;
+var SourceLocation = _.SourceLocation;
+var lineBreak = _.lineBreak;
+var isNewLine = _.isNewLine;
+var tt = _.tokTypes;
+
+var lp = LooseParser.prototype;
+
+lp.startNode = function () {
+  var node = new Node();
+  node.start = this.tok.start;
+  if (this.options.locations) node.loc = new SourceLocation(this.toks, this.tok.loc.start);
+  if (this.options.directSourceFile) node.sourceFile = this.options.directSourceFile;
+  if (this.options.ranges) node.range = [this.tok.start, 0];
+  return node;
+};
+
+lp.storeCurrentPos = function () {
+  return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start;
+};
+
+lp.startNodeAt = function (pos) {
+  var node = new Node();
+  if (this.options.locations) {
+    node.start = pos[0];
+    node.loc = new SourceLocation(this.toks, pos[1]);
+    pos = pos[0];
+  } else {
+    node.start = pos;
+  }
+  if (this.options.directSourceFile) node.sourceFile = this.options.directSourceFile;
+  if (this.options.ranges) node.range = [pos, 0];
+  return node;
+};
+
+lp.finishNode = function (node, type) {
+  node.type = type;
+  node.end = this.last.end;
+  if (this.options.locations) node.loc.end = this.last.loc.end;
+  if (this.options.ranges) node.range[1] = this.last.end;
+  return node;
+};
+
+lp.dummyIdent = function () {
+  var dummy = this.startNode();
+  dummy.name = "✖";
+  return this.finishNode(dummy, "Identifier");
+};
+
+function isDummy(node) {
+  return node.name == "✖";
+}
+
+lp.eat = function (type) {
+  if (this.tok.type === type) {
+    this.next();
+    return true;
+  } else {
+    return false;
+  }
+};
+
+lp.isContextual = function (name) {
+  return this.tok.type === tt.name && this.tok.value === name;
+};
+
+lp.eatContextual = function (name) {
+  return this.tok.value === name && this.eat(tt.name);
+};
+
+lp.canInsertSemicolon = function () {
+  return this.tok.type === tt.eof || this.tok.type === tt.braceR || lineBreak.test(this.input.slice(this.last.end, this.tok.start));
+};
+
+lp.semicolon = function () {
+  return this.eat(tt.semi);
+};
+
+lp.expect = function (type) {
+  if (this.eat(type)) return true;
+  for (var i = 1; i <= 2; i++) {
+    if (this.lookAhead(i).type == type) {
+      for (var j = 0; j < i; j++) {
+        this.next();
+      }return true;
+    }
+  }
+};
+
+lp.pushCx = function () {
+  this.context.push(this.curIndent);
+};
+lp.popCx = function () {
+  this.curIndent = this.context.pop();
+};
+
+lp.lineEnd = function (pos) {
+  while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos;
+  return pos;
+};
+
+lp.indentationAfter = function (pos) {
+  for (var count = 0;; ++pos) {
+    var ch = this.input.charCodeAt(pos);
+    if (ch === 32) ++count;else if (ch === 9) count += this.options.tabSize;else return count;
+  }
+};
+
+lp.closes = function (closeTok, indent, line, blockHeuristic) {
+  if (this.tok.type === closeTok || this.tok.type === tt.eof) return true;
+  return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() && (!blockHeuristic || this.nextLineStart >= this.input.length || this.indentationAfter(this.nextLineStart) < indent);
+};
+
+lp.tokenStartsLine = function () {
+  for (var p = this.tok.start - 1; p >= this.curLineStart; --p) {
+    var ch = this.input.charCodeAt(p);
+    if (ch !== 9 && ch !== 32) return false;
+  }
+  return true;
+};
+
+},{"..":2,"./state":5}],5:[function(require,module,exports){
+"use strict";
+
+exports.LooseParser = LooseParser;
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _ = require("..");
+
+var tokenizer = _.tokenizer;
+var SourceLocation = _.SourceLocation;
+var tt = _.tokTypes;
+
+function LooseParser(input, options) {
+  this.toks = tokenizer(input, options);
+  this.options = this.toks.options;
+  this.input = this.toks.input;
+  this.tok = this.last = { type: tt.eof, start: 0, end: 0 };
+  if (this.options.locations) {
+    var here = this.toks.curPosition();
+    this.tok.loc = new SourceLocation(this.toks, here, here);
+  }
+  this.ahead = []; // Tokens ahead
+  this.context = []; // Indentation contexted
+  this.curIndent = 0;
+  this.curLineStart = 0;
+  this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
+}
+
+},{"..":2}],6:[function(require,module,exports){
+"use strict";
+
+var LooseParser = require("./state").LooseParser;
+
+var isDummy = require("./parseutil").isDummy;
+
+var _ = require("..");
+
+var getLineInfo = _.getLineInfo;
+var tt = _.tokTypes;
+
+var lp = LooseParser.prototype;
+
+lp.parseTopLevel = function () {
+  var node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0);
+  node.body = [];
+  while (this.tok.type !== tt.eof) node.body.push(this.parseStatement());
+  this.last = this.tok;
+  if (this.options.ecmaVersion >= 6) {
+    node.sourceType = this.options.sourceType;
+  }
+  return this.finishNode(node, "Program");
+};
+
+lp.parseStatement = function () {
+  var starttype = this.tok.type,
+      node = this.startNode();
+
+  switch (starttype) {
+    case tt._break:case tt._continue:
+      this.next();
+      var isBreak = starttype === tt._break;
+      if (this.semicolon() || this.canInsertSemicolon()) {
+        node.label = null;
+      } else {
+        node.label = this.tok.type === tt.name ? this.parseIdent() : null;
+        this.semicolon();
+      }
+      return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
+
+    case tt._debugger:
+      this.next();
+      this.semicolon();
+      return this.finishNode(node, "DebuggerStatement");
+
+    case tt._do:
+      this.next();
+      node.body = this.parseStatement();
+      node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent();
+      this.semicolon();
+      return this.finishNode(node, "DoWhileStatement");
+
+    case tt._for:
+      this.next();
+      this.pushCx();
+      this.expect(tt.parenL);
+      if (this.tok.type === tt.semi) return this.parseFor(node, null);
+      if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) {
+        var _init = this.parseVar(true);
+        if (_init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) {
+          return this.parseForIn(node, _init);
+        }
+        return this.parseFor(node, _init);
+      }
+      var init = this.parseExpression(true);
+      if (this.tok.type === tt._in || this.isContextual("of")) return this.parseForIn(node, this.toAssignable(init));
+      return this.parseFor(node, init);
+
+    case tt._function:
+      this.next();
+      return this.parseFunction(node, true);
+
+    case tt._if:
+      this.next();
+      node.test = this.parseParenExpression();
+      node.consequent = this.parseStatement();
+      node.alternate = this.eat(tt._else) ? this.parseStatement() : null;
+      return this.finishNode(node, "IfStatement");
+
+    case tt._return:
+      this.next();
+      if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null;else {
+        node.argument = this.parseExpression();this.semicolon();
+      }
+      return this.finishNode(node, "ReturnStatement");
+
+    case tt._switch:
+      var blockIndent = this.curIndent,
+          line = this.curLineStart;
+      this.next();
+      node.discriminant = this.parseParenExpression();
+      node.cases = [];
+      this.pushCx();
+      this.expect(tt.braceL);
+
+      var cur = undefined;
+      while (!this.closes(tt.braceR, blockIndent, line, true)) {
+        if (this.tok.type === tt._case || this.tok.type === tt._default) {
+          var isCase = this.tok.type === tt._case;
+          if (cur) this.finishNode(cur, "SwitchCase");
+          node.cases.push(cur = this.startNode());
+          cur.consequent = [];
+          this.next();
+          if (isCase) cur.test = this.parseExpression();else cur.test = null;
+          this.expect(tt.colon);
+        } else {
+          if (!cur) {
+            node.cases.push(cur = this.startNode());
+            cur.consequent = [];
+            cur.test = null;
+          }
+          cur.consequent.push(this.parseStatement());
+        }
+      }
+      if (cur) this.finishNode(cur, "SwitchCase");
+      this.popCx();
+      this.eat(tt.braceR);
+      return this.finishNode(node, "SwitchStatement");
+
+    case tt._throw:
+      this.next();
+      node.argument = this.parseExpression();
+      this.semicolon();
+      return this.finishNode(node, "ThrowStatement");
+
+    case tt._try:
+      this.next();
+      node.block = this.parseBlock();
+      node.handler = null;
+      if (this.tok.type === tt._catch) {
+        var clause = this.startNode();
+        this.next();
+        this.expect(tt.parenL);
+        clause.param = this.toAssignable(this.parseExprAtom());
+        this.expect(tt.parenR);
+        clause.guard = null;
+        clause.body = this.parseBlock();
+        node.handler = this.finishNode(clause, "CatchClause");
+      }
+      node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
+      if (!node.handler && !node.finalizer) return node.block;
+      return this.finishNode(node, "TryStatement");
+
+    case tt._var:
+    case tt._let:
+    case tt._const:
+      return this.parseVar();
+
+    case tt._while:
+      this.next();
+      node.test = this.parseParenExpression();
+      node.body = this.parseStatement();
+      return this.finishNode(node, "WhileStatement");
+
+    case tt._with:
+      this.next();
+      node.object = this.parseParenExpression();
+      node.body = this.parseStatement();
+      return this.finishNode(node, "WithStatement");
+
+    case tt.braceL:
+      return this.parseBlock();
+
+    case tt.semi:
+      this.next();
+      return this.finishNode(node, "EmptyStatement");
+
+    case tt._class:
+      return this.parseClass(true);
+
+    case tt._import:
+      return this.parseImport();
+
+    case tt._export:
+      return this.parseExport();
+
+    default:
+      var expr = this.parseExpression();
+      if (isDummy(expr)) {
+        this.next();
+        if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement");
+        return this.parseStatement();
+      } else if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) {
+        node.body = this.parseStatement();
+        node.label = expr;
+        return this.finishNode(node, "LabeledStatement");
+      } else {
+        node.expression = expr;
+        this.semicolon();
+        return this.finishNode(node, "ExpressionStatement");
+      }
+  }
+};
+
+lp.parseBlock = function () {
+  var node = this.startNode();
+  this.pushCx();
+  this.expect(tt.braceL);
+  var blockIndent = this.curIndent,
+      line = this.curLineStart;
+  node.body = [];
+  while (!this.closes(tt.braceR, blockIndent, line, true)) node.body.push(this.parseStatement());
+  this.popCx();
+  this.eat(tt.braceR);
+  return this.finishNode(node, "BlockStatement");
+};
+
+lp.parseFor = function (node, init) {
+  node.init = init;
+  node.test = node.update = null;
+  if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression();
+  if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression();
+  this.popCx();
+  this.expect(tt.parenR);
+  node.body = this.parseStatement();
+  return this.finishNode(node, "ForStatement");
+};
+
+lp.parseForIn = function (node, init) {
+  var type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement";
+  this.next();
+  node.left = init;
+  node.right = this.parseExpression();
+  this.popCx();
+  this.expect(tt.parenR);
+  node.body = this.parseStatement();
+  return this.finishNode(node, type);
+};
+
+lp.parseVar = function (noIn) {
+  var node = this.startNode();
+  node.kind = this.tok.type.keyword;
+  this.next();
+  node.declarations = [];
+  do {
+    var decl = this.startNode();
+    decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom()) : this.parseIdent();
+    decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null;
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+  } while (this.eat(tt.comma));
+  if (!node.declarations.length) {
+    var decl = this.startNode();
+    decl.id = this.dummyIdent();
+    node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
+  }
+  if (!noIn) this.semicolon();
+  return this.finishNode(node, "VariableDeclaration");
+};
+
+lp.parseClass = function (isStatement) {
+  var node = this.startNode();
+  this.next();
+  if (this.tok.type === tt.name) node.id = this.parseIdent();else if (isStatement) node.id = this.dummyIdent();else node.id = null;
+  node.superClass = this.eat(tt._extends) ? this.parseExpression() : null;
+  node.body = this.startNode();
+  node.body.body = [];
+  this.pushCx();
+  var indent = this.curIndent + 1,
+      line = this.curLineStart;
+  this.eat(tt.braceL);
+  if (this.curIndent + 1 < indent) {
+    indent = this.curIndent;line = this.curLineStart;
+  }
+  while (!this.closes(tt.braceR, indent, line)) {
+    if (this.semicolon()) continue;
+    var method = this.startNode(),
+        isGenerator = undefined,
+        start = undefined;
+    if (this.options.ecmaVersion >= 6) {
+      method["static"] = false;
+      isGenerator = this.eat(tt.star);
+    }
+    this.parsePropertyName(method);
+    if (isDummy(method.key)) {
+      if (isDummy(this.parseMaybeAssign())) this.next();this.eat(tt.comma);continue;
+    }
+    if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" && (this.tok.type != tt.parenL && this.tok.type != tt.braceL)) {
+      method["static"] = true;
+      isGenerator = this.eat(tt.star);
+      this.parsePropertyName(method);
+    } else {
+      method["static"] = false;
+    }
+    if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" && !method.computed && (method.key.name === "get" || method.key.name === "set") && this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) {
+      method.kind = method.key.name;
+      this.parsePropertyName(method);
+      method.value = this.parseMethod(false);
+    } else {
+      if (!method.computed && !method["static"] && !isGenerator && (method.key.type === "Identifier" && method.key.name === "constructor" || method.key.type === "Literal" && method.key.value === "constructor")) {
+        method.kind = "constructor";
+      } else {
+        method.kind = "method";
+      }
+      method.value = this.parseMethod(isGenerator);
+    }
+    node.body.body.push(this.finishNode(method, "MethodDefinition"));
+  }
+  this.popCx();
+  if (!this.eat(tt.braceR)) {
+    // If there is no closing brace, make the node span to the start
+    // of the next token (this is useful for Tern)
+    this.last.end = this.tok.start;
+    if (this.options.locations) this.last.loc.end = this.tok.loc.start;
+  }
+  this.semicolon();
+  this.finishNode(node.body, "ClassBody");
+  return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
+};
+
+lp.parseFunction = function (node, isStatement) {
+  this.initFunction(node);
+  if (this.options.ecmaVersion >= 6) {
+    node.generator = this.eat(tt.star);
+  }
+  if (this.tok.type === tt.name) node.id = this.parseIdent();else if (isStatement) node.id = this.dummyIdent();
+  node.params = this.parseFunctionParams();
+  node.body = this.parseBlock();
+  return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+};
+
+lp.parseExport = function () {
+  var node = this.startNode();
+  this.next();
+  if (this.eat(tt.star)) {
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
+    return this.finishNode(node, "ExportAllDeclaration");
+  }
+  if (this.eat(tt._default)) {
+    var expr = this.parseMaybeAssign();
+    if (expr.id) {
+      switch (expr.type) {
+        case "FunctionExpression":
+          expr.type = "FunctionDeclaration";break;
+        case "ClassExpression":
+          expr.type = "ClassDeclaration";break;
+      }
+    }
+    node.declaration = expr;
+    this.semicolon();
+    return this.finishNode(node, "ExportDefaultDeclaration");
+  }
+  if (this.tok.type.keyword) {
+    node.declaration = this.parseStatement();
+    node.specifiers = [];
+    node.source = null;
+  } else {
+    node.declaration = null;
+    node.specifiers = this.parseExportSpecifierList();
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
+    this.semicolon();
+  }
+  return this.finishNode(node, "ExportNamedDeclaration");
+};
+
+lp.parseImport = function () {
+  var node = this.startNode();
+  this.next();
+  if (this.tok.type === tt.string) {
+    node.specifiers = [];
+    node.source = this.parseExprAtom();
+    node.kind = "";
+  } else {
+    var elt = undefined;
+    if (this.tok.type === tt.name && this.tok.value !== "from") {
+      elt = this.startNode();
+      elt.local = this.parseIdent();
+      this.finishNode(elt, "ImportDefaultSpecifier");
+      this.eat(tt.comma);
+    }
+    node.specifiers = this.parseImportSpecifierList();
+    node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
+    if (elt) node.specifiers.unshift(elt);
+  }
+  this.semicolon();
+  return this.finishNode(node, "ImportDeclaration");
+};
+
+lp.parseImportSpecifierList = function () {
+  var elts = [];
+  if (this.tok.type === tt.star) {
+    var elt = this.startNode();
+    this.next();
+    if (this.eatContextual("as")) elt.local = this.parseIdent();
+    elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"));
+  } else {
+    var indent = this.curIndent,
+        line = this.curLineStart,
+        continuedLine = this.nextLineStart;
+    this.pushCx();
+    this.eat(tt.braceL);
+    if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
+    while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
+      var elt = this.startNode();
+      if (this.eat(tt.star)) {
+        if (this.eatContextual("as")) elt.local = this.parseIdent();
+        this.finishNode(elt, "ImportNamespaceSpecifier");
+      } else {
+        if (this.isContextual("from")) break;
+        elt.imported = this.parseIdent();
+        elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported;
+        this.finishNode(elt, "ImportSpecifier");
+      }
+      elts.push(elt);
+      this.eat(tt.comma);
+    }
+    this.eat(tt.braceR);
+    this.popCx();
+  }
+  return elts;
+};
+
+lp.parseExportSpecifierList = function () {
+  var elts = [];
+  var indent = this.curIndent,
+      line = this.curLineStart,
+      continuedLine = this.nextLineStart;
+  this.pushCx();
+  this.eat(tt.braceL);
+  if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
+  while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
+    if (this.isContextual("from")) break;
+    var elt = this.startNode();
+    elt.local = this.parseIdent();
+    elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local;
+    this.finishNode(elt, "ExportSpecifier");
+    elts.push(elt);
+    this.eat(tt.comma);
+  }
+  this.eat(tt.braceR);
+  this.popCx();
+  return elts;
+};
+
+},{"..":2,"./parseutil":4,"./state":5}],7:[function(require,module,exports){
+"use strict";
+
+var _ = require("..");
+
+var tt = _.tokTypes;
+var Token = _.Token;
+var isNewLine = _.isNewLine;
+var SourceLocation = _.SourceLocation;
+var getLineInfo = _.getLineInfo;
+var lineBreakG = _.lineBreakG;
+
+var LooseParser = require("./state").LooseParser;
+
+var lp = LooseParser.prototype;
+
+function isSpace(ch) {
+  return ch < 14 && ch > 8 || ch === 32 || ch === 160 || isNewLine(ch);
+}
+
+lp.next = function () {
+  this.last = this.tok;
+  if (this.ahead.length) this.tok = this.ahead.shift();else this.tok = this.readToken();
+
+  if (this.tok.start >= this.nextLineStart) {
+    while (this.tok.start >= this.nextLineStart) {
+      this.curLineStart = this.nextLineStart;
+      this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
+    }
+    this.curIndent = this.indentationAfter(this.curLineStart);
+  }
+};
+
+lp.readToken = function () {
+  for (;;) {
+    try {
+      this.toks.next();
+      if (this.toks.type === tt.dot && this.input.substr(this.toks.end, 1) === "." && this.options.ecmaVersion >= 6) {
+        this.toks.end++;
+        this.toks.type = tt.ellipsis;
+      }
+      return new Token(this.toks);
+    } catch (e) {
+      if (!(e instanceof SyntaxError)) throw e;
+
+      // Try to skip some text, based on the error message, and then continue
+      var msg = e.message,
+          pos = e.raisedAt,
+          replace = true;
+      if (/unterminated/i.test(msg)) {
+        pos = this.lineEnd(e.pos + 1);
+        if (/string/.test(msg)) {
+          replace = { start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos) };
+        } else if (/regular expr/i.test(msg)) {
+          var re = this.input.slice(e.pos, pos);
+          try {
+            re = new RegExp(re);
+          } catch (e) {}
+          replace = { start: e.pos, end: pos, type: tt.regexp, value: re };
+        } else if (/template/.test(msg)) {
+          replace = { start: e.pos, end: pos,
+            type: tt.template,
+            value: this.input.slice(e.pos, pos) };
+        } else {
+          replace = false;
+        }
+      } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) {
+        while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos;
+      } else if (/character escape|expected hexadecimal/i.test(msg)) {
+        while (pos < this.input.length) {
+          var ch = this.input.charCodeAt(pos++);
+          if (ch === 34 || ch === 39 || isNewLine(ch)) break;
+        }
+      } else if (/unexpected character/i.test(msg)) {
+        pos++;
+        replace = false;
+      } else if (/regular expression/i.test(msg)) {
+        replace = true;
+      } else {
+        throw e;
+      }
+      this.resetTo(pos);
+      if (replace === true) replace = { start: pos, end: pos, type: tt.name, value: "✖" };
+      if (replace) {
+        if (this.options.locations) replace.loc = new SourceLocation(this.toks, getLineInfo(this.input, replace.start), getLineInfo(this.input, replace.end));
+        return replace;
+      }
+    }
+  }
+};
+
+lp.resetTo = function (pos) {
+  this.toks.pos = pos;
+  var ch = this.input.charAt(pos - 1);
+  this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) || /[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos));
+
+  if (this.options.locations) {
+    this.toks.curLine = 1;
+    this.toks.lineStart = lineBreakG.lastIndex = 0;
+    var match = undefined;
+    while ((match = lineBreakG.exec(this.input)) && match.index < pos) {
+      ++this.toks.curLine;
+      this.toks.lineStart = match.index + match[0].length;
+    }
+  }
+};
+
+lp.lookAhead = function (n) {
+  while (n > this.ahead.length) this.ahead.push(this.readToken());
+  return this.ahead[n - 1];
+};
+
+},{"..":2,"./state":5}]},{},[1])(1)
+});

+ 344 - 0
editor/js/libs/acorn/walk.js

@@ -0,0 +1,344 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.acorn || (g.acorn = {})).walk = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+"use strict";
+
+var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
+
+// AST walker module for Mozilla Parser API compatible trees
+
+// A simple walk is one where you simply specify callbacks to be
+// called on specific nodes. The last two arguments are optional. A
+// simple use would be
+//
+//     walk.simple(myTree, {
+//         Expression: function(node) { ... }
+//     });
+//
+// to do something with all expressions. All Parser API node types
+// can be used to identify node types, as well as Expression,
+// Statement, and ScopeBody, which denote categories of nodes.
+//
+// The base argument can be used to pass a custom (recursive)
+// walker, and state can be used to give this walked an initial
+// state.
+
+exports.simple = simple;
+
+// An ancestor walk builds up an array of ancestor nodes (including
+// the current node) and passes them to the callback as the state parameter.
+exports.ancestor = ancestor;
+
+// A recursive walk is one where your functions override the default
+// walkers. They can modify and replace the state parameter that's
+// threaded through the walk, and can opt how and whether to walk
+// their child nodes (by calling their third argument on these
+// nodes).
+exports.recursive = recursive;
+
+// Find a node with a given start, end, and type (all are optional,
+// null can be used as wildcard). Returns a {node, state} object, or
+// undefined when it doesn't find a matching node.
+exports.findNodeAt = findNodeAt;
+
+// Find the innermost node of a given type that contains the given
+// position. Interface similar to findNodeAt.
+exports.findNodeAround = findNodeAround;
+
+// Find the outermost matching node after a given position.
+exports.findNodeAfter = findNodeAfter;
+
+// Find the outermost matching node before a given position.
+exports.findNodeBefore = findNodeBefore;
+
+// Used to create a custom walker. Will fill in all missing node
+// type properties with the defaults.
+exports.make = make;
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+function simple(node, visitors, base, state) {
+  if (!base) base = exports.base;(function c(node, st, override) {
+    var type = override || node.type,
+        found = visitors[type];
+    base[type](node, st, c);
+    if (found) found(node, st);
+  })(node, state);
+}
+
+function ancestor(node, visitors, base, state) {
+  if (!base) base = exports.base;
+  if (!state) state = [];(function c(node, st, override) {
+    var type = override || node.type,
+        found = visitors[type];
+    if (node != st[st.length - 1]) {
+      st = st.slice();
+      st.push(node);
+    }
+    base[type](node, st, c);
+    if (found) found(node, st);
+  })(node, state);
+}
+
+function recursive(node, state, funcs, base) {
+  var visitor = funcs ? exports.make(funcs, base) : base;(function c(node, st, override) {
+    visitor[override || node.type](node, st, c);
+  })(node, state);
+}
+
+function makeTest(test) {
+  if (typeof test == "string") {
+    return function (type) {
+      return type == test;
+    };
+  } else if (!test) {
+    return function () {
+      return true;
+    };
+  } else {
+    return test;
+  }
+}
+
+var Found = function Found(node, state) {
+  _classCallCheck(this, Found);
+
+  this.node = node;this.state = state;
+};
+
+function findNodeAt(node, start, end, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  try {
+    ;(function c(node, st, override) {
+      var type = override || node.type;
+      if ((start == null || node.start <= start) && (end == null || node.end >= end)) base[type](node, st, c);
+      if (test(type, node) && (start == null || node.start == start) && (end == null || node.end == end)) throw new Found(node, st);
+    })(node, state);
+  } catch (e) {
+    if (e instanceof Found) {
+      return e;
+    }throw e;
+  }
+}
+
+function findNodeAround(node, pos, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  try {
+    ;(function c(node, st, override) {
+      var type = override || node.type;
+      if (node.start > pos || node.end < pos) {
+        return;
+      }base[type](node, st, c);
+      if (test(type, node)) throw new Found(node, st);
+    })(node, state);
+  } catch (e) {
+    if (e instanceof Found) {
+      return e;
+    }throw e;
+  }
+}
+
+function findNodeAfter(node, pos, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  try {
+    ;(function c(node, st, override) {
+      if (node.end < pos) {
+        return;
+      }var type = override || node.type;
+      if (node.start >= pos && test(type, node)) throw new Found(node, st);
+      base[type](node, st, c);
+    })(node, state);
+  } catch (e) {
+    if (e instanceof Found) {
+      return e;
+    }throw e;
+  }
+}
+
+function findNodeBefore(node, pos, test, base, state) {
+  test = makeTest(test);
+  if (!base) base = exports.base;
+  var max = undefined;(function c(node, st, override) {
+    if (node.start > pos) {
+      return;
+    }var type = override || node.type;
+    if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node)) max = new Found(node, st);
+    base[type](node, st, c);
+  })(node, state);
+  return max;
+}
+
+function make(funcs, base) {
+  if (!base) base = exports.base;
+  var visitor = {};
+  for (var type in base) visitor[type] = base[type];
+  for (var type in funcs) visitor[type] = funcs[type];
+  return visitor;
+}
+
+function skipThrough(node, st, c) {
+  c(node, st);
+}
+function ignore(_node, _st, _c) {}
+
+// Node walkers.
+
+var base = {};
+
+exports.base = base;
+base.Program = base.BlockStatement = function (node, st, c) {
+  for (var i = 0; i < node.body.length; ++i) {
+    c(node.body[i], st, "Statement");
+  }
+};
+base.Statement = skipThrough;
+base.EmptyStatement = ignore;
+base.ExpressionStatement = base.ParenthesizedExpression = function (node, st, c) {
+  return c(node.expression, st, "Expression");
+};
+base.IfStatement = function (node, st, c) {
+  c(node.test, st, "Expression");
+  c(node.consequent, st, "Statement");
+  if (node.alternate) c(node.alternate, st, "Statement");
+};
+base.LabeledStatement = function (node, st, c) {
+  return c(node.body, st, "Statement");
+};
+base.BreakStatement = base.ContinueStatement = ignore;
+base.WithStatement = function (node, st, c) {
+  c(node.object, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.SwitchStatement = function (node, st, c) {
+  c(node.discriminant, st, "Expression");
+  for (var i = 0; i < node.cases.length; ++i) {
+    var cs = node.cases[i];
+    if (cs.test) c(cs.test, st, "Expression");
+    for (var j = 0; j < cs.consequent.length; ++j) {
+      c(cs.consequent[j], st, "Statement");
+    }
+  }
+};
+base.ReturnStatement = base.YieldExpression = function (node, st, c) {
+  if (node.argument) c(node.argument, st, "Expression");
+};
+base.ThrowStatement = base.SpreadElement = base.RestElement = function (node, st, c) {
+  return c(node.argument, st, "Expression");
+};
+base.TryStatement = function (node, st, c) {
+  c(node.block, st, "Statement");
+  if (node.handler) c(node.handler.body, st, "ScopeBody");
+  if (node.finalizer) c(node.finalizer, st, "Statement");
+};
+base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
+  c(node.test, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.ForStatement = function (node, st, c) {
+  if (node.init) c(node.init, st, "ForInit");
+  if (node.test) c(node.test, st, "Expression");
+  if (node.update) c(node.update, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.ForInStatement = base.ForOfStatement = function (node, st, c) {
+  c(node.left, st, "ForInit");
+  c(node.right, st, "Expression");
+  c(node.body, st, "Statement");
+};
+base.ForInit = function (node, st, c) {
+  if (node.type == "VariableDeclaration") c(node, st);else c(node, st, "Expression");
+};
+base.DebuggerStatement = ignore;
+
+base.FunctionDeclaration = function (node, st, c) {
+  return c(node, st, "Function");
+};
+base.VariableDeclaration = function (node, st, c) {
+  for (var i = 0; i < node.declarations.length; ++i) {
+    var decl = node.declarations[i];
+    if (decl.init) c(decl.init, st, "Expression");
+  }
+};
+
+base.Function = function (node, st, c) {
+  return c(node.body, st, "ScopeBody");
+};
+base.ScopeBody = function (node, st, c) {
+  return c(node, st, "Statement");
+};
+
+base.Expression = skipThrough;
+base.ThisExpression = base.Super = base.MetaProperty = ignore;
+base.ArrayExpression = base.ArrayPattern = function (node, st, c) {
+  for (var i = 0; i < node.elements.length; ++i) {
+    var elt = node.elements[i];
+    if (elt) c(elt, st, "Expression");
+  }
+};
+base.ObjectExpression = base.ObjectPattern = function (node, st, c) {
+  for (var i = 0; i < node.properties.length; ++i) {
+    c(node.properties[i], st);
+  }
+};
+base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
+base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
+  for (var i = 0; i < node.expressions.length; ++i) {
+    c(node.expressions[i], st, "Expression");
+  }
+};
+base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
+  c(node.argument, st, "Expression");
+};
+base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = function (node, st, c) {
+  c(node.left, st, "Expression");
+  c(node.right, st, "Expression");
+};
+base.ConditionalExpression = function (node, st, c) {
+  c(node.test, st, "Expression");
+  c(node.consequent, st, "Expression");
+  c(node.alternate, st, "Expression");
+};
+base.NewExpression = base.CallExpression = function (node, st, c) {
+  c(node.callee, st, "Expression");
+  if (node.arguments) for (var i = 0; i < node.arguments.length; ++i) {
+    c(node.arguments[i], st, "Expression");
+  }
+};
+base.MemberExpression = function (node, st, c) {
+  c(node.object, st, "Expression");
+  if (node.computed) c(node.property, st, "Expression");
+};
+base.ExportDeclaration = function (node, st, c) {
+  return c(node.declaration, st);
+};
+base.ImportDeclaration = function (node, st, c) {
+  for (var i = 0; i < node.specifiers.length; i++) {
+    c(node.specifiers[i], st);
+  }
+};
+base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore;
+
+base.TaggedTemplateExpression = function (node, st, c) {
+  c(node.tag, st, "Expression");
+  c(node.quasi, st);
+};
+base.ClassDeclaration = base.ClassExpression = function (node, st, c) {
+  if (node.superClass) c(node.superClass, st, "Expression");
+  for (var i = 0; i < node.body.body.length; i++) {
+    c(node.body.body[i], st);
+  }
+};
+base.MethodDefinition = base.Property = function (node, st, c) {
+  if (node.computed) c(node.key, st, "Expression");
+  c(node.value, st, "Expression");
+};
+base.ComprehensionExpression = function (node, st, c) {
+  for (var i = 0; i < node.blocks.length; i++) {
+    c(node.blocks[i].right, st, "Expression");
+  }c(node.body, st, "Expression");
+};
+
+},{}]},{},[1])(1)
+});

+ 13 - 6
editor/js/libs/app.js

@@ -11,7 +11,7 @@ var APP = {
 		var loader = new THREE.ObjectLoader();
 		var camera, scene, renderer;
 
-		var vr, controls;
+		var vr, controls, effect;
 
 		var events = {};
 
@@ -89,7 +89,7 @@ var APP = {
 				if ( camera.parent === undefined ) {
 
 					// camera needs to be in the scene so camera2 matrix updates
-					
+
 					scene.add( camera );
 
 				}
@@ -100,7 +100,7 @@ var APP = {
 				camera = camera2;
 
 				controls = new THREE.VRControls( camera );
-				renderer = new THREE.VREffect( renderer );
+				effect = new THREE.VREffect( renderer );
 
 				document.addEventListener( 'keyup', function ( event ) {
 
@@ -114,7 +114,7 @@ var APP = {
 
 				this.dom.addEventListener( 'dblclick', function () {
 
-					renderer.setFullScreen( true );
+					effect.setFullScreen( true );
 
 				} );
 
@@ -160,9 +160,16 @@ var APP = {
 
 			dispatch( events.update, { time: time, delta: time - prevTime } );
 
-			if ( vr ) controls.update();
+			if ( vr === true ) {
+
+				controls.update();
+				effect.render( scene, camera );
 
-			renderer.render( scene, camera );
+			} else {
+
+				renderer.render( scene, camera );
+
+			}
 
 			prevTime = time;
 

+ 32 - 0
editor/js/libs/codemirror/addon/dialog.css

@@ -0,0 +1,32 @@
+.CodeMirror-dialog {
+  position: absolute;
+  left: 0; right: 0;
+  background: inherit;
+  z-index: 15;
+  padding: .1em .8em;
+  overflow: hidden;
+  color: inherit;
+}
+
+.CodeMirror-dialog-top {
+  border-bottom: 1px solid #eee;
+  top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+  border-top: 1px solid #eee;
+  bottom: 0;
+}
+
+.CodeMirror-dialog input {
+  border: none;
+  outline: none;
+  background: transparent;
+  width: 20em;
+  color: inherit;
+  font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+  font-size: 70%;
+}

+ 157 - 0
editor/js/libs/codemirror/addon/dialog.js

@@ -0,0 +1,157 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  function dialogDiv(cm, template, bottom) {
+    var wrap = cm.getWrapperElement();
+    var dialog;
+    dialog = wrap.appendChild(document.createElement("div"));
+    if (bottom)
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
+    else
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
+
+    if (typeof template == "string") {
+      dialog.innerHTML = template;
+    } else { // Assuming it's a detached DOM element.
+      dialog.appendChild(template);
+    }
+    return dialog;
+  }
+
+  function closeNotification(cm, newVal) {
+    if (cm.state.currentNotificationClose)
+      cm.state.currentNotificationClose();
+    cm.state.currentNotificationClose = newVal;
+  }
+
+  CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+    if (!options) options = {};
+
+    closeNotification(this, null);
+
+    var dialog = dialogDiv(this, template, options.bottom);
+    var closed = false, me = this;
+    function close(newVal) {
+      if (typeof newVal == 'string') {
+        inp.value = newVal;
+      } else {
+        if (closed) return;
+        closed = true;
+        dialog.parentNode.removeChild(dialog);
+        me.focus();
+
+        if (options.onClose) options.onClose(dialog);
+      }
+    }
+
+    var inp = dialog.getElementsByTagName("input")[0], button;
+    if (inp) {
+      if (options.value) {
+        inp.value = options.value;
+        if (options.selectValueOnOpen !== false) {
+          inp.select();
+        }
+      }
+
+      if (options.onInput)
+        CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
+      if (options.onKeyUp)
+        CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+
+      CodeMirror.on(inp, "keydown", function(e) {
+        if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
+        if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
+          inp.blur();
+          CodeMirror.e_stop(e);
+          close();
+        }
+        if (e.keyCode == 13) callback(inp.value, e);
+      });
+
+      if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
+
+      inp.focus();
+    } else if (button = dialog.getElementsByTagName("button")[0]) {
+      CodeMirror.on(button, "click", function() {
+        close();
+        me.focus();
+      });
+
+      if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
+
+      button.focus();
+    }
+    return close;
+  });
+
+  CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
+    closeNotification(this, null);
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var buttons = dialog.getElementsByTagName("button");
+    var closed = false, me = this, blurring = 1;
+    function close() {
+      if (closed) return;
+      closed = true;
+      dialog.parentNode.removeChild(dialog);
+      me.focus();
+    }
+    buttons[0].focus();
+    for (var i = 0; i < buttons.length; ++i) {
+      var b = buttons[i];
+      (function(callback) {
+        CodeMirror.on(b, "click", function(e) {
+          CodeMirror.e_preventDefault(e);
+          close();
+          if (callback) callback(me);
+        });
+      })(callbacks[i]);
+      CodeMirror.on(b, "blur", function() {
+        --blurring;
+        setTimeout(function() { if (blurring <= 0) close(); }, 200);
+      });
+      CodeMirror.on(b, "focus", function() { ++blurring; });
+    }
+  });
+
+  /*
+   * openNotification
+   * Opens a notification, that can be closed with an optional timer
+   * (default 5000ms timer) and always closes on click.
+   *
+   * If a notification is opened while another is opened, it will close the
+   * currently opened one and open the new one immediately.
+   */
+  CodeMirror.defineExtension("openNotification", function(template, options) {
+    closeNotification(this, close);
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var closed = false, doneTimer;
+    var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
+
+    function close() {
+      if (closed) return;
+      closed = true;
+      clearTimeout(doneTimer);
+      dialog.parentNode.removeChild(dialog);
+    }
+
+    CodeMirror.on(dialog, 'click', function(e) {
+      CodeMirror.e_preventDefault(e);
+      close();
+    });
+
+    if (duration)
+      doneTimer = setTimeout(close, duration);
+
+    return close;
+  });
+});

+ 38 - 0
editor/js/libs/codemirror/addon/show-hint.css

@@ -0,0 +1,38 @@
+.CodeMirror-hints {
+  position: absolute;
+  z-index: 10;
+  overflow: hidden;
+  list-style: none;
+
+  margin: 0;
+  padding: 2px;
+
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  border-radius: 3px;
+  border: 1px solid silver;
+
+  background: white;
+  font-size: 90%;
+  font-family: monospace;
+
+  max-height: 20em;
+  overflow-y: auto;
+}
+
+.CodeMirror-hint {
+  margin: 0;
+  padding: 0 4px;
+  border-radius: 2px;
+  max-width: 19em;
+  overflow: hidden;
+  white-space: pre;
+  color: black;
+  cursor: pointer;
+}
+
+li.CodeMirror-hint-active {
+  background: #08f;
+  color: white;
+}

+ 383 - 0
editor/js/libs/codemirror/addon/show-hint.js

@@ -0,0 +1,383 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
+  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
+
+  // This is the old interface, kept around for now to stay
+  // backwards-compatible.
+  CodeMirror.showHint = function(cm, getHints, options) {
+    if (!getHints) return cm.showHint(options);
+    if (options && options.async) getHints.async = true;
+    var newOpts = {hint: getHints};
+    if (options) for (var prop in options) newOpts[prop] = options[prop];
+    return cm.showHint(newOpts);
+  };
+
+  CodeMirror.defineExtension("showHint", function(options) {
+    // We want a single cursor position.
+    if (this.listSelections().length > 1 || this.somethingSelected()) return;
+
+    if (this.state.completionActive) this.state.completionActive.close();
+    var completion = this.state.completionActive = new Completion(this, options);
+    if (!completion.options.hint) return;
+
+    CodeMirror.signal(this, "startCompletion", this);
+    completion.update(true);
+  });
+
+  function Completion(cm, options) {
+    this.cm = cm;
+    this.options = this.buildOptions(options);
+    this.widget = null;
+    this.debounce = 0;
+    this.tick = 0;
+    this.startPos = this.cm.getCursor();
+    this.startLen = this.cm.getLine(this.startPos.line).length;
+
+    var self = this;
+    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
+  }
+
+  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
+    return setTimeout(fn, 1000/60);
+  };
+  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
+
+  Completion.prototype = {
+    close: function() {
+      if (!this.active()) return;
+      this.cm.state.completionActive = null;
+      this.tick = null;
+      this.cm.off("cursorActivity", this.activityFunc);
+
+      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
+      if (this.widget) this.widget.close();
+      CodeMirror.signal(this.cm, "endCompletion", this.cm);
+    },
+
+    active: function() {
+      return this.cm.state.completionActive == this;
+    },
+
+    pick: function(data, i) {
+      var completion = data.list[i];
+      if (completion.hint) completion.hint(this.cm, data, completion);
+      else this.cm.replaceRange(getText(completion), completion.from || data.from,
+                                completion.to || data.to, "complete");
+      CodeMirror.signal(data, "pick", completion);
+      this.close();
+    },
+
+    cursorActivity: function() {
+      if (this.debounce) {
+        cancelAnimationFrame(this.debounce);
+        this.debounce = 0;
+      }
+
+      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
+      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
+          pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
+          (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
+        this.close();
+      } else {
+        var self = this;
+        this.debounce = requestAnimationFrame(function() {self.update();});
+        if (this.widget) this.widget.disable();
+      }
+    },
+
+    update: function(first) {
+      if (this.tick == null) return;
+      if (this.data) CodeMirror.signal(this.data, "update");
+      if (!this.options.hint.async) {
+        this.finishUpdate(this.options.hint(this.cm, this.options), first);
+      } else {
+        var myTick = ++this.tick, self = this;
+        this.options.hint(this.cm, function(data) {
+          if (self.tick == myTick) self.finishUpdate(data, first);
+        }, this.options);
+      }
+    },
+
+    finishUpdate: function(data, first) {
+      this.data = data;
+
+      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
+      if (this.widget) this.widget.close();
+      if (data && data.list.length) {
+        if (picked && data.list.length == 1) {
+          this.pick(data, 0);
+        } else {
+          this.widget = new Widget(this, data);
+          CodeMirror.signal(data, "shown");
+        }
+      }
+    },
+
+    buildOptions: function(options) {
+      var editor = this.cm.options.hintOptions;
+      var out = {};
+      for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
+      if (editor) for (var prop in editor)
+        if (editor[prop] !== undefined) out[prop] = editor[prop];
+      if (options) for (var prop in options)
+        if (options[prop] !== undefined) out[prop] = options[prop];
+      return out;
+    }
+  };
+
+  function getText(completion) {
+    if (typeof completion == "string") return completion;
+    else return completion.text;
+  }
+
+  function buildKeyMap(completion, handle) {
+    var baseMap = {
+      Up: function() {handle.moveFocus(-1);},
+      Down: function() {handle.moveFocus(1);},
+      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
+      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
+      Home: function() {handle.setFocus(0);},
+      End: function() {handle.setFocus(handle.length - 1);},
+      Enter: handle.pick,
+      Tab: handle.pick,
+      Esc: handle.close
+    };
+    var custom = completion.options.customKeys;
+    var ourMap = custom ? {} : baseMap;
+    function addBinding(key, val) {
+      var bound;
+      if (typeof val != "string")
+        bound = function(cm) { return val(cm, handle); };
+      // This mechanism is deprecated
+      else if (baseMap.hasOwnProperty(val))
+        bound = baseMap[val];
+      else
+        bound = val;
+      ourMap[key] = bound;
+    }
+    if (custom)
+      for (var key in custom) if (custom.hasOwnProperty(key))
+        addBinding(key, custom[key]);
+    var extra = completion.options.extraKeys;
+    if (extra)
+      for (var key in extra) if (extra.hasOwnProperty(key))
+        addBinding(key, extra[key]);
+    return ourMap;
+  }
+
+  function getHintElement(hintsElement, el) {
+    while (el && el != hintsElement) {
+      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
+      el = el.parentNode;
+    }
+  }
+
+  function Widget(completion, data) {
+    this.completion = completion;
+    this.data = data;
+    this.picked = false;
+    var widget = this, cm = completion.cm;
+
+    var hints = this.hints = document.createElement("ul");
+    hints.className = "CodeMirror-hints";
+    this.selectedHint = data.selectedHint || 0;
+
+    var completions = data.list;
+    for (var i = 0; i < completions.length; ++i) {
+      var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
+      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
+      if (cur.className != null) className = cur.className + " " + className;
+      elt.className = className;
+      if (cur.render) cur.render(elt, data, cur);
+      else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
+      elt.hintId = i;
+    }
+
+    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
+    var left = pos.left, top = pos.bottom, below = true;
+    hints.style.left = left + "px";
+    hints.style.top = top + "px";
+    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+    var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
+    (completion.options.container || document.body).appendChild(hints);
+    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
+    if (overlapY > 0) {
+      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
+      if (curTop - height > 0) { // Fits above cursor
+        hints.style.top = (top = pos.top - height) + "px";
+        below = false;
+      } else if (height > winH) {
+        hints.style.height = (winH - 5) + "px";
+        hints.style.top = (top = pos.bottom - box.top) + "px";
+        var cursor = cm.getCursor();
+        if (data.from.ch != cursor.ch) {
+          pos = cm.cursorCoords(cursor);
+          hints.style.left = (left = pos.left) + "px";
+          box = hints.getBoundingClientRect();
+        }
+      }
+    }
+    var overlapX = box.right - winW;
+    if (overlapX > 0) {
+      if (box.right - box.left > winW) {
+        hints.style.width = (winW - 5) + "px";
+        overlapX -= (box.right - box.left) - winW;
+      }
+      hints.style.left = (left = pos.left - overlapX) + "px";
+    }
+
+    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
+      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
+      setFocus: function(n) { widget.changeActive(n); },
+      menuSize: function() { return widget.screenAmount(); },
+      length: completions.length,
+      close: function() { completion.close(); },
+      pick: function() { widget.pick(); },
+      data: data
+    }));
+
+    if (completion.options.closeOnUnfocus) {
+      var closingOnBlur;
+      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
+      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
+    }
+
+    var startScroll = cm.getScrollInfo();
+    cm.on("scroll", this.onScroll = function() {
+      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
+      var newTop = top + startScroll.top - curScroll.top;
+      var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+      if (!below) point += hints.offsetHeight;
+      if (point <= editor.top || point >= editor.bottom) return completion.close();
+      hints.style.top = newTop + "px";
+      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
+    });
+
+    CodeMirror.on(hints, "dblclick", function(e) {
+      var t = getHintElement(hints, e.target || e.srcElement);
+      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
+    });
+
+    CodeMirror.on(hints, "click", function(e) {
+      var t = getHintElement(hints, e.target || e.srcElement);
+      if (t && t.hintId != null) {
+        widget.changeActive(t.hintId);
+        if (completion.options.completeOnSingleClick) widget.pick();
+      }
+    });
+
+    CodeMirror.on(hints, "mousedown", function() {
+      setTimeout(function(){cm.focus();}, 20);
+    });
+
+    CodeMirror.signal(data, "select", completions[0], hints.firstChild);
+    return true;
+  }
+
+  Widget.prototype = {
+    close: function() {
+      if (this.completion.widget != this) return;
+      this.completion.widget = null;
+      this.hints.parentNode.removeChild(this.hints);
+      this.completion.cm.removeKeyMap(this.keyMap);
+
+      var cm = this.completion.cm;
+      if (this.completion.options.closeOnUnfocus) {
+        cm.off("blur", this.onBlur);
+        cm.off("focus", this.onFocus);
+      }
+      cm.off("scroll", this.onScroll);
+    },
+
+    disable: function() {
+      this.completion.cm.removeKeyMap(this.keyMap);
+      var widget = this;
+      this.keyMap = {Enter: function() { widget.picked = true; }};
+      this.completion.cm.addKeyMap(this.keyMap);
+    },
+
+    pick: function() {
+      this.completion.pick(this.data, this.selectedHint);
+    },
+
+    changeActive: function(i, avoidWrap) {
+      if (i >= this.data.list.length)
+        i = avoidWrap ? this.data.list.length - 1 : 0;
+      else if (i < 0)
+        i = avoidWrap ? 0  : this.data.list.length - 1;
+      if (this.selectedHint == i) return;
+      var node = this.hints.childNodes[this.selectedHint];
+      node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
+      node = this.hints.childNodes[this.selectedHint = i];
+      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
+      if (node.offsetTop < this.hints.scrollTop)
+        this.hints.scrollTop = node.offsetTop - 3;
+      else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
+        this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
+      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
+    },
+
+    screenAmount: function() {
+      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
+    }
+  };
+
+  CodeMirror.registerHelper("hint", "auto", function(cm, options) {
+    var helpers = cm.getHelpers(cm.getCursor(), "hint"), words;
+    if (helpers.length) {
+      for (var i = 0; i < helpers.length; i++) {
+        var cur = helpers[i](cm, options);
+        if (cur && cur.list.length) return cur;
+      }
+    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
+      if (words) return CodeMirror.hint.fromList(cm, {words: words});
+    } else if (CodeMirror.hint.anyword) {
+      return CodeMirror.hint.anyword(cm, options);
+    }
+  });
+
+  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
+    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+    var found = [];
+    for (var i = 0; i < options.words.length; i++) {
+      var word = options.words[i];
+      if (word.slice(0, token.string.length) == token.string)
+        found.push(word);
+    }
+
+    if (found.length) return {
+      list: found,
+      from: CodeMirror.Pos(cur.line, token.start),
+            to: CodeMirror.Pos(cur.line, token.end)
+    };
+  });
+
+  CodeMirror.commands.autocomplete = CodeMirror.showHint;
+
+  var defaultOptions = {
+    hint: CodeMirror.hint.auto,
+    completeSingle: true,
+    alignWithWord: true,
+    closeCharacters: /[\s()\[\]{};:>,]/,
+    closeOnUnfocus: true,
+    completeOnSingleClick: false,
+    container: null,
+    customKeys: null,
+    extraKeys: null
+  };
+
+  CodeMirror.defineOption("hintOptions", null);
+});

+ 86 - 0
editor/js/libs/codemirror/addon/tern.css

@@ -0,0 +1,86 @@
+.CodeMirror-Tern-completion {
+  padding-left: 22px;
+  position: relative;
+}
+.CodeMirror-Tern-completion:before {
+  position: absolute;
+  left: 2px;
+  bottom: 2px;
+  border-radius: 50%;
+  font-size: 12px;
+  font-weight: bold;
+  height: 15px;
+  width: 15px;
+  line-height: 16px;
+  text-align: center;
+  color: white;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.CodeMirror-Tern-completion-unknown:before {
+  content: "?";
+  background: #4bb;
+}
+.CodeMirror-Tern-completion-object:before {
+  content: "O";
+  background: #77c;
+}
+.CodeMirror-Tern-completion-fn:before {
+  content: "F";
+  background: #7c7;
+}
+.CodeMirror-Tern-completion-array:before {
+  content: "A";
+  background: #c66;
+}
+.CodeMirror-Tern-completion-number:before {
+  content: "1";
+  background: #999;
+}
+.CodeMirror-Tern-completion-string:before {
+  content: "S";
+  background: #999;
+}
+.CodeMirror-Tern-completion-bool:before {
+  content: "B";
+  background: #999;
+}
+
+.CodeMirror-Tern-completion-guess {
+  color: #999;
+}
+
+.CodeMirror-Tern-tooltip {
+  border: 1px solid silver;
+  border-radius: 3px;
+  color: #444;
+  padding: 2px 5px;
+  font-size: 90%;
+  font-family: monospace;
+  background-color: white;
+  white-space: pre-wrap;
+
+  max-width: 40em;
+  position: absolute;
+  z-index: 10;
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+
+  transition: opacity 1s;
+  -moz-transition: opacity 1s;
+  -webkit-transition: opacity 1s;
+  -o-transition: opacity 1s;
+  -ms-transition: opacity 1s;
+}
+
+.CodeMirror-Tern-hint-doc {
+  max-width: 25em;
+  margin-top: -3px;
+}
+
+.CodeMirror-Tern-fname { color: black; }
+.CodeMirror-Tern-farg { color: #70a; }
+.CodeMirror-Tern-farg-current { text-decoration: underline; }
+.CodeMirror-Tern-type { color: #07c; }
+.CodeMirror-Tern-fhint-guess { opacity: .7; }

+ 698 - 0
editor/js/libs/codemirror/addon/tern.js

@@ -0,0 +1,698 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// Glue code between CodeMirror and Tern.
+//
+// Create a CodeMirror.TernServer to wrap an actual Tern server,
+// register open documents (CodeMirror.Doc instances) with it, and
+// call its methods to activate the assisting functions that Tern
+// provides.
+//
+// Options supported (all optional):
+// * defs: An array of JSON definition data structures.
+// * plugins: An object mapping plugin names to configuration
+//   options.
+// * getFile: A function(name, c) that can be used to access files in
+//   the project that haven't been loaded yet. Simply do c(null) to
+//   indicate that a file is not available.
+// * fileFilter: A function(value, docName, doc) that will be applied
+//   to documents before passing them on to Tern.
+// * switchToDoc: A function(name, doc) that should, when providing a
+//   multi-file view, switch the view or focus to the named file.
+// * showError: A function(editor, message) that can be used to
+//   override the way errors are displayed.
+// * completionTip: Customize the content in tooltips for completions.
+//   Is passed a single argument—the completion's data as returned by
+//   Tern—and may return a string, DOM node, or null to indicate that
+//   no tip should be shown. By default the docstring is shown.
+// * typeTip: Like completionTip, but for the tooltips shown for type
+//   queries.
+// * responseFilter: A function(doc, query, request, error, data) that
+//   will be applied to the Tern responses before treating them
+// * caseInsensitive: boolean to send case insensitive querys to tern
+//
+//
+// It is possible to run the Tern server in a web worker by specifying
+// these additional options:
+// * useWorker: Set to true to enable web worker mode. You'll probably
+//   want to feature detect the actual value you use here, for example
+//   !!window.Worker.
+// * workerScript: The main script of the worker. Point this to
+//   wherever you are hosting worker.js from this directory.
+// * workerDeps: An array of paths pointing (relative to workerScript)
+//   to the Acorn and Tern libraries and any Tern plugins you want to
+//   load. Or, if you minified those into a single script and included
+//   them in the workerScript, simply leave this undefined.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  // declare global: tern
+
+  CodeMirror.TernServer = function(options) {
+    var self = this;
+    this.options = options || {};
+    var plugins = this.options.plugins || (this.options.plugins = {});
+    if (!plugins.doc_comment) plugins.doc_comment = true;
+    if (this.options.useWorker) {
+      this.server = new WorkerServer(this);
+    } else {
+      this.server = new tern.Server({
+        getFile: function(name, c) { return getFile(self, name, c); },
+        async: true,
+        defs: this.options.defs || [],
+        plugins: plugins
+      });
+    }
+    this.docs = Object.create(null);
+    this.trackChange = function(doc, change) { trackChange(self, doc, change); };
+
+    this.cachedArgHints = null;
+    this.activeArgHints = null;
+    this.jumpStack = [];
+
+    this.getHint = function(cm, c) { return hint(self, cm, c); };
+    this.getHint.async = true;
+  };
+
+  CodeMirror.TernServer.prototype = {
+    addDoc: function(name, doc) {
+      var data = {doc: doc, name: name, changed: null};
+      this.server.addFile(name, docValue(this, data));
+      CodeMirror.on(doc, "change", this.trackChange);
+      return this.docs[name] = data;
+    },
+
+    delDoc: function(id) {
+      var found = resolveDoc(this, id);
+      if (!found) return;
+      CodeMirror.off(found.doc, "change", this.trackChange);
+      delete this.docs[found.name];
+      this.server.delFile(found.name);
+    },
+
+    hideDoc: function(id) {
+      closeArgHints(this);
+      var found = resolveDoc(this, id);
+      if (found && found.changed) sendDoc(this, found);
+    },
+
+    complete: function(cm) {
+      cm.showHint({hint: this.getHint});
+    },
+
+    showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
+
+    showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
+
+    updateArgHints: function(cm) { updateArgHints(this, cm); },
+
+    jumpToDef: function(cm) { jumpToDef(this, cm); },
+
+    jumpBack: function(cm) { jumpBack(this, cm); },
+
+    rename: function(cm) { rename(this, cm); },
+
+    selectName: function(cm) { selectName(this, cm); },
+
+    request: function (cm, query, c, pos) {
+      var self = this;
+      var doc = findDoc(this, cm.getDoc());
+      var request = buildRequest(this, doc, query, pos);
+
+      this.server.request(request, function (error, data) {
+        if (!error && self.options.responseFilter)
+          data = self.options.responseFilter(doc, query, request, error, data);
+        c(error, data);
+      });
+    },
+
+    destroy: function () {
+      if (this.worker) {
+        this.worker.terminate();
+        this.worker = null;
+      }
+    }
+  };
+
+  var Pos = CodeMirror.Pos;
+  var cls = "CodeMirror-Tern-";
+  var bigDoc = 250;
+
+  function getFile(ts, name, c) {
+    var buf = ts.docs[name];
+    if (buf)
+      c(docValue(ts, buf));
+    else if (ts.options.getFile)
+      ts.options.getFile(name, c);
+    else
+      c(null);
+  }
+
+  function findDoc(ts, doc, name) {
+    for (var n in ts.docs) {
+      var cur = ts.docs[n];
+      if (cur.doc == doc) return cur;
+    }
+    if (!name) for (var i = 0;; ++i) {
+      n = "[doc" + (i || "") + "]";
+      if (!ts.docs[n]) { name = n; break; }
+    }
+    return ts.addDoc(name, doc);
+  }
+
+  function resolveDoc(ts, id) {
+    if (typeof id == "string") return ts.docs[id];
+    if (id instanceof CodeMirror) id = id.getDoc();
+    if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
+  }
+
+  function trackChange(ts, doc, change) {
+    var data = findDoc(ts, doc);
+
+    var argHints = ts.cachedArgHints;
+    if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0)
+      ts.cachedArgHints = null;
+
+    var changed = data.changed;
+    if (changed == null)
+      data.changed = changed = {from: change.from.line, to: change.from.line};
+    var end = change.from.line + (change.text.length - 1);
+    if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
+    if (end >= changed.to) changed.to = end + 1;
+    if (changed.from > change.from.line) changed.from = change.from.line;
+
+    if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
+      if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
+    }, 200);
+  }
+
+  function sendDoc(ts, doc) {
+    ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
+      if (error) window.console.error(error);
+      else doc.changed = null;
+    });
+  }
+
+  // Completion
+
+  function hint(ts, cm, c) {
+    ts.request(cm, {type: "completions", types: true, docs: true, urls: true, caseInsensitive: ts.options.caseInsensitive}, function(error, data) {
+      if (error) return showError(ts, cm, error);
+      var completions = [], after = "";
+      var from = data.start, to = data.end;
+      if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
+          cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
+        after = "\"]";
+
+      for (var i = 0; i < data.completions.length; ++i) {
+        var completion = data.completions[i], className = typeToIcon(completion.type);
+        if (data.guess) className += " " + cls + "guess";
+        completions.push({text: completion.name + after,
+                          displayText: completion.name,
+                          className: className,
+                          data: completion});
+      }
+
+      var obj = {from: from, to: to, list: completions};
+      var tooltip = null;
+      CodeMirror.on(obj, "close", function() { remove(tooltip); });
+      CodeMirror.on(obj, "update", function() { remove(tooltip); });
+      CodeMirror.on(obj, "select", function(cur, node) {
+        remove(tooltip);
+        var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
+        if (content) {
+          tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
+                                node.getBoundingClientRect().top + window.pageYOffset, content);
+          tooltip.className += " " + cls + "hint-doc";
+        }
+      });
+      c(obj);
+    });
+  }
+
+  function typeToIcon(type) {
+    var suffix;
+    if (type == "?") suffix = "unknown";
+    else if (type == "number" || type == "string" || type == "bool") suffix = type;
+    else if (/^fn\(/.test(type)) suffix = "fn";
+    else if (/^\[/.test(type)) suffix = "array";
+    else suffix = "object";
+    return cls + "completion " + cls + "completion-" + suffix;
+  }
+
+  // Type queries
+
+  function showContextInfo(ts, cm, pos, queryName, c) {
+    ts.request(cm, queryName, function(error, data) {
+      if (error) return showError(ts, cm, error);
+      if (ts.options.typeTip) {
+        var tip = ts.options.typeTip(data);
+      } else {
+        var tip = elt("span", null, elt("strong", null, data.type || "not found"));
+        if (data.doc)
+          tip.appendChild(document.createTextNode(" — " + data.doc));
+        if (data.url) {
+          tip.appendChild(document.createTextNode(" "));
+          var child = tip.appendChild(elt("a", null, "[docs]"));
+          child.href = data.url;
+          child.target = "_blank";
+        }
+      }
+      tempTooltip(cm, tip);
+      if (c) c();
+    }, pos);
+  }
+
+  // Maintaining argument hints
+
+  function updateArgHints(ts, cm) {
+    closeArgHints(ts);
+
+    if (cm.somethingSelected()) return;
+    var state = cm.getTokenAt(cm.getCursor()).state;
+    var inner = CodeMirror.innerMode(cm.getMode(), state);
+    if (inner.mode.name != "javascript") return;
+    var lex = inner.state.lexical;
+    if (lex.info != "call") return;
+
+    var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
+    for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
+      var str = cm.getLine(line), extra = 0;
+      for (var pos = 0;;) {
+        var tab = str.indexOf("\t", pos);
+        if (tab == -1) break;
+        extra += tabSize - (tab + extra) % tabSize - 1;
+        pos = tab + 1;
+      }
+      ch = lex.column - extra;
+      if (str.charAt(ch) == "(") {found = true; break;}
+    }
+    if (!found) return;
+
+    var start = Pos(line, ch);
+    var cache = ts.cachedArgHints;
+    if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
+      return showArgHints(ts, cm, argPos);
+
+    ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
+      if (error || !data.type || !(/^fn\(/).test(data.type)) return;
+      ts.cachedArgHints = {
+        start: pos,
+        type: parseFnType(data.type),
+        name: data.exprName || data.name || "fn",
+        guess: data.guess,
+        doc: cm.getDoc()
+      };
+      showArgHints(ts, cm, argPos);
+    });
+  }
+
+  function showArgHints(ts, cm, pos) {
+    closeArgHints(ts);
+
+    var cache = ts.cachedArgHints, tp = cache.type;
+    var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
+                  elt("span", cls + "fname", cache.name), "(");
+    for (var i = 0; i < tp.args.length; ++i) {
+      if (i) tip.appendChild(document.createTextNode(", "));
+      var arg = tp.args[i];
+      tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
+      if (arg.type != "?") {
+        tip.appendChild(document.createTextNode(":\u00a0"));
+        tip.appendChild(elt("span", cls + "type", arg.type));
+      }
+    }
+    tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
+    if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
+    var place = cm.cursorCoords(null, "page");
+    ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip);
+  }
+
+  function parseFnType(text) {
+    var args = [], pos = 3;
+
+    function skipMatching(upto) {
+      var depth = 0, start = pos;
+      for (;;) {
+        var next = text.charAt(pos);
+        if (upto.test(next) && !depth) return text.slice(start, pos);
+        if (/[{\[\(]/.test(next)) ++depth;
+        else if (/[}\]\)]/.test(next)) --depth;
+        ++pos;
+      }
+    }
+
+    // Parse arguments
+    if (text.charAt(pos) != ")") for (;;) {
+      var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
+      if (name) {
+        pos += name[0].length;
+        name = name[1];
+      }
+      args.push({name: name, type: skipMatching(/[\),]/)});
+      if (text.charAt(pos) == ")") break;
+      pos += 2;
+    }
+
+    var rettype = text.slice(pos).match(/^\) -> (.*)$/);
+
+    return {args: args, rettype: rettype && rettype[1]};
+  }
+
+  // Moving to the definition of something
+
+  function jumpToDef(ts, cm) {
+    function inner(varName) {
+      var req = {type: "definition", variable: varName || null};
+      var doc = findDoc(ts, cm.getDoc());
+      ts.server.request(buildRequest(ts, doc, req), function(error, data) {
+        if (error) return showError(ts, cm, error);
+        if (!data.file && data.url) { window.open(data.url); return; }
+
+        if (data.file) {
+          var localDoc = ts.docs[data.file], found;
+          if (localDoc && (found = findContext(localDoc.doc, data))) {
+            ts.jumpStack.push({file: doc.name,
+                               start: cm.getCursor("from"),
+                               end: cm.getCursor("to")});
+            moveTo(ts, doc, localDoc, found.start, found.end);
+            return;
+          }
+        }
+        showError(ts, cm, "Could not find a definition.");
+      });
+    }
+
+    if (!atInterestingExpression(cm))
+      dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
+    else
+      inner();
+  }
+
+  function jumpBack(ts, cm) {
+    var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
+    if (!doc) return;
+    moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
+  }
+
+  function moveTo(ts, curDoc, doc, start, end) {
+    doc.doc.setSelection(start, end);
+    if (curDoc != doc && ts.options.switchToDoc) {
+      closeArgHints(ts);
+      ts.options.switchToDoc(doc.name, doc.doc);
+    }
+  }
+
+  // The {line,ch} representation of positions makes this rather awkward.
+  function findContext(doc, data) {
+    var before = data.context.slice(0, data.contextOffset).split("\n");
+    var startLine = data.start.line - (before.length - 1);
+    var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
+
+    var text = doc.getLine(startLine).slice(start.ch);
+    for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
+      text += "\n" + doc.getLine(cur);
+    if (text.slice(0, data.context.length) == data.context) return data;
+
+    var cursor = doc.getSearchCursor(data.context, 0, false);
+    var nearest, nearestDist = Infinity;
+    while (cursor.findNext()) {
+      var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
+      if (!dist) dist = Math.abs(from.ch - start.ch);
+      if (dist < nearestDist) { nearest = from; nearestDist = dist; }
+    }
+    if (!nearest) return null;
+
+    if (before.length == 1)
+      nearest.ch += before[0].length;
+    else
+      nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
+    if (data.start.line == data.end.line)
+      var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
+    else
+      var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
+    return {start: nearest, end: end};
+  }
+
+  function atInterestingExpression(cm) {
+    var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
+    if (tok.start < pos.ch && (tok.type == "comment" || tok.type == "string")) return false;
+    return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
+  }
+
+  // Variable renaming
+
+  function rename(ts, cm) {
+    var token = cm.getTokenAt(cm.getCursor());
+    if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
+    dialog(cm, "New name for " + token.string, function(newName) {
+      ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
+        if (error) return showError(ts, cm, error);
+        applyChanges(ts, data.changes);
+      });
+    });
+  }
+
+  function selectName(ts, cm) {
+    var name = findDoc(ts, cm.doc).name;
+    ts.request(cm, {type: "refs"}, function(error, data) {
+      if (error) return showError(ts, cm, error);
+      var ranges = [], cur = 0;
+      for (var i = 0; i < data.refs.length; i++) {
+        var ref = data.refs[i];
+        if (ref.file == name) {
+          ranges.push({anchor: ref.start, head: ref.end});
+          if (cmpPos(cur, ref.start) >= 0 && cmpPos(cur, ref.end) <= 0)
+            cur = ranges.length - 1;
+        }
+      }
+      cm.setSelections(ranges, cur);
+    });
+  }
+
+  var nextChangeOrig = 0;
+  function applyChanges(ts, changes) {
+    var perFile = Object.create(null);
+    for (var i = 0; i < changes.length; ++i) {
+      var ch = changes[i];
+      (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
+    }
+    for (var file in perFile) {
+      var known = ts.docs[file], chs = perFile[file];;
+      if (!known) continue;
+      chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
+      var origin = "*rename" + (++nextChangeOrig);
+      for (var i = 0; i < chs.length; ++i) {
+        var ch = chs[i];
+        known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
+      }
+    }
+  }
+
+  // Generic request-building helper
+
+  function buildRequest(ts, doc, query, pos) {
+    var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
+    if (!allowFragments) delete query.fullDocs;
+    if (typeof query == "string") query = {type: query};
+    query.lineCharPositions = true;
+    if (query.end == null) {
+      query.end = pos || doc.doc.getCursor("end");
+      if (doc.doc.somethingSelected())
+        query.start = doc.doc.getCursor("start");
+    }
+    var startPos = query.start || query.end;
+
+    if (doc.changed) {
+      if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
+          doc.changed.to - doc.changed.from < 100 &&
+          doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
+        files.push(getFragmentAround(doc, startPos, query.end));
+        query.file = "#0";
+        var offsetLines = files[0].offsetLines;
+        if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
+        query.end = Pos(query.end.line - offsetLines, query.end.ch);
+      } else {
+        files.push({type: "full",
+                    name: doc.name,
+                    text: docValue(ts, doc)});
+        query.file = doc.name;
+        doc.changed = null;
+      }
+    } else {
+      query.file = doc.name;
+    }
+    for (var name in ts.docs) {
+      var cur = ts.docs[name];
+      if (cur.changed && cur != doc) {
+        files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
+        cur.changed = null;
+      }
+    }
+
+    return {query: query, files: files};
+  }
+
+  function getFragmentAround(data, start, end) {
+    var doc = data.doc;
+    var minIndent = null, minLine = null, endLine, tabSize = 4;
+    for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
+      var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
+      if (fn < 0) continue;
+      var indent = CodeMirror.countColumn(line, null, tabSize);
+      if (minIndent != null && minIndent <= indent) continue;
+      minIndent = indent;
+      minLine = p;
+    }
+    if (minLine == null) minLine = min;
+    var max = Math.min(doc.lastLine(), end.line + 20);
+    if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
+      endLine = max;
+    else for (endLine = end.line + 1; endLine < max; ++endLine) {
+      var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
+      if (indent <= minIndent) break;
+    }
+    var from = Pos(minLine, 0);
+
+    return {type: "part",
+            name: data.name,
+            offsetLines: from.line,
+            text: doc.getRange(from, Pos(endLine, 0))};
+  }
+
+  // Generic utilities
+
+  var cmpPos = CodeMirror.cmpPos;
+
+  function elt(tagname, cls /*, ... elts*/) {
+    var e = document.createElement(tagname);
+    if (cls) e.className = cls;
+    for (var i = 2; i < arguments.length; ++i) {
+      var elt = arguments[i];
+      if (typeof elt == "string") elt = document.createTextNode(elt);
+      e.appendChild(elt);
+    }
+    return e;
+  }
+
+  function dialog(cm, text, f) {
+    if (cm.openDialog)
+      cm.openDialog(text + ": <input type=text>", f);
+    else
+      f(prompt(text, ""));
+  }
+
+  // Tooltips
+
+  function tempTooltip(cm, content) {
+    if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
+    var where = cm.cursorCoords();
+    var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content);
+    function maybeClear() {
+      old = true;
+      if (!mouseOnTip) clear();
+    }
+    function clear() {
+      cm.state.ternTooltip = null;
+      if (!tip.parentNode) return;
+      cm.off("cursorActivity", clear);
+      cm.off('blur', clear);
+      cm.off('scroll', clear);
+      fadeOut(tip);
+    }
+    var mouseOnTip = false, old = false;
+    CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
+    CodeMirror.on(tip, "mouseout", function(e) {
+      if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) {
+        if (old) clear();
+        else mouseOnTip = false;
+      }
+    });
+    setTimeout(maybeClear, 1700);
+    cm.on("cursorActivity", clear);
+    cm.on('blur', clear);
+    cm.on('scroll', clear);
+  }
+
+  function makeTooltip(x, y, content) {
+    var node = elt("div", cls + "tooltip", content);
+    node.style.left = x + "px";
+    node.style.top = y + "px";
+    document.body.appendChild(node);
+    return node;
+  }
+
+  function remove(node) {
+    var p = node && node.parentNode;
+    if (p) p.removeChild(node);
+  }
+
+  function fadeOut(tooltip) {
+    tooltip.style.opacity = "0";
+    setTimeout(function() { remove(tooltip); }, 1100);
+  }
+
+  function showError(ts, cm, msg) {
+    if (ts.options.showError)
+      ts.options.showError(cm, msg);
+    else
+      tempTooltip(cm, String(msg));
+  }
+
+  function closeArgHints(ts) {
+    if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; }
+  }
+
+  function docValue(ts, doc) {
+    var val = doc.doc.getValue();
+    if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
+    return val;
+  }
+
+  // Worker wrapper
+
+  function WorkerServer(ts) {
+    var worker = ts.worker = new Worker(ts.options.workerScript);
+    worker.postMessage({type: "init",
+                        defs: ts.options.defs,
+                        plugins: ts.options.plugins,
+                        scripts: ts.options.workerDeps});
+    var msgId = 0, pending = {};
+
+    function send(data, c) {
+      if (c) {
+        data.id = ++msgId;
+        pending[msgId] = c;
+      }
+      worker.postMessage(data);
+    }
+    worker.onmessage = function(e) {
+      var data = e.data;
+      if (data.type == "getFile") {
+        getFile(ts, data.name, function(err, text) {
+          send({type: "getFile", err: String(err), text: text, id: data.id});
+        });
+      } else if (data.type == "debug") {
+        window.console.log(data.message);
+      } else if (data.id && pending[data.id]) {
+        pending[data.id](data.err, data.body);
+        delete pending[data.id];
+      }
+    };
+    worker.onerror = function(e) {
+      for (var id in pending) pending[id](e);
+      pending = {};
+    };
+
+    this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
+    this.delFile = function(name) { send({type: "del", name: name}); };
+    this.request = function(body, c) { send({type: "req", body: body}, c); };
+  }
+});

+ 24 - 10
editor/js/libs/codemirror/codemirror.css

@@ -4,6 +4,7 @@
   /* Set height, width, borders, and global font properties here */
   font-family: monospace;
   height: 300px;
+  color: black;
 }
 
 /* PADDING */
@@ -32,8 +33,7 @@
   min-width: 20px;
   text-align: right;
   color: #999;
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
+  white-space: nowrap;
 }
 
 .CodeMirror-guttermarker { color: black; }
@@ -139,11 +139,9 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
    the editor. You probably shouldn't touch them. */
 
 .CodeMirror {
-  line-height: 1;
   position: relative;
   overflow: hidden;
   background: white;
-  color: black;
 }
 
 .CodeMirror-scroll {
@@ -155,14 +153,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   height: 100%;
   outline: none; /* Prevent dragging from highlighting the element */
   position: relative;
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
 }
 .CodeMirror-sizer {
   position: relative;
   border-right: 30px solid transparent;
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
 }
 
 /* The fake, visible scrollbars. Used to force redraw during scrolling
@@ -197,8 +191,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 .CodeMirror-gutter {
   white-space: normal;
   height: 100%;
-  -moz-box-sizing: content-box;
-  box-sizing: content-box;
   display: inline-block;
   margin-bottom: -30px;
   /* Hack to make IE7 behave */
@@ -215,6 +207,11 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   cursor: default;
   z-index: 4;
 }
+.CodeMirror-gutter-wrapper {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
+}
 
 .CodeMirror-lines {
   cursor: text;
@@ -235,6 +232,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   z-index: 2;
   position: relative;
   overflow: visible;
+  -webkit-tap-highlight-color: transparent;
 }
 .CodeMirror-wrap pre {
   word-wrap: break-word;
@@ -256,6 +254,20 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
 
 .CodeMirror-widget {}
 
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
 .CodeMirror-measure {
   position: absolute;
   width: 100%;
@@ -283,6 +295,8 @@ div.CodeMirror-cursors {
 .CodeMirror-selected { background: #d9d9d9; }
 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
 .CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror ::selection { background: #d7d4f0; }
+.CodeMirror ::-moz-selection { background: #d7d4f0; }
 
 .cm-searching {
   background: #ffa;

File diff suppressed because it is too large
+ 928 - 64
editor/js/libs/codemirror/codemirror.js


+ 245 - 0
editor/js/libs/codemirror/mode/glsl.js

@@ -0,0 +1,245 @@
+// Full source: 
+//
+// 		https://github.com/hughsk/glsl-editor 
+//
+// (C) Copyright Hugh Kennedy
+//
+// This software is released under the MIT license:
+//
+// Permission is hereby granted, free of charge, to any person obtaining a 
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEAL-
+// INGS IN THE SOFTWARE.
+
+// The original source code has been slightly modified for the purpose of
+// integration (tschw).
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+  CodeMirror.defineMode("glsl", function(config, parserConfig) {
+    var indentUnit = config.indentUnit,
+        keywords = parserConfig.keywords || words(glslKeywords),
+        builtins = parserConfig.builtins || words(glslBuiltins),
+        blockKeywords = parserConfig.blockKeywords || words("case do else for if switch while struct"),
+        atoms = parserConfig.atoms || words("null"),
+        hooks = parserConfig.hooks || {},
+        multiLineStrings = parserConfig.multiLineStrings;
+    var isOperatorChar = /[+\-*&%=<>!?|\/]/;
+
+    var curPunc;
+
+    function tokenBase(stream, state) {
+      var ch = stream.next();
+      if (hooks[ch]) {
+        var result = hooks[ch](stream, state);
+        if (result !== false) return result;
+      }
+      if (ch == '"' || ch == "'") {
+        state.tokenize = tokenString(ch);
+        return state.tokenize(stream, state);
+      }
+      if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+        curPunc = ch;
+        return "bracket";
+      }
+      if (/\d/.test(ch)) {
+        stream.eatWhile(/[\w\.]/);
+        return "number";
+      }
+      if (ch == "/") {
+        if (stream.eat("*")) {
+          state.tokenize = tokenComment;
+          return tokenComment(stream, state);
+        }
+        if (stream.eat("/")) {
+          stream.skipToEnd();
+          return "comment";
+        }
+      }
+      if (ch == "#") {
+        stream.eatWhile(/[\S]+/);
+        stream.eatWhile(/[\s]+/);
+        stream.eatWhile(/[\S]+/);
+        stream.eatWhile(/[\s]+/);
+        return "comment";
+      }
+      if (isOperatorChar.test(ch)) {
+        stream.eatWhile(isOperatorChar);
+        return "operator";
+      }
+      stream.eatWhile(/[\w\$_]/);
+      var cur = stream.current();
+      if (keywords.propertyIsEnumerable(cur)) {
+        if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
+        return "keyword";
+      }
+      if (builtins.propertyIsEnumerable(cur)) {
+        return "builtin";
+      }
+      if (atoms.propertyIsEnumerable(cur)) return "atom";
+      return "word";
+    }
+
+    function tokenString(quote) {
+      return function(stream, state) {
+        var escaped = false, next, end = false;
+        while ((next = stream.next()) != null) {
+          if (next == quote && !escaped) {end = true; break;}
+          escaped = !escaped && next == "\\";
+        }
+        if (end || !(escaped || multiLineStrings))
+          state.tokenize = tokenBase;
+        return "string";
+      };
+    }
+
+    function tokenComment(stream, state) {
+      var maybeEnd = false, ch;
+      while (ch = stream.next()) {
+        if (ch == "/" && maybeEnd) {
+          state.tokenize = tokenBase;
+          break;
+        }
+        maybeEnd = (ch == "*");
+      }
+      return "comment";
+    }
+
+    function Context(indented, column, type, align, prev) {
+      this.indented = indented;
+      this.column = column;
+      this.type = type;
+      this.align = align;
+      this.prev = prev;
+    }
+    function pushContext(state, col, type) {
+      return state.context = new Context(state.indented, col, type, null, state.context);
+    }
+    function popContext(state) {
+      var t = state.context.type;
+      if (t == ")" || t == "]" || t == "}")
+        state.indented = state.context.indented;
+      return state.context = state.context.prev;
+    }
+
+    // Interface
+
+    return {
+      startState: function(basecolumn) {
+        return {
+          tokenize: null,
+          context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
+          indented: 0,
+          startOfLine: true
+        };
+      },
+
+      token: function(stream, state) {
+        var ctx = state.context;
+        if (stream.sol()) {
+          if (ctx.align == null) ctx.align = false;
+          state.indented = stream.indentation();
+          state.startOfLine = true;
+        }
+        if (stream.eatSpace()) return null;
+        curPunc = null;
+        var style = (state.tokenize || tokenBase)(stream, state);
+        if (style == "comment" || style == "meta") return style;
+        if (ctx.align == null) ctx.align = true;
+
+        if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
+        else if (curPunc == "{") pushContext(state, stream.column(), "}");
+        else if (curPunc == "[") pushContext(state, stream.column(), "]");
+        else if (curPunc == "(") pushContext(state, stream.column(), ")");
+        else if (curPunc == "}") {
+          while (ctx.type == "statement") ctx = popContext(state);
+          if (ctx.type == "}") ctx = popContext(state);
+          while (ctx.type == "statement") ctx = popContext(state);
+        }
+        else if (curPunc == ctx.type) popContext(state);
+        else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
+          pushContext(state, stream.column(), "statement");
+        state.startOfLine = false;
+        return style;
+      },
+
+      indent: function(state, textAfter) {
+        if (state.tokenize != tokenBase && state.tokenize != null) return 0;
+        var firstChar = textAfter && textAfter.charAt(0), ctx = state.context, closing = firstChar == ctx.type;
+        if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit);
+        else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+        else return ctx.indented + (closing ? 0 : indentUnit);
+      },
+
+      electricChars: "{}"
+    };
+  });
+
+  function words(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+  var glslKeywords = "attribute const uniform varying break continue " +
+    "do for while if else in out inout float int void bool true false " +
+    "lowp mediump highp precision invariant discard return mat2 mat3 " +
+    "mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 sampler2D " +
+    "samplerCube struct gl_FragCoord gl_FragColor";
+  var glslBuiltins = "radians degrees sin cos tan asin acos atan pow " +
+    "exp log exp2 log2 sqrt inversesqrt abs sign floor ceil fract mod " +
+    "min max clamp mix step smoothstep length distance dot cross " +
+    "normalize faceforward reflect refract matrixCompMult lessThan " +
+    "lessThanEqual greaterThan greaterThanEqual equal notEqual any all " +
+    "not dFdx dFdy fwidth texture2D texture2DProj texture2DLod " +
+    "texture2DProjLod textureCube textureCubeLod require export";
+
+  function cppHook(stream, state) {
+    if (!state.startOfLine) return false;
+    stream.skipToEnd();
+    return "meta";
+  }
+
+  ;(function() {
+    // C#-style strings where "" escapes a quote.
+    function tokenAtString(stream, state) {
+      var next;
+      while ((next = stream.next()) != null) {
+        if (next == '"' && !stream.eat('"')) {
+          state.tokenize = null;
+          break;
+        }
+      }
+      return "string";
+    }
+
+    CodeMirror.defineMIME("text/x-glsl", {
+      name: "glsl",
+      keywords: words(glslKeywords),
+      builtins: words(glslBuiltins),
+      blockKeywords: words("case do else for if switch while struct"),
+      atoms: words("null"),
+      hooks: {"#": cppHook}
+    });
+  }());
+});

+ 13 - 2
editor/js/libs/codemirror/mode/javascript.js

@@ -118,7 +118,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
                state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
         readRegexp(stream);
-        stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
+        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
         return ret("regexp", "string-2");
       } else {
         stream.eatWhile(isOperatorChar);
@@ -549,6 +549,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   }
   function classBody(type, value) {
     if (type == "variable" || cx.style == "keyword") {
+      if (value == "static") {
+        cx.marked = "keyword";
+        return cont(classBody);
+      }
       cx.marked = "property";
       if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
       return cont(functiondef, classBody);
@@ -600,6 +604,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "if") return cont(expression, comprehension);
   }
 
+  function isContinuedStatement(state, textAfter) {
+    return state.lastType == "operator" || state.lastType == "," ||
+      isOperatorChar.test(textAfter.charAt(0)) ||
+      /[,.]/.test(textAfter.charAt(0));
+  }
+
   // Interface
 
   return {
@@ -651,7 +661,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "stat")
-        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
+        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
       else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
         return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
       else if (lexical.align) return lexical.column + (closing ? 0 : 1);
@@ -663,6 +673,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     blockCommentEnd: jsonMode ? null : "*/",
     lineComment: jsonMode ? null : "//",
     fold: "brace",
+    closeBrackets: "()[]{}''\"\"``",
 
     helperType: jsonMode ? "json" : "javascript",
     jsonldMode: jsonldMode,

+ 2 - 0
editor/js/libs/codemirror/theme/monokai.css

@@ -2,6 +2,8 @@
 
 .cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2;}
 .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;}
+.cm-s-monokai.CodeMirror ::selection { background: rgba(73, 72, 62, .99); }
+.cm-s-monokai.CodeMirror ::-moz-selection { background: rgba(73, 72, 62, .99); }
 .cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;}
 .cm-s-monokai .CodeMirror-guttermarker { color: white; }
 .cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; }

+ 456 - 0
editor/js/libs/jsonlint.js

@@ -0,0 +1,456 @@
+// Full source:
+//
+//		https://github.com/zaach/jsonlint
+//
+// Copyright (C) 2012 Zachary Carter
+//
+// Permission is hereby granted, free of charge, to any person obtaining a 
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEAL-
+// INGS IN THE SOFTWARE.
+
+/* Jison generated parser */
+var jsonlint = (function(){
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1},
+terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},
+productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],
+performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1: // replace escaped characters with actual character
+          this.$ = yytext.replace(/\\(\\|")/g, "$"+"1")
+                     .replace(/\\n/g,'\n')
+                     .replace(/\\r/g,'\r')
+                     .replace(/\\t/g,'\t')
+                     .replace(/\\v/g,'\v')
+                     .replace(/\\f/g,'\f')
+                     .replace(/\\b/g,'\b');
+        
+break;
+case 2:this.$ = Number(yytext);
+break;
+case 3:this.$ = null;
+break;
+case 4:this.$ = true;
+break;
+case 5:this.$ = false;
+break;
+case 6:return this.$ = $$[$0-1];
+break;
+case 13:this.$ = {};
+break;
+case 14:this.$ = $$[$0-1];
+break;
+case 15:this.$ = [$$[$0-2], $$[$0]];
+break;
+case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
+break;
+case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1];
+break;
+case 18:this.$ = [];
+break;
+case 19:this.$ = $$[$0-1];
+break;
+case 20:this.$ = [$$[$0]];
+break;
+case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]);
+break;
+}
+},
+table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],
+defaultActions: {16:[2,6]},
+parseError: function parseError(str, hash) {
+    throw new Error(str);
+},
+parse: function parse(input) {
+    var self = this,
+        stack = [0],
+        vstack = [null], // semantic value stack
+        lstack = [], // location stack
+        table = this.table,
+        yytext = '',
+        yylineno = 0,
+        yyleng = 0,
+        recovering = 0,
+        TERROR = 2,
+        EOF = 1;
+
+    //this.reductionCount = this.shiftCount = 0;
+
+    this.lexer.setInput(input);
+    this.lexer.yy = this.yy;
+    this.yy.lexer = this.lexer;
+    if (typeof this.lexer.yylloc == 'undefined')
+        this.lexer.yylloc = {};
+    var yyloc = this.lexer.yylloc;
+    lstack.push(yyloc);
+
+    if (typeof this.yy.parseError === 'function')
+        this.parseError = this.yy.parseError;
+
+    function popStack (n) {
+        stack.length = stack.length - 2*n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+
+    function lex() {
+        var token;
+        token = self.lexer.lex() || 1; // $end = 1
+        // if token isn't its numeric value, convert
+        if (typeof token !== 'number') {
+            token = self.symbols_[token] || token;
+        }
+        return token;
+    }
+
+    var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
+    while (true) {
+        // retreive state number from top of stack
+        state = stack[stack.length-1];
+
+        // use default actions if available
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol == null)
+                symbol = lex();
+            // read action for current state and first input
+            action = table[state] && table[state][symbol];
+        }
+
+        // handle parse error
+        _handle_error:
+        if (typeof action === 'undefined' || !action.length || !action[0]) {
+
+            if (!recovering) {
+                // Report error
+                expected = [];
+                for (p in table[state]) if (this.terminals_[p] && p > 2) {
+                    expected.push("'"+this.terminals_[p]+"'");
+                }
+                var errStr = '';
+                if (this.lexer.showPosition) {
+                    errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
+                } else {
+                    errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
+                                  (symbol == 1 /*EOF*/ ? "end of input" :
+                                              ("'"+(this.terminals_[symbol] || symbol)+"'"));
+                }
+                this.parseError(errStr,
+                    {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
+            }
+
+            // just recovered from another error
+            if (recovering == 3) {
+                if (symbol == EOF) {
+                    throw new Error(errStr || 'Parsing halted.');
+                }
+
+                // discard current lookahead and grab another
+                yyleng = this.lexer.yyleng;
+                yytext = this.lexer.yytext;
+                yylineno = this.lexer.yylineno;
+                yyloc = this.lexer.yylloc;
+                symbol = lex();
+            }
+
+            // try to recover from error
+            while (1) {
+                // check for error recovery rule in this state
+                if ((TERROR.toString()) in table[state]) {
+                    break;
+                }
+                if (state == 0) {
+                    throw new Error(errStr || 'Parsing halted.');
+                }
+                popStack(1);
+                state = stack[stack.length-1];
+            }
+
+            preErrorSymbol = symbol; // save the lookahead token
+            symbol = TERROR;         // insert generic error symbol as new lookahead
+            state = stack[stack.length-1];
+            action = table[state] && table[state][TERROR];
+            recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
+        }
+
+        // this shouldn't happen, unless resolve defaults are off
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
+        }
+
+        switch (action[0]) {
+
+            case 1: // shift
+                //this.shiftCount++;
+
+                stack.push(symbol);
+                vstack.push(this.lexer.yytext);
+                lstack.push(this.lexer.yylloc);
+                stack.push(action[1]); // push state
+                symbol = null;
+                if (!preErrorSymbol) { // normal execution/no error
+                    yyleng = this.lexer.yyleng;
+                    yytext = this.lexer.yytext;
+                    yylineno = this.lexer.yylineno;
+                    yyloc = this.lexer.yylloc;
+                    if (recovering > 0)
+                        recovering--;
+                } else { // error just occurred, resume old lookahead f/ before error
+                    symbol = preErrorSymbol;
+                    preErrorSymbol = null;
+                }
+                break;
+
+            case 2: // reduce
+                //this.reductionCount++;
+
+                len = this.productions_[action[1]][1];
+
+                // perform semantic action
+                yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
+                // default location, uses first token for firsts, last for lasts
+                yyval._$ = {
+                    first_line: lstack[lstack.length-(len||1)].first_line,
+                    last_line: lstack[lstack.length-1].last_line,
+                    first_column: lstack[lstack.length-(len||1)].first_column,
+                    last_column: lstack[lstack.length-1].last_column
+                };
+                r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+
+                if (typeof r !== 'undefined') {
+                    return r;
+                }
+
+                // pop off stack
+                if (len) {
+                    stack = stack.slice(0,-1*len*2);
+                    vstack = vstack.slice(0, -1*len);
+                    lstack = lstack.slice(0, -1*len);
+                }
+
+                stack.push(this.productions_[action[1]][0]);    // push nonterminal (reduce)
+                vstack.push(yyval.$);
+                lstack.push(yyval._$);
+                // goto new state = table[STATE][NONTERMINAL]
+                newState = table[stack[stack.length-2]][stack[stack.length-1]];
+                stack.push(newState);
+                break;
+
+            case 3: // accept
+                return true;
+        }
+
+    }
+
+    return true;
+}};
+/* Jison generated lexer */
+var lexer = (function(){
+var lexer = ({EOF:1,
+parseError:function parseError(str, hash) {
+        if (this.yy.parseError) {
+            this.yy.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+setInput:function (input) {
+        this._input = input;
+        this._more = this._less = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
+        return this;
+    },
+input:function () {
+        var ch = this._input[0];
+        this.yytext+=ch;
+        this.yyleng++;
+        this.match+=ch;
+        this.matched+=ch;
+        var lines = ch.match(/\n/);
+        if (lines) this.yylineno++;
+        this._input = this._input.slice(1);
+        return ch;
+    },
+unput:function (ch) {
+        this._input = ch + this._input;
+        return this;
+    },
+more:function () {
+        this._more = true;
+        return this;
+    },
+less:function (n) {
+        this._input = this.match.slice(n) + this._input;
+    },
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
+    },
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c+"^";
+    },
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) this.done = true;
+
+        var token,
+            match,
+            tempMatch,
+            index,
+            col,
+            lines;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i=0;i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (!this.options.flex) break;
+            }
+        }
+        if (match) {
+            lines = match[0].match(/\n.*/g);
+            if (lines) this.yylineno += lines.length;
+            this.yylloc = {first_line: this.yylloc.last_line,
+                           last_line: this.yylineno+1,
+                           first_column: this.yylloc.last_column,
+                           last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
+            this.yytext += match[0];
+            this.match += match[0];
+            this.yyleng = this.yytext.length;
+            this._more = false;
+            this._input = this._input.slice(match[0].length);
+            this.matched += match[0];
+            token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
+            if (this.done && this._input) this.done = false;
+            if (token) return token;
+            else return;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 
+                    {text: "", token: null, line: this.yylineno});
+        }
+    },
+lex:function lex() {
+        var r = this.next();
+        if (typeof r !== 'undefined') {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+begin:function begin(condition) {
+        this.conditionStack.push(condition);
+    },
+popState:function popState() {
+        return this.conditionStack.pop();
+    },
+_currentRules:function _currentRules() {
+        return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
+    },
+topState:function () {
+        return this.conditionStack[this.conditionStack.length-2];
+    },
+pushState:function begin(condition) {
+        this.begin(condition);
+    }});
+lexer.options = {};
+lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+var YYSTATE=YY_START
+switch($avoiding_name_collisions) {
+case 0:/* skip whitespace */
+break;
+case 1:return 6
+break;
+case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4
+break;
+case 3:return 17
+break;
+case 4:return 18
+break;
+case 5:return 23
+break;
+case 6:return 24
+break;
+case 7:return 22
+break;
+case 8:return 21
+break;
+case 9:return 10
+break;
+case 10:return 11
+break;
+case 11:return 8
+break;
+case 12:return 14
+break;
+case 13:return 'INVALID'
+break;
+}
+};
+lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/];
+lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}};
+
+
+;
+return lexer;})()
+parser.lexer = lexer;
+return parser;
+})();
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = jsonlint;
+exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); }
+exports.main = function commonjsMain(args) {
+    if (!args[1])
+        throw new Error('Usage: '+args[0]+' FILE');
+    if (typeof process !== 'undefined') {
+        var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
+    } else {
+        var cwd = require("file").path(require("file").cwd());
+        var source = cwd.join(args[1]).read({charset: "utf-8"});
+    }
+    return exports.parser.parse(source);
+}
+if (typeof module !== 'undefined' && require.main === module) {
+  exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
+}
+}

File diff suppressed because it is too large
+ 276 - 0
editor/js/libs/tern-threejs/threejs.js


+ 87 - 0
editor/js/libs/ternjs/comment.js

@@ -0,0 +1,87 @@
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    return mod(exports);
+  if (typeof define == "function" && define.amd) // AMD
+    return define(["exports"], mod);
+  mod(tern.comment || (tern.comment = {}));
+})(function(exports) {
+  function isSpace(ch) {
+    return (ch < 14 && ch > 8) || ch === 32 || ch === 160;
+  }
+
+  function onOwnLine(text, pos) {
+    for (; pos > 0; --pos) {
+      var ch = text.charCodeAt(pos - 1);
+      if (ch == 10) break;
+      if (!isSpace(ch)) return false;
+    }
+    return true;
+  }
+
+  // Gather comments directly before a function
+  exports.commentsBefore = function(text, pos) {
+    var found = null, emptyLines = 0, topIsLineComment;
+    out: while (pos > 0) {
+      var prev = text.charCodeAt(pos - 1);
+      if (prev == 10) {
+        for (var scan = --pos, sawNonWS = false; scan > 0; --scan) {
+          prev = text.charCodeAt(scan - 1);
+          if (prev == 47 && text.charCodeAt(scan - 2) == 47) {
+            if (!onOwnLine(text, scan - 2)) break out;
+            var content = text.slice(scan, pos);
+            if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0];
+            else (found || (found = [])).unshift(content);
+            topIsLineComment = true;
+            emptyLines = 0;
+            pos = scan - 2;
+            break;
+          } else if (prev == 10) {
+            if (!sawNonWS && ++emptyLines > 1) break out;
+            break;
+          } else if (!sawNonWS && !isSpace(prev)) {
+            sawNonWS = true;
+          }
+        }
+      } else if (prev == 47 && text.charCodeAt(pos - 2) == 42) {
+        for (var scan = pos - 2; scan > 1; --scan) {
+          if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) {
+            if (!onOwnLine(text, scan - 2)) break out;
+            (found || (found = [])).unshift(text.slice(scan, pos - 2));
+            topIsLineComment = false;
+            emptyLines = 0;
+            break;
+          }
+        }
+        pos = scan - 2;
+      } else if (isSpace(prev)) {
+        --pos;
+      } else {
+        break;
+      }
+    }
+    return found;
+  };
+
+  exports.commentAfter = function(text, pos) {
+    while (pos < text.length) {
+      var next = text.charCodeAt(pos);
+      if (next == 47) {
+        var after = text.charCodeAt(pos + 1), end;
+        if (after == 47) // line comment
+          end = text.indexOf("\n", pos + 2);
+        else if (after == 42) // block comment
+          end = text.indexOf("*/", pos + 2);
+        else
+          return;
+        return text.slice(pos + 2, end < 0 ? text.length : end);
+      } else if (isSpace(next)) {
+        ++pos;
+      }
+    }
+  };
+
+  exports.ensureCommentsBefore = function(text, node) {
+    if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore;
+    return node.commentsBefore = exports.commentsBefore(text, node.start);
+  };
+});

+ 589 - 0
editor/js/libs/ternjs/def.js

@@ -0,0 +1,589 @@
+// Type description parser
+//
+// Type description JSON files (such as ecma5.json and browser.json)
+// are used to
+//
+// A) describe types that come from native code
+//
+// B) to cheaply load the types for big libraries, or libraries that
+//    can't be inferred well
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    return exports.init = mod;
+  if (typeof define == "function" && define.amd) // AMD
+    return define({init: mod});
+  tern.def = {init: mod};
+})(function(exports, infer) {
+  "use strict";
+
+  function hop(obj, prop) {
+    return Object.prototype.hasOwnProperty.call(obj, prop);
+  }
+
+  var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) {
+    this.pos = start || 0;
+    this.spec = spec;
+    this.base = base;
+    this.forceNew = forceNew;
+  };
+
+  function unwrapType(type, self, args) {
+    return type.call ? type(self, args) : type;
+  }
+
+  function extractProp(type, prop) {
+    if (prop == "!ret") {
+      if (type.retval) return type.retval;
+      var rv = new infer.AVal;
+      type.propagate(new infer.IsCallee(infer.ANull, [], null, rv));
+      return rv;
+    } else {
+      return type.getProp(prop);
+    }
+  }
+
+  function computedFunc(args, retType) {
+    return function(self, cArgs) {
+      var realArgs = [];
+      for (var i = 0; i < args.length; i++) realArgs.push(unwrapType(args[i], self, cArgs));
+      return new infer.Fn(name, infer.ANull, realArgs, unwrapType(retType, self, cArgs));
+    };
+  }
+  function computedUnion(types) {
+    return function(self, args) {
+      var union = new infer.AVal;
+      for (var i = 0; i < types.length; i++) unwrapType(types[i], self, args).propagate(union);
+      return union;
+    };
+  }
+  function computedArray(inner) {
+    return function(self, args) {
+      return new infer.Arr(inner(self, args));
+    };
+  }
+
+  TypeParser.prototype = {
+    eat: function(str) {
+      if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) {
+        this.pos += str.length;
+        return true;
+      }
+    },
+    word: function(re) {
+      var word = "", ch, re = re || /[\w$]/;
+      while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; }
+      return word;
+    },
+    error: function() {
+      throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")");
+    },
+    parseFnType: function(comp, name, top) {
+      var args = [], names = [], computed = false;
+      if (!this.eat(")")) for (var i = 0; ; ++i) {
+        var colon = this.spec.indexOf(": ", this.pos), argname;
+        if (colon != -1) {
+          argname = this.spec.slice(this.pos, colon);
+          if (/^[$\w?]+$/.test(argname))
+            this.pos = colon + 2;
+          else
+            argname = null;
+        }
+        names.push(argname);
+        var argType = this.parseType(comp);
+        if (argType.call) computed = true;
+        args.push(argType);
+        if (!this.eat(", ")) {
+          this.eat(")") || this.error();
+          break;
+        }
+      }
+      var retType, computeRet, computeRetStart, fn;
+      if (this.eat(" -> ")) {
+        var retStart = this.pos;
+        retType = this.parseType(true);
+        if (retType.call) {
+          if (top) {
+            computeRet = retType;
+            retType = infer.ANull;
+            computeRetStart = retStart;
+          } else {
+            computed = true;
+          }
+        }
+      } else {
+        retType = infer.ANull;
+      }
+      if (computed) return computedFunc(args, retType);
+
+      if (top && (fn = this.base))
+        infer.Fn.call(this.base, name, infer.ANull, args, names, retType);
+      else
+        fn = new infer.Fn(name, infer.ANull, args, names, retType);
+      if (computeRet) fn.computeRet = computeRet;
+      if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos);
+      return fn;
+    },
+    parseType: function(comp, name, top) {
+      var main = this.parseTypeMaybeProp(comp, name, top);
+      if (!this.eat("|")) return main;
+      var types = [main], computed = main.call;
+      for (;;) {
+        var next = this.parseTypeMaybeProp(comp, name, top);
+        types.push(next);
+        if (next.call) computed = true;
+        if (!this.eat("|")) break;
+      }
+      if (computed) return computedUnion(types);
+      var union = new infer.AVal;
+      for (var i = 0; i < types.length; i++) types[i].propagate(union);
+      return union;
+    },
+    parseTypeMaybeProp: function(comp, name, top) {
+      var result = this.parseTypeInner(comp, name, top);
+      while (comp && this.eat(".")) result = this.extendWithProp(result);
+      return result;
+    },
+    extendWithProp: function(base) {
+      var propName = this.word(/[\w<>$!]/) || this.error();
+      if (base.apply) return function(self, args) {
+        return extractProp(base(self, args), propName);
+      };
+      return extractProp(base, propName);
+    },
+    parseTypeInner: function(comp, name, top) {
+      if (this.eat("fn(")) {
+        return this.parseFnType(comp, name, top);
+      } else if (this.eat("[")) {
+        var inner = this.parseType(comp);
+        this.eat("]") || this.error();
+        if (inner.call) return computedArray(inner);
+        if (top && this.base) {
+          infer.Arr.call(this.base, inner);
+          return this.base;
+        }
+        return new infer.Arr(inner);
+      } else if (this.eat("+")) {
+        var path = this.word(/[\w$<>\.!]/);
+        var base = parsePath(path + ".prototype");
+        var type;
+        if (!(base instanceof infer.Obj)) base = parsePath(path);
+        if (!(base instanceof infer.Obj)) return base;
+        if (comp && this.eat("[")) return this.parsePoly(base);
+        if (top && this.forceNew) return new infer.Obj(base);
+        return infer.getInstance(base);
+      } else if (comp && this.eat("!")) {
+        var arg = this.word(/\d/);
+        if (arg) {
+          arg = Number(arg);
+          return function(_self, args) {return args[arg] || infer.ANull;};
+        } else if (this.eat("this")) {
+          return function(self) {return self;};
+        } else if (this.eat("custom:")) {
+          var fname = this.word(/[\w$]/);
+          return customFunctions[fname] || function() { return infer.ANull; };
+        } else {
+          return this.fromWord("!" + this.word(/[\w$<>\.!]/));
+        }
+      } else if (this.eat("?")) {
+        return infer.ANull;
+      } else {
+        return this.fromWord(this.word(/[\w$<>\.!`]/));
+      }
+    },
+    fromWord: function(spec) {
+      var cx = infer.cx();
+      switch (spec) {
+      case "number": return cx.num;
+      case "string": return cx.str;
+      case "bool": return cx.bool;
+      case "<top>": return cx.topScope;
+      }
+      if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec];
+      return parsePath(spec);
+    },
+    parsePoly: function(base) {
+      var propName = "<i>", match;
+      if (match = this.spec.slice(this.pos).match(/^\s*(\w+)\s*=\s*/)) {
+        propName = match[1];
+        this.pos += match[0].length;
+      }
+      var value = this.parseType(true);
+      if (!this.eat("]")) this.error();
+      if (value.call) return function(self, args) {
+        var instance = infer.getInstance(base);
+        value(self, args).propagate(instance.defProp(propName));
+        return instance;
+      };
+      var instance = infer.getInstance(base);
+      value.propagate(instance.defProp(propName));
+      return instance;
+    }
+  };
+
+  function parseType(spec, name, base, forceNew) {
+    var type = new TypeParser(spec, null, base, forceNew).parseType(false, name, true);
+    if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) {
+      var arg = type.args[i];
+      if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) {
+        var fArg = fArgs[i];
+        if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull));
+      });
+    })(i);
+    return type;
+  }
+
+  function addEffect(fn, handler, replaceRet) {
+    var oldCmp = fn.computeRet, rv = fn.retval;
+    fn.computeRet = function(self, args, argNodes) {
+      var handled = handler(self, args, argNodes);
+      var old = oldCmp ? oldCmp(self, args, argNodes) : rv;
+      return replaceRet ? handled : old;
+    };
+  }
+
+  var parseEffect = exports.parseEffect = function(effect, fn) {
+    var m;
+    if (effect.indexOf("propagate ") == 0) {
+      var p = new TypeParser(effect, 10);
+      var origin = p.parseType(true);
+      if (!p.eat(" ")) p.error();
+      var target = p.parseType(true);
+      addEffect(fn, function(self, args) {
+        unwrapType(origin, self, args).propagate(unwrapType(target, self, args));
+      });
+    } else if (effect.indexOf("call ") == 0) {
+      var andRet = effect.indexOf("and return ", 5) == 5;
+      var p = new TypeParser(effect, andRet ? 16 : 5);
+      var getCallee = p.parseType(true), getSelf = null, getArgs = [];
+      if (p.eat(" this=")) getSelf = p.parseType(true);
+      while (p.eat(" ")) getArgs.push(p.parseType(true));
+      addEffect(fn, function(self, args) {
+        var callee = unwrapType(getCallee, self, args);
+        var slf = getSelf ? unwrapType(getSelf, self, args) : infer.ANull, as = [];
+        for (var i = 0; i < getArgs.length; ++i) as.push(unwrapType(getArgs[i], self, args));
+        var result = andRet ? new infer.AVal : infer.ANull;
+        callee.propagate(new infer.IsCallee(slf, as, null, result));
+        return result;
+      }, andRet);
+    } else if (m = effect.match(/^custom (\S+)\s*(.*)/)) {
+      var customFunc = customFunctions[m[1]];
+      if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc);
+    } else if (effect.indexOf("copy ") == 0) {
+      var p = new TypeParser(effect, 5);
+      var getFrom = p.parseType(true);
+      p.eat(" ");
+      var getTo = p.parseType(true);
+      addEffect(fn, function(self, args) {
+        var from = unwrapType(getFrom, self, args), to = unwrapType(getTo, self, args);
+        from.forAllProps(function(prop, val, local) {
+          if (local && prop != "<i>")
+            to.propagate(new infer.PropHasSubset(prop, val));
+        });
+      });
+    } else {
+      throw new Error("Unknown effect type: " + effect);
+    }
+  };
+
+  var currentTopScope;
+
+  var parsePath = exports.parsePath = function(path, scope) {
+    var cx = infer.cx(), cached = cx.paths[path], origPath = path;
+    if (cached != null) return cached;
+    cx.paths[path] = infer.ANull;
+
+    var base = scope || currentTopScope || cx.topScope;
+
+    if (cx.localDefs) for (var name in cx.localDefs) {
+      if (path.indexOf(name) == 0) {
+        if (path == name) return cx.paths[path] = cx.localDefs[path];
+        if (path.charAt(name.length) == ".") {
+          base = cx.localDefs[name];
+          path = path.slice(name.length + 1);
+          break;
+        }
+      }
+    }
+
+    var parts = path.split(".");
+    for (var i = 0; i < parts.length && base != infer.ANull; ++i) {
+      var prop = parts[i];
+      if (prop.charAt(0) == "!") {
+        if (prop == "!proto") {
+          base = (base instanceof infer.Obj && base.proto) || infer.ANull;
+        } else {
+          var fn = base.getFunctionType();
+          if (!fn) {
+            base = infer.ANull;
+          } else if (prop == "!ret") {
+            base = fn.retval && fn.retval.getType(false) || infer.ANull;
+          } else {
+            var arg = fn.args && fn.args[Number(prop.slice(1))];
+            base = (arg && arg.getType(false)) || infer.ANull;
+          }
+        }
+      } else if (base instanceof infer.Obj) {
+        var propVal = (prop == "prototype" && base instanceof infer.Fn) ? base.getProp(prop) : base.props[prop];
+        if (!propVal || propVal.isEmpty())
+          base = infer.ANull;
+        else
+          base = propVal.types[0];
+      }
+    }
+    // Uncomment this to get feedback on your poorly written .json files
+    // if (base == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")");
+    cx.paths[origPath] = base == infer.ANull ? null : base;
+    return base;
+  };
+
+  function emptyObj(ctor) {
+    var empty = Object.create(ctor.prototype);
+    empty.props = Object.create(null);
+    empty.isShell = true;
+    return empty;
+  }
+
+  function isSimpleAnnotation(spec) {
+    if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false;
+    for (var prop in spec)
+      if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data")
+        return false;
+    return true;
+  }
+
+  function passOne(base, spec, path) {
+    if (!base) {
+      var tp = spec["!type"];
+      if (tp) {
+        if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn);
+        else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr);
+        else throw new Error("Invalid !type spec: " + tp);
+      } else if (spec["!stdProto"]) {
+        base = infer.cx().protos[spec["!stdProto"]];
+      } else {
+        base = emptyObj(infer.Obj);
+      }
+      base.name = path;
+    }
+
+    for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
+      var inner = spec[name];
+      if (typeof inner == "string" || isSimpleAnnotation(inner)) continue;
+      var prop = base.defProp(name);
+      passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop);
+    }
+    return base;
+  }
+
+  function passTwo(base, spec, path) {
+    if (base.isShell) {
+      delete base.isShell;
+      var tp = spec["!type"];
+      if (tp) {
+        parseType(tp, path, base);
+      } else {
+        var proto = spec["!proto"] && parseType(spec["!proto"]);
+        infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path);
+      }
+    }
+
+    var effects = spec["!effects"];
+    if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i)
+      parseEffect(effects[i], base);
+    copyInfo(spec, base);
+
+    for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
+      var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name;
+      if (typeof inner == "string") {
+        if (known.isEmpty()) parseType(inner, innerPath).propagate(known);
+      } else {
+        if (!isSimpleAnnotation(inner))
+          passTwo(known.getObjType(), inner, innerPath);
+        else if (known.isEmpty())
+          parseType(inner["!type"], innerPath, null, true).propagate(known);
+        else
+          continue;
+        if (inner["!doc"]) known.doc = inner["!doc"];
+        if (inner["!url"]) known.url = inner["!url"];
+        if (inner["!span"]) known.span = inner["!span"];
+      }
+    }
+    return base;
+  }
+
+  function copyInfo(spec, type) {
+    if (spec["!doc"]) type.doc = spec["!doc"];
+    if (spec["!url"]) type.url = spec["!url"];
+    if (spec["!span"]) type.span = spec["!span"];
+    if (spec["!data"]) type.metaData = spec["!data"];
+  }
+
+  function runPasses(type, arg) {
+    var parent = infer.cx().parent, pass = parent && parent.passes && parent.passes[type];
+    if (pass) for (var i = 0; i < pass.length; i++) pass[i](arg);
+  }
+
+  function doLoadEnvironment(data, scope) {
+    var cx = infer.cx();
+
+    infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length);
+    cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null);
+
+    runPasses("preLoadDef", data);
+
+    passOne(scope, data);
+
+    var def = data["!define"];
+    if (def) {
+      for (var name in def) {
+        var spec = def[name];
+        cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name);
+      }
+      for (var name in def) {
+        var spec = def[name];
+        if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name);
+      }
+    }
+
+    passTwo(scope, data);
+
+    runPasses("postLoadDef", data);
+
+    cx.curOrigin = cx.localDefs = null;
+  }
+
+  exports.load = function(data, scope) {
+    if (!scope) scope = infer.cx().topScope;
+    var oldScope = currentTopScope;
+    currentTopScope = scope;
+    try {
+      doLoadEnvironment(data, scope);
+    } finally {
+      currentTopScope = oldScope;
+    }
+  };
+
+  exports.parse = function(data, origin, path) {
+    var cx = infer.cx();
+    if (origin) {
+      cx.origin = origin;
+      cx.localDefs = cx.definitions[origin];
+    }
+
+    try {
+      if (typeof data == "string")
+        return parseType(data, path);
+      else
+        return passTwo(passOne(null, data, path), data, path);
+    } finally {
+      if (origin) cx.origin = cx.localDefs = null;
+    }
+  };
+
+  // Used to register custom logic for more involved effect or type
+  // computation.
+  var customFunctions = Object.create(null);
+  infer.registerFunction = function(name, f) { customFunctions[name] = f; };
+
+  var IsCreated = infer.constraint("created, target, spec", {
+    addType: function(tp) {
+      if (tp instanceof infer.Obj && this.created++ < 5) {
+        var derived = new infer.Obj(tp), spec = this.spec;
+        if (spec instanceof infer.AVal) spec = spec.getObjType(false);
+        if (spec instanceof infer.Obj) for (var prop in spec.props) {
+          var cur = spec.props[prop].types[0];
+          var p = derived.defProp(prop);
+          if (cur && cur instanceof infer.Obj && cur.props.value) {
+            var vtp = cur.props.value.getType(false);
+            if (vtp) p.addType(vtp);
+          }
+        }
+        this.target.addType(derived);
+      }
+    }
+  });
+
+  infer.registerFunction("Object_create", function(_self, args, argNodes) {
+    if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null)
+      return new infer.Obj();
+
+    var result = new infer.AVal;
+    if (args[0]) args[0].propagate(new IsCreated(0, result, args[1]));
+    return result;
+  });
+
+  var PropSpec = infer.constraint("target", {
+    addType: function(tp) {
+      if (!(tp instanceof infer.Obj)) return;
+      if (tp.hasProp("value"))
+        tp.getProp("value").propagate(this.target);
+      else if (tp.hasProp("get"))
+        tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target));
+    }
+  });
+
+  infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) {
+    if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" &&
+        typeof argNodes[1].value == "string") {
+      var obj = args[0], connect = new infer.AVal;
+      obj.propagate(new infer.PropHasSubset(argNodes[1].value, connect, argNodes[1]));
+      args[2].propagate(new PropSpec(connect));
+    }
+    return infer.ANull;
+  });
+
+  infer.registerFunction("Object_defineProperties", function(_self, args, argNodes) {
+    if (args.length >= 2) {
+      var obj = args[0];
+      args[1].forAllProps(function(prop, val, local) {
+        if (!local) return;
+        var connect = new infer.AVal;
+        obj.propagate(new infer.PropHasSubset(prop, connect, argNodes && argNodes[1]));
+        val.propagate(new PropSpec(connect));
+      });
+    }
+    return infer.ANull;
+  });
+
+  var IsBound = infer.constraint("self, args, target", {
+    addType: function(tp) {
+      if (!(tp instanceof infer.Fn)) return;
+      this.target.addType(new infer.Fn(tp.name, infer.ANull, tp.args.slice(this.args.length),
+                                       tp.argNames.slice(this.args.length), tp.retval));
+      this.self.propagate(tp.self);
+      for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i)
+        this.args[i].propagate(tp.args[i]);
+    }
+  });
+
+  infer.registerFunction("Function_bind", function(self, args) {
+    if (!args.length) return infer.ANull;
+    var result = new infer.AVal;
+    self.propagate(new IsBound(args[0], args.slice(1), result));
+    return result;
+  });
+
+  infer.registerFunction("Array_ctor", function(_self, args) {
+    var arr = new infer.Arr;
+    if (args.length != 1 || !args[0].hasType(infer.cx().num)) {
+      var content = arr.getProp("<i>");
+      for (var i = 0; i < args.length; ++i) args[i].propagate(content);
+    }
+    return arr;
+  });
+
+  infer.registerFunction("Promise_ctor", function(_self, args, argNodes) {
+    if (args.length < 1) return infer.ANull;
+    var self = new infer.Obj(infer.cx().definitions.ecma6["Promise.prototype"]);
+    var valProp = self.defProp("value", argNodes && argNodes[0]);
+    var valArg = new infer.AVal;
+    valArg.propagate(valProp);
+    var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull);
+    var reject = infer.cx().definitions.ecma6.promiseReject;
+    args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull));
+    return self;
+  });
+
+  return exports;
+});

Some files were not shown because too many files changed in this diff