浏览代码

Merge remote-tracking branch 'remotes/mrdoob/dev' into dev

alteredq 13 年之前
父节点
当前提交
e93a0fd847
共有 100 个文件被更改,包括 8001 次插入75 次删除
  1. 1 1
      README.md
  2. 4 9
      gui/index.html
  3. 8 0
      gui/js/UI.js
  4. 3 3
      gui/js/ui/Menubar.js
  5. 35 35
      gui/js/ui/Sidebar.Properties.Geometry.js
  6. 36 9
      gui/js/ui/Sidebar.Properties.Material.js
  7. 3 16
      gui/js/ui/Sidebar.Properties.Object3D.js
  8. 1 0
      gui/js/ui/Sidebar.js
  9. 13 2
      gui/js/ui/Viewport.js
  10. 177 0
      utils/build.js
  11. 1 0
      utils/node_modules/argsparser/.gitignore
  12. 4 0
      utils/node_modules/argsparser/Makefile
  13. 1 0
      utils/node_modules/argsparser/index.js
  14. 41 0
      utils/node_modules/argsparser/lib/argsparser.js
  15. 19 0
      utils/node_modules/argsparser/package.json
  16. 34 0
      utils/node_modules/argsparser/readme.md
  17. 39 0
      utils/node_modules/argsparser/test/test.js
  18. 4 0
      utils/node_modules/uglify-js/.npmignore
  19. 981 0
      utils/node_modules/uglify-js/README.html
  20. 578 0
      utils/node_modules/uglify-js/README.org
  21. 332 0
      utils/node_modules/uglify-js/bin/uglifyjs
  22. 75 0
      utils/node_modules/uglify-js/docstyle.css
  23. 2599 0
      utils/node_modules/uglify-js/lib/consolidator.js
  24. 75 0
      utils/node_modules/uglify-js/lib/object-ast.js
  25. 203 0
      utils/node_modules/uglify-js/lib/parse-js.js
  26. 2011 0
      utils/node_modules/uglify-js/lib/process.js
  27. 73 0
      utils/node_modules/uglify-js/lib/squeeze-more.js
  28. 24 0
      utils/node_modules/uglify-js/package.json
  29. 24 0
      utils/node_modules/uglify-js/package.json~
  30. 28 0
      utils/node_modules/uglify-js/test/beautify.js
  31. 403 0
      utils/node_modules/uglify-js/test/testparser.js
  32. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/array1.js
  33. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/array2.js
  34. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/array3.js
  35. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/array4.js
  36. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/assignment.js
  37. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/concatstring.js
  38. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/const.js
  39. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/empty-blocks.js
  40. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/forstatement.js
  41. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/if.js
  42. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/ifreturn.js
  43. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/ifreturn2.js
  44. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue10.js
  45. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue11.js
  46. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue13.js
  47. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue14.js
  48. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue16.js
  49. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue17.js
  50. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue20.js
  51. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue21.js
  52. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue25.js
  53. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue27.js
  54. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue278.js
  55. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue28.js
  56. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue29.js
  57. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue30.js
  58. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue34.js
  59. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue4.js
  60. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue48.js
  61. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue50.js
  62. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue53.js
  63. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue54.1.js
  64. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue68.js
  65. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue69.js
  66. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/issue9.js
  67. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/mangle.js
  68. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/null_string.js
  69. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/strict-equals.js
  70. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/var.js
  71. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/whitespace.js
  72. 1 0
      utils/node_modules/uglify-js/test/unit/compress/expected/with.js
  73. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/array1.js
  74. 4 0
      utils/node_modules/uglify-js/test/unit/compress/test/array2.js
  75. 4 0
      utils/node_modules/uglify-js/test/unit/compress/test/array3.js
  76. 6 0
      utils/node_modules/uglify-js/test/unit/compress/test/array4.js
  77. 20 0
      utils/node_modules/uglify-js/test/unit/compress/test/assignment.js
  78. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/concatstring.js
  79. 5 0
      utils/node_modules/uglify-js/test/unit/compress/test/const.js
  80. 4 0
      utils/node_modules/uglify-js/test/unit/compress/test/empty-blocks.js
  81. 10 0
      utils/node_modules/uglify-js/test/unit/compress/test/forstatement.js
  82. 6 0
      utils/node_modules/uglify-js/test/unit/compress/test/if.js
  83. 9 0
      utils/node_modules/uglify-js/test/unit/compress/test/ifreturn.js
  84. 16 0
      utils/node_modules/uglify-js/test/unit/compress/test/ifreturn2.js
  85. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue10.js
  86. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue11.js
  87. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue13.js
  88. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue14.js
  89. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue16.js
  90. 4 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue17.js
  91. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue20.js
  92. 6 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue21.js
  93. 7 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue25.js
  94. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue27.js
  95. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue278.js
  96. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue28.js
  97. 1 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue29.js
  98. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue30.js
  99. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue34.js
  100. 3 0
      utils/node_modules/uglify-js/test/unit/compress/test/issue4.js

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ three.js
 
 The aim of the project is to create a lightweight 3D library with a very low level of complexity — in other words, for dummies. The library provides <canvas>, <svg> and WebGL renderers.
 
-[Examples](http://mrdoob.github.com/three.js/) — [Documentation](http://mrdoob.github.com/three.js/docs/latest/)
+[Examples](http://mrdoob.github.com/three.js/) — [Documentation](http://mrdoob.github.com/three.js/docs/latest/) — [Questions](http://stackoverflow.com/questions/tagged/three.js)
 
 
 ### Usage ###

+ 4 - 9
gui/index.html

@@ -49,6 +49,7 @@
 				objectAdded: new SIGNALS.Signal(),
 				objectSelected: new SIGNALS.Signal(),
 				objectChanged: new SIGNALS.Signal(),
+				materialChanged: new SIGNALS.Signal(),
 				windowResize: new SIGNALS.Signal()
 
 			};
@@ -105,9 +106,7 @@
 
 						case 'js':
 
-							var loader = new THREE.GeometryLoader();
-
-							var geometry = loader.parse( JSON.parse( contents ) );
+							var geometry = new THREE.GeometryLoader().parse( JSON.parse( contents ) );
 							var material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
 							var mesh = new THREE.Mesh( geometry, material );
 
@@ -118,9 +117,7 @@
 
 						case 'obj':
 
-							var loader = new THREE.OBJLoader();
-
-							var object = loader.parse( contents );
+							var object = new THREE.OBJLoader().parse( contents );
 
 							signals.objectAdded.dispatch( object );
 							signals.objectSelected.dispatch( object );
@@ -135,9 +132,7 @@
 
 						case 'vtk':
 
-							var loader = new THREE.VTKLoader();
-
-							var geometry = loader.parse( contents );
+							var geometry = new THREE.VTKLoader().parse( contents );
 							var material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
 							var mesh = new THREE.Mesh( geometry, material );
 

+ 8 - 0
gui/js/UI.js

@@ -176,8 +176,16 @@ UI.Element.prototype = {
 		this.setStyle( 'display', arguments );
 		return this;
 
+	},
+
+	setOverflow: function () {
+
+		this.setStyle( 'overflow', arguments );
+		return this;
+
 	}
 
+
 }
 
 

+ 3 - 3
gui/js/ui/Menubar.js

@@ -5,9 +5,9 @@ var Menubar = function ( signals ) {
 
 	var options = new UI.Panel();
 	options.setMargin( '8px' );
-	options.add( new UI.Text().setText( 'File' ).setColor( '#666' ).setMarginRight( '10px' ) );
-	options.add( new UI.Text().setText( 'Edit' ).setColor( '#666' ).setMarginRight( '10px' ) );
-	options.add( new UI.Text().setText( 'About' ).setColor( '#666' ).setMarginRight( '10px' ) );
+	options.add( new UI.Text().setText( 'File' ).setColor( '#666' ).setMarginRight( '20px' ) );
+	options.add( new UI.Text().setText( 'Edit' ).setColor( '#666' ).setMarginRight( '20px' ) );
+	options.add( new UI.Text().setText( 'About' ).setColor( '#666' ).setMarginRight( '20px' ) );
 	container.add( options );
 
 	return container;

+ 35 - 35
gui/js/ui/Sidebar.Properties.Geometry.js

@@ -4,40 +4,27 @@ Sidebar.Properties.Geometry = function ( signals ) {
 	container.setDisplay( 'none' );
 
 	container.add( new UI.Text().setText( 'GEOMETRY' ).setColor( '#666' ) );
-
-	var button = new UI.Button( 'absolute' ).setRight( '0px' ).setText( 'Export' ).onClick( exportGeometry );
-	button.download = 'test.js';
-	container.add( button );
-
+	container.add( new UI.Button( 'absolute' ).setRight( '0px' ).setText( 'Export' ).onClick( exportGeometry ) );
 	container.add( new UI.Break(), new UI.Break() );
 
 	container.add( new UI.Text().setText( 'Name' ).setColor( '#666' ) );
-
 	var geometryName = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
-
 	container.add( geometryName );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Class' ).setColor( '#666' ) );
-
 	var geometryClass = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
 	container.add( geometryClass );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Vertices' ).setColor( '#666' ) );
-	
 	var verticesCount = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
 	container.add( verticesCount );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Faces' ).setColor( '#666' ) );
-
 	var facesCount = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
 	container.add( facesCount );
-
 	container.add( new UI.Break(), new UI.Break(), new UI.Break() );
 
 	//
@@ -107,6 +94,7 @@ Sidebar.Properties.Geometry = function ( signals ) {
 		var faces = [];
 		var uvs = [[]];
 		var normals = [];
+		var normalsHash = {};
 
 		for ( var i = 0; i < geometry.faces.length; i ++ ) {
 
@@ -123,15 +111,14 @@ Sidebar.Properties.Geometry = function ( signals ) {
 
 			var faceType = 0
 			faceType = setBit( faceType, 0, ! isTriangle );
-			/*
-			faceType = setBit( faceType, 1, hasMaterial );
-			faceType = setBit( faceType, 2, hasFaceUv );
-			faceType = setBit( faceType, 3, hasFaceVertexUv );
+			
+			// faceType = setBit( faceType, 1, hasMaterial );
+			// faceType = setBit( faceType, 2, hasFaceUv );
+			// faceType = setBit( faceType, 3, hasFaceVertexUv );
 			faceType = setBit( faceType, 4, hasFaceNormal );
 			faceType = setBit( faceType, 5, hasFaceVertexNormal );
-			faceType = setBit( faceType, 6, hasFaceColor );
-			faceType = setBit( faceType, 7, hasFaceVertexColor );
-			*/
+			// faceType = setBit( faceType, 6, hasFaceColor );
+			// faceType = setBit( faceType, 7, hasFaceVertexColor );
 
 			faces.push( faceType );
 
@@ -189,37 +176,33 @@ Sidebar.Properties.Geometry = function ( signals ) {
 
 			if ( hasFaceNormal ) {
 
-				/*
-				var normal = face.normal;
-				faces.push( normal.x, normal.y, normal.z );
-				*/
+				var faceNormal = face.normal;
+				faces.push( getNormalIndex( faceNormal.x, faceNormal.y, faceNormal.z ) );
 
 			}
 
 			if ( hasFaceVertexNormal ) {
 
-				/*
-				var normals = face.vertexNormals;
+				var vertexNormals = face.vertexNormals;
 
 				if ( isTriangle ) {
 
 					faces.push(
-						normals[ 0 ].u, normals[ 0 ].y, normals[ 0 ].z,
-						normals[ 1 ].u, normals[ 1 ].y, normals[ 1 ].z,
-						normals[ 2 ].u, normals[ 2 ].y, normals[ 2 ].z
+						getNormalIndex( vertexNormals[ 0 ].x, vertexNormals[ 0 ].y, vertexNormals[ 0 ].z ),
+						getNormalIndex( vertexNormals[ 1 ].x, vertexNormals[ 1 ].y, vertexNormals[ 1 ].z ),
+						getNormalIndex( vertexNormals[ 2 ].x, vertexNormals[ 2 ].y, vertexNormals[ 2 ].z )
 					);
 
 				} else {
 
 					faces.push(
-						normals[ 0 ].x, normals[ 0 ].y, normals[ 0 ].z,
-						normals[ 1 ].x, normals[ 1 ].y, normals[ 1 ].z,
-						normals[ 2 ].x, normals[ 2 ].y, normals[ 2 ].z,
-						normals[ 3 ].x, normals[ 3 ].y, normals[ 3 ].z
+						getNormalIndex( vertexNormals[ 0 ].x, vertexNormals[ 0 ].y, vertexNormals[ 0 ].z ),
+						getNormalIndex( vertexNormals[ 1 ].x, vertexNormals[ 1 ].y, vertexNormals[ 1 ].z ),
+						getNormalIndex( vertexNormals[ 2 ].x, vertexNormals[ 2 ].y, vertexNormals[ 2 ].z ),
+						getNormalIndex( vertexNormals[ 3 ].x, vertexNormals[ 3 ].y, vertexNormals[ 3 ].z )
 					);
 
 				}
-				*/
 
 			}
 
@@ -231,6 +214,23 @@ Sidebar.Properties.Geometry = function ( signals ) {
 
 		}
 
+		function getNormalIndex( x, y, z ) {
+
+				var hash = x.toString() + y.toString() + z.toString();
+
+				if ( normalsHash[ hash ] !== undefined ) { 
+
+					return normalsHash[ hash ];
+
+				}
+
+				normalsHash[ hash ] = normals.length / 3;
+				normals.push( x, y, z );
+
+				return normalsHash[ hash ];
+
+		}
+
 		//
 
 		var output = [

+ 36 - 9
gui/js/ui/Sidebar.Properties.Material.js

@@ -4,45 +4,72 @@ Sidebar.Properties.Material = function ( signals ) {
 	container.setDisplay( 'none' );
 
 	container.add( new UI.Text().setText( 'MATERIAL' ).setColor( '#666' ) );
-
 	container.add( new UI.Break(), new UI.Break() );
 
 	container.add( new UI.Text().setText( 'Name' ).setColor( '#666' ) );
-
 	var materialName = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
-
 	container.add( materialName );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Class' ).setColor( '#666' ) );
-
 	var materialClass = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
-
 	container.add( materialClass );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Color' ).setColor( '#666' ) );
-
 	var materialColor = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
-
 	container.add( materialColor );
+	container.add( new UI.HorizontalRule() );
+
+	container.add( new UI.Text().setText( 'Map' ).setColor( '#666' ) );
+	var materialMap = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
+	container.add( materialMap );
+	container.add( new UI.HorizontalRule() );
+
+	container.add( new UI.Text().setText( 'Opacity' ).setColor( '#666' ) );
+	var materialOpacity = new UI.FloatNumber( 'absolute' ).setLeft( '90px' ).setFontSize( '12px' ).onChange( update );
+	container.add( materialOpacity );
+	container.add( new UI.HorizontalRule() );
+
+	container.add( new UI.Text().setText( 'Transparent' ).setColor( '#666' ) );
+	var materialTransparent = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
+	container.add( materialTransparent );
 
 	//
 
+	var selected = null;
+
+	function update() {
+
+		if ( selected ) {
+
+			selected.opacity = materialOpacity.getValue();
+
+			signals.materialChanged.dispatch( selected );
+
+		}
+
+	}
+
 	signals.objectSelected.add( function ( object ) {
 
 		if ( object && object.material ) {
 
+			selected = object.material;
+
 			container.setDisplay( 'block' );
 
 			materialName.setText( object.material.name );
 			materialClass.setText( getMaterialInstanceName( object.material ) );
 			materialColor.setText( '#' + object.material.color.getHex().toString(16) );
+			materialMap.setText( object.material.map );
+			materialOpacity.setValue( object.material.opacity );
+			materialTransparent.setText( object.material.transparent );
 
 		} else {
 
+			selected = null;
+
 			container.setDisplay( 'none' );
 
 		}

+ 3 - 16
gui/js/ui/Sidebar.Properties.Object3D.js

@@ -1,54 +1,41 @@
 Sidebar.Properties.Object3D = function ( signals ) {
 
-	var selected = null;
-
 	var container = new UI.Panel();
 	container.setDisplay( 'none' );
 
 	container.add( new UI.Text().setText( 'OBJECT' ).setColor( '#666' ) );
-
 	container.add( new UI.Break(), new UI.Break() );
 
 	container.add( new UI.Text().setText( 'Name' ).setColor( '#666' ) );
-
-	var objectName = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' ).setFontSize( '12px' );
-
+	var objectName = new UI.Text( 'absolute' ).setLeft( '90px' ).setColor( '#444' );
 	container.add( objectName );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Position' ).setColor( '#666' ) );
-
 	var positionX = new UI.FloatNumber( 'absolute' ).setLeft( '90px' ).onChange( update );
 	var positionY = new UI.FloatNumber( 'absolute' ).setLeft( '160px' ).onChange( update );
 	var positionZ = new UI.FloatNumber( 'absolute' ).setLeft( '230px' ).onChange( update );
-
 	container.add( positionX, positionY, positionZ );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Rotation' ).setColor( '#666' ) );
-
 	var rotationX = new UI.FloatNumber( 'absolute' ).setLeft( '90px' ).onChange( update );
 	var rotationY = new UI.FloatNumber( 'absolute' ).setLeft( '160px' ).onChange( update );
 	var rotationZ = new UI.FloatNumber( 'absolute' ).setLeft( '230px' ).onChange( update );
-
 	container.add( rotationX, rotationY, rotationZ );
-
 	container.add( new UI.HorizontalRule() );
 
 	container.add( new UI.Text().setText( 'Scale' ).setColor( '#666' ) );
-
 	var scaleX = new UI.FloatNumber( 'absolute' ).setValue( 1 ).setLeft( '90px' ).onChange( update );
 	var scaleY = new UI.FloatNumber( 'absolute' ).setValue( 1 ).setLeft( '160px' ).onChange( update );
 	var scaleZ = new UI.FloatNumber( 'absolute' ).setValue( 1 ).setLeft( '230px' ).onChange( update );
-
 	container.add( scaleX, scaleY, scaleZ );
-
 	container.add( new UI.Break(), new UI.Break(), new UI.Break() );
 
 	//
 
+	var selected = null;
+
 	function update() {
 
 		if ( selected ) {

+ 1 - 0
gui/js/ui/Sidebar.js

@@ -3,6 +3,7 @@ var Sidebar = function ( signals ) {
 	var container = new UI.Panel( 'absolute' );
 	container.setWidth( '300px' ).setHeight( '100%' );
 	container.setBackgroundColor( '#eee' );
+	container.setOverflow( 'auto' );
 
 	var outliner = new Sidebar.Outliner( signals );
 	container.add( outliner );

+ 13 - 2
gui/js/ui/Viewport.js

@@ -46,6 +46,7 @@ var Viewport = function ( signals ) {
 	camera.lookAt( scene.position );
 	scene.add( camera );
 
+	/*
 	var controls = new THREE.TrackballControls( camera, container.dom );
 	controls.rotateSpeed = 1.0;
 	controls.zoomSpeed = 1.2;
@@ -55,6 +56,10 @@ var Viewport = function ( signals ) {
 	controls.staticMoving = true;
 	controls.dynamicDampingFactor = 0.3;
 	controls.addEventListener( 'change', render );
+	*/
+
+	var controls = new THREE.OrbitControls( camera, container.dom );
+	controls.addEventListener( 'change', render );
 
 	var light = new THREE.DirectionalLight( 0xffffff );
 	light.position.set( 1, 0.5, 0 ).normalize();
@@ -184,6 +189,12 @@ var Viewport = function ( signals ) {
 
 	} );
 
+	signals.materialChanged.add( function ( material ) {
+
+		render();
+
+	} );
+
 	signals.windowResize.add( function () {
 
 		camera.aspect = container.dom.offsetWidth / container.dom.offsetHeight;
@@ -215,12 +226,12 @@ var Viewport = function ( signals ) {
 
 	function render() {
 
-		scene.updateMatrixWorld();
 		sceneHelpers.updateMatrixWorld();
+		scene.updateMatrixWorld();
 
 		renderer.clear();
-		renderer.render( scene, camera );
 		renderer.render( sceneHelpers, camera );
+		renderer.render( scene, camera );
 
 	}
 

+ 177 - 0
utils/build.js

@@ -0,0 +1,177 @@
+var fs = require("fs");
+var path = require("path");
+var argsparser =  require( "argsparser" );
+var uglify = require("uglify-js");
+
+
+function merge(files){
+	"use strict";
+	var buffer = [];
+	for (var i = 0,il = files.length;i<il;i++){
+		var fileName = path.join("src", files[i]);
+		buffer.push(fs.readFileSync(fileName,'utf8'));
+	}
+	
+	return buffer.join("");
+
+}
+
+function output(text, filename){
+	"use strict";
+    var file = path.join('build', filename);
+    fs.writeFileSync(file,text,'utf8');
+}
+
+
+function compress(text, fname_externs){
+	/*
+
+	externs = ""
+	if fname_externs:
+		externs = "--externs %s.js" % fname_externs
+
+	in_tuple = tempfile.mkstemp()
+	with os.fdopen(in_tuple[0], 'w') as handle:
+		handle.write(text)
+
+	out_tuple = tempfile.mkstemp()
+
+	os.system("java -jar compiler/compiler.jar --warning_level=VERBOSE --jscomp_off=globalThis --jscomp_off=checkTypes --externs externs_common.js %s --language_in=ECMASCRIPT5_STRICT --js %s --js_output_file %s" % (externs, in_tuple[1], out_tuple[1]))
+
+	with os.fdopen(out_tuple[0], 'r') as handle:
+		compressed = handle.read()
+
+	os.unlink(in_tuple[1])
+	os.unlink(out_tuple[1])
+
+	return compressed*/
+	"use strict";	
+	return uglify(text);
+}
+
+function addHeader(text, endFilename){
+	"use strict";
+	return "// " + endFilename + " - http://github.com/mrdoob/three.js\n" + text;
+	
+}
+
+function makeDebug(text){
+	"use strict";
+	var position = 0;
+	while (true){
+		position = text.indexOf("/* DEBUG", position);
+		if (position == -1){
+			break;
+		}
+		text = text.substring(0,position) + text.substring(position+8);
+		position = text.find("*/", position);
+		text = text.substring(0,position) + text.substring(position+2);
+	}
+	return text;
+}
+
+function buildLib(files, debug, minified, filename, fname_externs){
+	"use strict";
+	var text = merge(files);
+
+	if (debug){
+		text = makeDebug(text);
+		filename = filename + 'Debug';
+	}
+	
+	var folder;
+	if (filename == "Three"){
+		folder = '';
+	} else {
+		folder = 'custom/';
+	}
+
+	filename = filename + '.js';
+
+	//print("=" * 40)
+	console.log("========================================");
+	console.log("Compiling " + filename);
+	//print("=" * 40)
+	console.log("========================================");
+
+	if (minified){
+		text = compress(text, fname_externs);
+	}
+
+	output(addHeader(text, filename), folder + filename);
+
+}
+
+function buildIncludes(files, filename){
+	"use strict";
+	//var template = "\t\t<script src='../src/%s'></script>";
+	//var text = "\n".join(template % f for f in files)
+	var text = [];
+	for (var i = 0,il = files.length;i<il;i++){
+		text.push("\t\t<script src='../src/" + files[i] + "'></script>");
+	}
+	
+	output(text.join("\n"), filename + '.js');
+}
+
+function getFileNames(){
+	"use strict";
+	var fileName = "utils/files.json";
+	var data = JSON.parse(fs.readFileSync(fileName,'utf8'));	
+	return data;
+}
+
+function parse_args(){
+	"use strict";
+	//parse 
+	var returnValue = argsparser.parse();
+	/*
+	# If no arguments have been passed, show the help message and exit
+	if len(sys.argv) == 1:
+		parser.print_help()
+		sys.exit(1)
+*/
+	for (var i in returnValue){
+		if (i.substring(0,2) == "--"){
+			returnValue[i.substring(2)] = returnValue[i];
+			delete returnValue[i];
+		} else {
+			delete returnValue[i];
+		}
+	}
+	return returnValue;
+}
+
+function main(){
+	"use strict";
+	var args = parse_args();
+	var debug = args.debug;
+	var minified = args.minified;
+	var files = getFileNames();
+
+	var config = [
+	['Three', 'includes', '', files["COMMON"].concat(files["EXTRAS"]), args.common],
+	['ThreeCanvas', 'includes_canvas', '', files["CANVAS"], args.canvas],
+	['ThreeWebGL', 'includes_webgl', '', files["WEBGL"], args.webgl],
+	['ThreeExtras', 'includes_extras', 'externs_extras', files["EXTRAS"], args.extras]
+	];
+
+
+	for (var i = 0,il = config.length;i<il;i++){
+		var chosenConfig = config[i],
+			fname_lib = chosenConfig[0], 
+			fname_inc = chosenConfig[1], 
+			fname_externs = chosenConfig[2], 
+			files = chosenConfig[3], 
+			enabled = chosenConfig[4];
+		if (enabled || args.all){
+			buildLib(files, debug, minified, fname_lib, fname_externs);
+			if (args.includes){
+				buildIncludes(files, fname_inc);
+			}
+		}
+	}
+}
+main();
+
+

+ 1 - 0
utils/node_modules/argsparser/.gitignore

@@ -0,0 +1 @@
+.DS_Store

+ 4 - 0
utils/node_modules/argsparser/Makefile

@@ -0,0 +1,4 @@
+test:
+	node test/test.js
+
+.PHONY: test

+ 1 - 0
utils/node_modules/argsparser/index.js

@@ -0,0 +1 @@
+module.exports = require('./lib/argsparser');

+ 41 - 0
utils/node_modules/argsparser/lib/argsparser.js

@@ -0,0 +1,41 @@
+/**
+ * Parser arguments array
+ * @param {Array} args optional arguments arrray.
+ * @return {Object} opts key value hash.
+ * @export
+ */
+exports.parse = function(args) {
+    // args is optional, default is process.argv
+    args = args || process.argv;
+
+    var opts = {}, curSwitch;
+
+    args.forEach(function(arg) {
+        // its a switch
+        if (/^(-|--)/.test(arg) || !curSwitch) {
+            opts[arg] = true;
+            curSwitch = arg;
+        // this arg is a data
+        } else {
+            if (arg === 'false') {
+                arg = false;
+            } else if (arg === 'true') {
+                arg = true;
+            } else if (!isNaN(arg)) {
+                arg = Number(arg);
+            }
+
+            // it was a boolean switch per default, 
+            // now it has got a val
+            if (typeof opts[curSwitch] === 'boolean') {
+                opts[curSwitch] = arg;
+            } else if (Array.isArray(opts[curSwitch])) {
+                opts[curSwitch].push(arg);
+            } else {
+                opts[curSwitch] = [opts[curSwitch], arg];
+            }
+        }
+    });
+
+    return opts;
+};

+ 19 - 0
utils/node_modules/argsparser/package.json

@@ -0,0 +1,19 @@
+{
+    "name": "argsparser",
+    "description": "A tiny command line arguments parser",
+    "version": "0.0.6",
+    "author": "Oleg Slobodskoi <[email protected]>",
+    "repository": {
+        "type": "git",
+        "url": "http://github.com/kof/node-argsparser.git"
+    },
+    "keywords": [ "arguments", "options", "command line", "parser" ],
+    "engines": { "node": ">= 0.2.0" },
+    "scripts": { "test": "node ./test/test.js"  },
+    "licenses": [
+        { 
+            "type": "MIT",
+            "url" : "http://www.opensource.org/licenses/mit-license.php"
+        }
+    ]
+}

+ 34 - 0
utils/node_modules/argsparser/readme.md

@@ -0,0 +1,34 @@
+## Yet another tiny arguments parser for node
+
+## Features
+ * extremely tiny
+ * instead to parse all possible spellings, it uses just some simple rules
+
+## How this parser works
+The target is to get a key-value object from an array. A key can be the first element or element prefixed by "-" and "--" (switch). 
+So the parser loops through the array and looks for keys. After he could detect an a key all next elements will be added as a value of this key until he find another key.
+If there is no value, then the key is true (boolean). If there are a lot of values, then the key is an array.
+
+## Examples
+
+node script.js -> {"node": "script.js"}
+
+node script.js -o -> {"node": "script.js", "-o": true}
+
+node script.js -o test -> {"node": "script.js", "-o": "test"}
+
+node script.js -a testa --b testb -> {node: "script.js", "-a": "testa", "--b": "testb"}
+ 
+node script.js -paths /test.js /test1.js -> {node: "script.js", "-paths": ["/test.js", "/test1.js"]}
+
+## Usage
+
+    // per default it parses process.argv
+    var args = require( "argsparser" ).parse(); // {"node": "/path/to/your/script.js"}
+    
+    // optional you can pass your own arguments array
+    var args = require( "argsparser" ).parse(["-a", "test"]); // {"-a": "test"}
+
+    
+## Installation
+    npm install argsparser    

+ 39 - 0
utils/node_modules/argsparser/test/test.js

@@ -0,0 +1,39 @@
+var a = require('assert'),
+    util = require('util'),
+    parse = require('../lib/argsparser').parse;
+
+util.print('Run tests...\n');
+
+a.deepEqual(parse(), {node: __filename}, 'node script.js');
+
+a.deepEqual(parse(['-o']), {'-o': true}, 'node script.js -o');
+
+a.deepEqual(parse(['-o', 'true']), {'-o': true}, 'node script.js -o true');
+
+a.deepEqual(parse(['-o', 'false']), {'-o': false}, 'node script.js -o false');
+
+a.deepEqual(parse(['-o', '123']), {'-o': 123}, 'node script.js -o 123');
+
+a.deepEqual(parse(['--token', 'bla--bla']), {'--token': 'bla--bla'}, 'node script.js --token bla--bla');
+
+a.deepEqual(parse(['-o', '123.456']), {'-o': 123.456}, 'node script.js -o 123.456');
+
+a.deepEqual(parse(['-o', 'test']), {'-o': 'test'}, 'node script.js -o test');
+
+a.deepEqual(parse(['-a', 'testa', '-b', 'testb']), {'-a': 'testa', '-b': 'testb'}, 'node script.js -a testa -b testb');
+
+a.deepEqual(parse(['--a', 'testa', '--b', 'testb']), {'--a': 'testa', '--b': 'testb'}, 'node script.js --a testa --b testb ');
+
+a.deepEqual(parse(['-a', 'testa', '--b', 'testb']), {'-a': 'testa', '--b': 'testb'}, 'node script.js -a testa --b testb');
+
+a.deepEqual(parse(['--a', 'testa', '-b', 'testb']), {'--a': 'testa', '-b': 'testb'}, 'node script.js --a testa -b testb');
+
+a.deepEqual(parse(['-paths', '/test.js', '/test1.js']), {'-paths': ['/test.js', '/test1.js']}, 'node script.js -paths /test.js /test1.js');
+
+a.deepEqual(parse(['--paths', '/test.js', '/test1.js']), {'--paths': ['/test.js', '/test1.js']}, 'node script.js --paths /test.js /test1.js');
+
+a.deepEqual(parse(['--paths', '/test.js', '/test1.js', '-a', 'testa']), {'--paths': ['/test.js', '/test1.js'], '-a': 'testa'}, 'node script.js --paths /test.js /test1.js -a testa');
+
+a.deepEqual(parse(['--port', '80', '8080']), {'--port': [80, 8080]}, 'node server.js --port 80 8080');
+
+util.print('All tests ok\n');

+ 4 - 0
utils/node_modules/uglify-js/.npmignore

@@ -0,0 +1,4 @@
+.DS_Store
+.tmp*~
+*.local.*
+.pinf-*

+ 981 - 0
utils/node_modules/uglify-js/README.html

@@ -0,0 +1,981 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+lang="en" xml:lang="en">
+<head>
+<title>UglifyJS &ndash; a JavaScript parser/compressor/beautifier</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+<meta name="generator" content="Org-mode"/>
+<meta name="generated" content="2011-12-09 14:59:08 EET"/>
+<meta name="author" content="Mihai Bazon"/>
+<meta name="description" content="a JavaScript parser/compressor/beautifier in JavaScript"/>
+<meta name="keywords" content="javascript, js, parser, compiler, compressor, mangle, minify, minifier"/>
+<style type="text/css">
+ <!--/*--><![CDATA[/*><!--*/
+  html { font-family: Times, serif; font-size: 12pt; }
+  .title  { text-align: center; }
+  .todo   { color: red; }
+  .done   { color: green; }
+  .tag    { background-color: #add8e6; font-weight:normal }
+  .target { }
+  .timestamp { color: #bebebe; }
+  .timestamp-kwd { color: #5f9ea0; }
+  .right  {margin-left:auto; margin-right:0px;  text-align:right;}
+  .left   {margin-left:0px;  margin-right:auto; text-align:left;}
+  .center {margin-left:auto; margin-right:auto; text-align:center;}
+  p.verse { margin-left: 3% }
+  pre {
+	border: 1pt solid #AEBDCC;
+	background-color: #F3F5F7;
+	padding: 5pt;
+	font-family: courier, monospace;
+        font-size: 90%;
+        overflow:auto;
+  }
+  table { border-collapse: collapse; }
+  td, th { vertical-align: top;  }
+  th.right  { text-align:center;  }
+  th.left   { text-align:center;   }
+  th.center { text-align:center; }
+  td.right  { text-align:right;  }
+  td.left   { text-align:left;   }
+  td.center { text-align:center; }
+  dt { font-weight: bold; }
+  div.figure { padding: 0.5em; }
+  div.figure p { text-align: center; }
+  div.inlinetask {
+    padding:10px;
+    border:2px solid gray;
+    margin:10px;
+    background: #ffffcc;
+  }
+  textarea { overflow-x: auto; }
+  .linenr { font-size:smaller }
+  .code-highlighted {background-color:#ffff00;}
+  .org-info-js_info-navigation { border-style:none; }
+  #org-info-js_console-label { font-size:10px; font-weight:bold;
+                               white-space:nowrap; }
+  .org-info-js_search-highlight {background-color:#ffff00; color:#000000;
+                                 font-weight:bold; }
+  /*]]>*/-->
+</style>
+<link rel="stylesheet" type="text/css" href="docstyle.css" />
+<script type="text/javascript">
+<!--/*--><![CDATA[/*><!--*/
+ function CodeHighlightOn(elem, id)
+ {
+   var target = document.getElementById(id);
+   if(null != target) {
+     elem.cacheClassElem = elem.className;
+     elem.cacheClassTarget = target.className;
+     target.className = "code-highlighted";
+     elem.className   = "code-highlighted";
+   }
+ }
+ function CodeHighlightOff(elem, id)
+ {
+   var target = document.getElementById(id);
+   if(elem.cacheClassElem)
+     elem.className = elem.cacheClassElem;
+   if(elem.cacheClassTarget)
+     target.className = elem.cacheClassTarget;
+ }
+/*]]>*///-->
+</script>
+
+</head>
+<body>
+
+<div id="preamble">
+
+</div>
+
+<div id="content">
+<h1 class="title">UglifyJS &ndash; a JavaScript parser/compressor/beautifier</h1>
+
+
+<div id="table-of-contents">
+<h2>Table of Contents</h2>
+<div id="text-table-of-contents">
+<ul>
+<li><a href="#sec-1">1 UglifyJS &mdash; a JavaScript parser/compressor/beautifier </a>
+<ul>
+<li><a href="#sec-1-1">1.1 Unsafe transformations </a>
+<ul>
+<li><a href="#sec-1-1-1">1.1.1 Calls involving the global Array constructor </a></li>
+<li><a href="#sec-1-1-2">1.1.2 <code>obj.toString()</code> ==&gt; <code>obj+“”</code> </a></li>
+</ul>
+</li>
+<li><a href="#sec-1-2">1.2 Install (NPM) </a></li>
+<li><a href="#sec-1-3">1.3 Install latest code from GitHub </a></li>
+<li><a href="#sec-1-4">1.4 Usage </a>
+<ul>
+<li><a href="#sec-1-4-1">1.4.1 API </a></li>
+<li><a href="#sec-1-4-2">1.4.2 Beautifier shortcoming &ndash; no more comments </a></li>
+<li><a href="#sec-1-4-3">1.4.3 Use as a code pre-processor </a></li>
+</ul>
+</li>
+<li><a href="#sec-1-5">1.5 Compression &ndash; how good is it? </a></li>
+<li><a href="#sec-1-6">1.6 Bugs? </a></li>
+<li><a href="#sec-1-7">1.7 Links </a></li>
+<li><a href="#sec-1-8">1.8 License </a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+
+<div id="outline-container-1" class="outline-2">
+<h2 id="sec-1"><span class="section-number-2">1</span> UglifyJS &mdash; a JavaScript parser/compressor/beautifier </h2>
+<div class="outline-text-2" id="text-1">
+
+
+<p>
+This package implements a general-purpose JavaScript
+parser/compressor/beautifier toolkit.  It is developed on <a href="http://nodejs.org/">NodeJS</a>, but it
+should work on any JavaScript platform supporting the CommonJS module system
+(and if your platform of choice doesn't support CommonJS, you can easily
+implement it, or discard the <code>exports.*</code> lines from UglifyJS sources).
+</p>
+<p>
+The tokenizer/parser generates an abstract syntax tree from JS code.  You
+can then traverse the AST to learn more about the code, or do various
+manipulations on it.  This part is implemented in <a href="../lib/parse-js.js">parse-js.js</a> and it's a
+port to JavaScript of the excellent <a href="http://marijn.haverbeke.nl/parse-js/">parse-js</a> Common Lisp library from <a href="http://marijn.haverbeke.nl/">Marijn Haverbeke</a>.
+</p>
+<p>
+( See <a href="http://github.com/mishoo/cl-uglify-js">cl-uglify-js</a> if you're looking for the Common Lisp version of
+UglifyJS. )
+</p>
+<p>
+The second part of this package, implemented in <a href="../lib/process.js">process.js</a>, inspects and
+manipulates the AST generated by the parser to provide the following:
+</p>
+<ul>
+<li>ability to re-generate JavaScript code from the AST.  Optionally
+  indented&mdash;you can use this if you want to “beautify” a program that has
+  been compressed, so that you can inspect the source.  But you can also run
+  our code generator to print out an AST without any whitespace, so you
+  achieve compression as well.
+
+</li>
+<li>shorten variable names (usually to single characters).  Our mangler will
+  analyze the code and generate proper variable names, depending on scope
+  and usage, and is smart enough to deal with globals defined elsewhere, or
+  with <code>eval()</code> calls or <code>with{}</code> statements.  In short, if <code>eval()</code> or
+  <code>with{}</code> are used in some scope, then all variables in that scope and any
+  variables in the parent scopes will remain unmangled, and any references
+  to such variables remain unmangled as well.
+
+</li>
+<li>various small optimizations that may lead to faster code but certainly
+  lead to smaller code.  Where possible, we do the following:
+
+<ul>
+<li>foo["bar"]  ==&gt;  foo.bar
+
+</li>
+<li>remove block brackets <code>{}</code>
+
+</li>
+<li>join consecutive var declarations:
+    var a = 10; var b = 20; ==&gt; var a=10,b=20;
+
+</li>
+<li>resolve simple constant expressions: 1 +2 * 3 ==&gt; 7.  We only do the
+    replacement if the result occupies less bytes; for example 1/3 would
+    translate to 0.333333333333, so in this case we don't replace it.
+
+</li>
+<li>consecutive statements in blocks are merged into a sequence; in many
+    cases, this leaves blocks with a single statement, so then we can remove
+    the block brackets.
+
+</li>
+<li>various optimizations for IF statements:
+
+<ul>
+<li>if (foo) bar(); else baz(); ==&gt; foo?bar():baz();
+</li>
+<li>if (!foo) bar(); else baz(); ==&gt; foo?baz():bar();
+</li>
+<li>if (foo) bar(); ==&gt; foo&amp;&amp;bar();
+</li>
+<li>if (!foo) bar(); ==&gt; foo||bar();
+</li>
+<li>if (foo) return bar(); else return baz(); ==&gt; return foo?bar():baz();
+</li>
+<li>if (foo) return bar(); else something(); ==&gt; {if(foo)return bar();something()}
+
+</li>
+</ul>
+
+</li>
+<li>remove some unreachable code and warn about it (code that follows a
+    <code>return</code>, <code>throw</code>, <code>break</code> or <code>continue</code> statement, except
+    function/variable declarations).
+
+</li>
+<li>act a limited version of a pre-processor (c.f. the pre-processor of
+    C/C++) to allow you to safely replace selected global symbols with
+    specified values.  When combined with the optimisations above this can
+    make UglifyJS operate slightly more like a compilation process, in
+    that when certain symbols are replaced by constant values, entire code
+    blocks may be optimised away as unreachable.
+</li>
+</ul>
+
+</li>
+</ul>
+
+
+
+</div>
+
+<div id="outline-container-1-1" class="outline-3">
+<h3 id="sec-1-1"><span class="section-number-3">1.1</span> <span class="target">Unsafe transformations</span>  </h3>
+<div class="outline-text-3" id="text-1-1">
+
+
+<p>
+The following transformations can in theory break code, although they're
+probably safe in most practical cases.  To enable them you need to pass the
+<code>--unsafe</code> flag.
+</p>
+
+</div>
+
+<div id="outline-container-1-1-1" class="outline-4">
+<h4 id="sec-1-1-1"><span class="section-number-4">1.1.1</span> Calls involving the global Array constructor </h4>
+<div class="outline-text-4" id="text-1-1-1">
+
+
+<p>
+The following transformations occur:
+</p>
+
+
+
+<pre class="src src-js"><span class="org-keyword">new</span> <span class="org-type">Array</span>(1, 2, 3, 4)  =&gt; [1,2,3,4]
+Array(a, b, c)         =&gt; [a,b,c]
+<span class="org-keyword">new</span> <span class="org-type">Array</span>(5)           =&gt; Array(5)
+<span class="org-keyword">new</span> <span class="org-type">Array</span>(a)           =&gt; Array(a)
+</pre>
+
+
+<p>
+These are all safe if the Array name isn't redefined.  JavaScript does allow
+one to globally redefine Array (and pretty much everything, in fact) but I
+personally don't see why would anyone do that.
+</p>
+<p>
+UglifyJS does handle the case where Array is redefined locally, or even
+globally but with a <code>function</code> or <code>var</code> declaration.  Therefore, in the
+following cases UglifyJS <b>doesn't touch</b> calls or instantiations of Array:
+</p>
+
+
+
+<pre class="src src-js"><span class="org-comment-delimiter">// </span><span class="org-comment">case 1.  globally declared variable</span>
+  <span class="org-keyword">var</span> <span class="org-variable-name">Array</span>;
+  <span class="org-keyword">new</span> <span class="org-type">Array</span>(1, 2, 3);
+  Array(a, b);
+
+  <span class="org-comment-delimiter">// </span><span class="org-comment">or (can be declared later)</span>
+  <span class="org-keyword">new</span> <span class="org-type">Array</span>(1, 2, 3);
+  <span class="org-keyword">var</span> <span class="org-variable-name">Array</span>;
+
+  <span class="org-comment-delimiter">// </span><span class="org-comment">or (can be a function)</span>
+  <span class="org-keyword">new</span> <span class="org-type">Array</span>(1, 2, 3);
+  <span class="org-keyword">function</span> <span class="org-function-name">Array</span>() { ... }
+
+<span class="org-comment-delimiter">// </span><span class="org-comment">case 2.  declared in a function</span>
+  (<span class="org-keyword">function</span>(){
+    a = <span class="org-keyword">new</span> <span class="org-type">Array</span>(1, 2, 3);
+    b = Array(5, 6);
+    <span class="org-keyword">var</span> <span class="org-variable-name">Array</span>;
+  })();
+
+  <span class="org-comment-delimiter">// </span><span class="org-comment">or</span>
+  (<span class="org-keyword">function</span>(<span class="org-variable-name">Array</span>){
+    <span class="org-keyword">return</span> Array(5, 6, 7);
+  })();
+
+  <span class="org-comment-delimiter">// </span><span class="org-comment">or</span>
+  (<span class="org-keyword">function</span>(){
+    <span class="org-keyword">return</span> <span class="org-keyword">new</span> <span class="org-type">Array</span>(1, 2, 3, 4);
+    <span class="org-keyword">function</span> <span class="org-function-name">Array</span>() { ... }
+  })();
+
+  <span class="org-comment-delimiter">// </span><span class="org-comment">etc.</span>
+</pre>
+
+
+</div>
+
+</div>
+
+<div id="outline-container-1-1-2" class="outline-4">
+<h4 id="sec-1-1-2"><span class="section-number-4">1.1.2</span> <code>obj.toString()</code> ==&gt; <code>obj+“”</code> </h4>
+<div class="outline-text-4" id="text-1-1-2">
+
+
+</div>
+</div>
+
+</div>
+
+<div id="outline-container-1-2" class="outline-3">
+<h3 id="sec-1-2"><span class="section-number-3">1.2</span> Install (NPM) </h3>
+<div class="outline-text-3" id="text-1-2">
+
+
+<p>
+UglifyJS is now available through NPM &mdash; <code>npm install uglify-js</code> should do
+the job.
+</p>
+</div>
+
+</div>
+
+<div id="outline-container-1-3" class="outline-3">
+<h3 id="sec-1-3"><span class="section-number-3">1.3</span> Install latest code from GitHub </h3>
+<div class="outline-text-3" id="text-1-3">
+
+
+
+
+
+<pre class="src src-sh"><span class="org-comment-delimiter">## </span><span class="org-comment">clone the repository</span>
+mkdir -p /where/you/wanna/put/it
+<span class="org-builtin">cd</span> /where/you/wanna/put/it
+git clone git://github.com/mishoo/UglifyJS.git
+
+<span class="org-comment-delimiter">## </span><span class="org-comment">make the module available to Node</span>
+mkdir -p ~/.node_libraries/
+<span class="org-builtin">cd</span> ~/.node_libraries/
+ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
+
+<span class="org-comment-delimiter">## </span><span class="org-comment">and if you want the CLI script too:</span>
+mkdir -p ~/bin
+<span class="org-builtin">cd</span> ~/bin
+ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
+  <span class="org-comment-delimiter"># </span><span class="org-comment">(then add ~/bin to your $PATH if it's not there already)</span>
+</pre>
+
+
+</div>
+
+</div>
+
+<div id="outline-container-1-4" class="outline-3">
+<h3 id="sec-1-4"><span class="section-number-3">1.4</span> Usage </h3>
+<div class="outline-text-3" id="text-1-4">
+
+
+<p>
+There is a command-line tool that exposes the functionality of this library
+for your shell-scripting needs:
+</p>
+
+
+
+<pre class="src src-sh">uglifyjs [ options... ] [ filename ]
+</pre>
+
+
+<p>
+<code>filename</code> should be the last argument and should name the file from which
+to read the JavaScript code.  If you don't specify it, it will read code
+from STDIN.
+</p>
+<p>
+Supported options:
+</p>
+<ul>
+<li><code>-b</code> or <code>--beautify</code> &mdash; output indented code; when passed, additional
+  options control the beautifier:
+
+<ul>
+<li><code>-i N</code> or <code>--indent N</code> &mdash; indentation level (number of spaces)
+
+</li>
+<li><code>-q</code> or <code>--quote-keys</code> &mdash; quote keys in literal objects (by default,
+    only keys that cannot be identifier names will be quotes).
+
+</li>
+</ul>
+
+</li>
+<li><code>--ascii</code> &mdash; pass this argument to encode non-ASCII characters as
+  <code>\uXXXX</code> sequences.  By default UglifyJS won't bother to do it and will
+  output Unicode characters instead.  (the output is always encoded in UTF8,
+  but if you pass this option you'll only get ASCII).
+
+</li>
+<li><code>-nm</code> or <code>--no-mangle</code> &mdash; don't mangle names.
+
+</li>
+<li><code>-nmf</code> or <code>--no-mangle-functions</code> &ndash; in case you want to mangle variable
+  names, but not touch function names.
+
+</li>
+<li><code>-ns</code> or <code>--no-squeeze</code> &mdash; don't call <code>ast_squeeze()</code> (which does various
+  optimizations that result in smaller, less readable code).
+
+</li>
+<li><code>-mt</code> or <code>--mangle-toplevel</code> &mdash; mangle names in the toplevel scope too
+  (by default we don't do this).
+
+</li>
+<li><code>--no-seqs</code> &mdash; when <code>ast_squeeze()</code> is called (thus, unless you pass
+  <code>--no-squeeze</code>) it will reduce consecutive statements in blocks into a
+  sequence.  For example, "a = 10; b = 20; foo();" will be written as
+  "a=10,b=20,foo();".  In various occasions, this allows us to discard the
+  block brackets (since the block becomes a single statement).  This is ON
+  by default because it seems safe and saves a few hundred bytes on some
+  libs that I tested it on, but pass <code>--no-seqs</code> to disable it.
+
+</li>
+<li><code>--no-dead-code</code> &mdash; by default, UglifyJS will remove code that is
+  obviously unreachable (code that follows a <code>return</code>, <code>throw</code>, <code>break</code> or
+  <code>continue</code> statement and is not a function/variable declaration).  Pass
+  this option to disable this optimization.
+
+</li>
+<li><code>-nc</code> or <code>--no-copyright</code> &mdash; by default, <code>uglifyjs</code> will keep the initial
+  comment tokens in the generated code (assumed to be copyright information
+  etc.).  If you pass this it will discard it.
+
+</li>
+<li><code>-o filename</code> or <code>--output filename</code> &mdash; put the result in <code>filename</code>.  If
+  this isn't given, the result goes to standard output (or see next one).
+
+</li>
+<li><code>--overwrite</code> &mdash; if the code is read from a file (not from STDIN) and you
+  pass <code>--overwrite</code> then the output will be written in the same file.
+
+</li>
+<li><code>--ast</code> &mdash; pass this if you want to get the Abstract Syntax Tree instead
+  of JavaScript as output.  Useful for debugging or learning more about the
+  internals.
+
+</li>
+<li><code>-v</code> or <code>--verbose</code> &mdash; output some notes on STDERR (for now just how long
+  each operation takes).
+
+</li>
+<li><code>-d SYMBOL[=VALUE]</code> or <code>--define SYMBOL[=VALUE]</code> &mdash; will replace
+  all instances of the specified symbol where used as an identifier
+  (except where symbol has properly declared by a var declaration or
+  use as function parameter or similar) with the specified value. This
+  argument may be specified multiple times to define multiple
+  symbols - if no value is specified the symbol will be replaced with
+  the value <code>true</code>, or you can specify a numeric value (such as
+  <code>1024</code>), a quoted string value (such as ="object"= or
+  ='https://github.com'<code>), or the name of another symbol or keyword   (such as =null</code> or <code>document</code>).
+  This allows you, for example, to assign meaningful names to key
+  constant values but discard the symbolic names in the uglified
+  version for brevity/efficiency, or when used wth care, allows
+  UglifyJS to operate as a form of <b>conditional compilation</b>
+  whereby defining appropriate values may, by dint of the constant
+  folding and dead code removal features above, remove entire
+  superfluous code blocks (e.g. completely remove instrumentation or
+  trace code for production use).
+  Where string values are being defined, the handling of quotes are
+  likely to be subject to the specifics of your command shell
+  environment, so you may need to experiment with quoting styles
+  depending on your platform, or you may find the option
+  <code>--define-from-module</code> more suitable for use.
+
+</li>
+<li><code>-define-from-module SOMEMODULE</code> &mdash; will load the named module (as
+  per the NodeJS <code>require()</code> function) and iterate all the exported
+  properties of the module defining them as symbol names to be defined
+  (as if by the <code>--define</code> option) per the name of each property
+  (i.e. without the module name prefix) and given the value of the
+  property. This is a much easier way to handle and document groups of
+  symbols to be defined rather than a large number of <code>--define</code>
+  options.
+
+</li>
+<li><code>--unsafe</code> &mdash; enable other additional optimizations that are known to be
+  unsafe in some contrived situations, but could still be generally useful.
+  For now only these:
+
+<ul>
+<li>foo.toString()  ==&gt;  foo+""
+</li>
+<li>new Array(x,&hellip;)  ==&gt; [x,&hellip;]
+</li>
+<li>new Array(x) ==&gt; Array(x)
+
+</li>
+</ul>
+
+</li>
+<li><code>--max-line-len</code> (default 32K characters) &mdash; add a newline after around
+  32K characters.  I've seen both FF and Chrome croak when all the code was
+  on a single line of around 670K.  Pass &ndash;max-line-len 0 to disable this
+  safety feature.
+
+</li>
+<li><code>--reserved-names</code> &mdash; some libraries rely on certain names to be used, as
+  pointed out in issue #92 and #81, so this option allow you to exclude such
+  names from the mangler.  For example, to keep names <code>require</code> and <code>$super</code>
+  intact you'd specify &ndash;reserved-names "require,$super".
+
+</li>
+<li><code>--inline-script</code> &ndash; when you want to include the output literally in an
+  HTML <code>&lt;script&gt;</code> tag you can use this option to prevent <code>&lt;/script</code> from
+  showing up in the output.
+
+</li>
+<li><code>--lift-vars</code> &ndash; when you pass this, UglifyJS will apply the following
+  transformations (see the notes in API, <code>ast_lift_variables</code>):
+
+<ul>
+<li>put all <code>var</code> declarations at the start of the scope
+</li>
+<li>make sure a variable is declared only once
+</li>
+<li>discard unused function arguments
+</li>
+<li>discard unused inner (named) functions
+</li>
+<li>finally, try to merge assignments into that one <code>var</code> declaration, if
+    possible.
+</li>
+</ul>
+
+</li>
+</ul>
+
+
+
+</div>
+
+<div id="outline-container-1-4-1" class="outline-4">
+<h4 id="sec-1-4-1"><span class="section-number-4">1.4.1</span> API </h4>
+<div class="outline-text-4" id="text-1-4-1">
+
+
+<p>
+To use the library from JavaScript, you'd do the following (example for
+NodeJS):
+</p>
+
+
+
+<pre class="src src-js"><span class="org-keyword">var</span> <span class="org-variable-name">jsp</span> = require(<span class="org-string">"uglify-js"</span>).parser;
+<span class="org-keyword">var</span> <span class="org-variable-name">pro</span> = require(<span class="org-string">"uglify-js"</span>).uglify;
+
+<span class="org-keyword">var</span> <span class="org-variable-name">orig_code</span> = <span class="org-string">"... JS code here"</span>;
+<span class="org-keyword">var</span> <span class="org-variable-name">ast</span> = jsp.parse(orig_code); <span class="org-comment-delimiter">// </span><span class="org-comment">parse code and get the initial AST</span>
+ast = pro.ast_mangle(ast); <span class="org-comment-delimiter">// </span><span class="org-comment">get a new AST with mangled names</span>
+ast = pro.ast_squeeze(ast); <span class="org-comment-delimiter">// </span><span class="org-comment">get an AST with compression optimizations</span>
+<span class="org-keyword">var</span> <span class="org-variable-name">final_code</span> = pro.gen_code(ast); <span class="org-comment-delimiter">// </span><span class="org-comment">compressed code here</span>
+</pre>
+
+
+<p>
+The above performs the full compression that is possible right now.  As you
+can see, there are a sequence of steps which you can apply.  For example if
+you want compressed output but for some reason you don't want to mangle
+variable names, you would simply skip the line that calls
+<code>pro.ast_mangle(ast)</code>.
+</p>
+<p>
+Some of these functions take optional arguments.  Here's a description:
+</p>
+<ul>
+<li><code>jsp.parse(code, strict_semicolons)</code> &ndash; parses JS code and returns an AST.
+  <code>strict_semicolons</code> is optional and defaults to <code>false</code>.  If you pass
+  <code>true</code> then the parser will throw an error when it expects a semicolon and
+  it doesn't find it.  For most JS code you don't want that, but it's useful
+  if you want to strictly sanitize your code.
+
+</li>
+<li><code>pro.ast_lift_variables(ast)</code> &ndash; merge and move <code>var</code> declarations to the
+  scop of the scope; discard unused function arguments or variables; discard
+  unused (named) inner functions.  It also tries to merge assignments
+  following the <code>var</code> declaration into it.
+
+<p>
+  If your code is very hand-optimized concerning <code>var</code> declarations, this
+  lifting variable declarations might actually increase size.  For me it
+  helps out.  On jQuery it adds 865 bytes (243 after gzip).  YMMV.  Also
+  note that (since it's not enabled by default) this operation isn't yet
+  heavily tested (please report if you find issues!).
+</p>
+<p>
+  Note that although it might increase the image size (on jQuery it gains
+  865 bytes, 243 after gzip) it's technically more correct: in certain
+  situations, dead code removal might drop variable declarations, which
+  would not happen if the variables are lifted in advance.
+</p>
+<p>
+  Here's an example of what it does:
+</p></li>
+</ul>
+
+
+
+
+
+<pre class="src src-js"><span class="org-keyword">function</span> <span class="org-function-name">f</span>(<span class="org-variable-name">a</span>, <span class="org-variable-name">b</span>, <span class="org-variable-name">c</span>, <span class="org-variable-name">d</span>, <span class="org-variable-name">e</span>) {
+    <span class="org-keyword">var</span> <span class="org-variable-name">q</span>;
+    <span class="org-keyword">var</span> <span class="org-variable-name">w</span>;
+    w = 10;
+    q = 20;
+    <span class="org-keyword">for</span> (<span class="org-keyword">var</span> <span class="org-variable-name">i</span> = 1; i &lt; 10; ++i) {
+        <span class="org-keyword">var</span> <span class="org-variable-name">boo</span> = foo(a);
+    }
+    <span class="org-keyword">for</span> (<span class="org-keyword">var</span> <span class="org-variable-name">i</span> = 0; i &lt; 1; ++i) {
+        <span class="org-keyword">var</span> <span class="org-variable-name">boo</span> = bar(c);
+    }
+    <span class="org-keyword">function</span> <span class="org-function-name">foo</span>(){ ... }
+    <span class="org-keyword">function</span> <span class="org-function-name">bar</span>(){ ... }
+    <span class="org-keyword">function</span> <span class="org-function-name">baz</span>(){ ... }
+}
+
+<span class="org-comment-delimiter">// </span><span class="org-comment">transforms into ==&gt;</span>
+
+<span class="org-keyword">function</span> <span class="org-function-name">f</span>(<span class="org-variable-name">a</span>, <span class="org-variable-name">b</span>, <span class="org-variable-name">c</span>) {
+    <span class="org-keyword">var</span> <span class="org-variable-name">i</span>, <span class="org-variable-name">boo</span>, <span class="org-variable-name">w</span> = 10, <span class="org-variable-name">q</span> = 20;
+    <span class="org-keyword">for</span> (i = 1; i &lt; 10; ++i) {
+        boo = foo(a);
+    }
+    <span class="org-keyword">for</span> (i = 0; i &lt; 1; ++i) {
+        boo = bar(c);
+    }
+    <span class="org-keyword">function</span> <span class="org-function-name">foo</span>() { ... }
+    <span class="org-keyword">function</span> <span class="org-function-name">bar</span>() { ... }
+}
+</pre>
+
+
+<ul>
+<li><code>pro.ast_mangle(ast, options)</code> &ndash; generates a new AST containing mangled
+  (compressed) variable and function names.  It supports the following
+  options:
+
+<ul>
+<li><code>toplevel</code> &ndash; mangle toplevel names (by default we don't touch them).
+</li>
+<li><code>except</code> &ndash; an array of names to exclude from compression.
+</li>
+<li><code>defines</code> &ndash; an object with properties named after symbols to
+    replace (see the <code>--define</code> option for the script) and the values
+    representing the AST replacement value.
+
+</li>
+</ul>
+
+</li>
+<li><code>pro.ast_squeeze(ast, options)</code> &ndash; employs further optimizations designed
+  to reduce the size of the code that <code>gen_code</code> would generate from the
+  AST.  Returns a new AST.  <code>options</code> can be a hash; the supported options
+  are:
+
+<ul>
+<li><code>make_seqs</code> (default true) which will cause consecutive statements in a
+    block to be merged using the "sequence" (comma) operator
+
+</li>
+<li><code>dead_code</code> (default true) which will remove unreachable code.
+
+</li>
+</ul>
+
+</li>
+<li><code>pro.gen_code(ast, options)</code> &ndash; generates JS code from the AST.  By
+  default it's minified, but using the <code>options</code> argument you can get nicely
+  formatted output.  <code>options</code> is, well, optional :-) and if you pass it it
+  must be an object and supports the following properties (below you can see
+  the default values):
+
+<ul>
+<li><code>beautify: false</code> &ndash; pass <code>true</code> if you want indented output
+</li>
+<li><code>indent_start: 0</code> (only applies when <code>beautify</code> is <code>true</code>) &ndash; initial
+    indentation in spaces
+</li>
+<li><code>indent_level: 4</code> (only applies when <code>beautify</code> is <code>true</code>) --
+    indentation level, in spaces (pass an even number)
+</li>
+<li><code>quote_keys: false</code> &ndash; if you pass <code>true</code> it will quote all keys in
+    literal objects
+</li>
+<li><code>space_colon: false</code> (only applies when <code>beautify</code> is <code>true</code>) &ndash; wether
+    to put a space before the colon in object literals
+</li>
+<li><code>ascii_only: false</code> &ndash; pass <code>true</code> if you want to encode non-ASCII
+    characters as <code>\uXXXX</code>.
+</li>
+<li><code>inline_script: false</code> &ndash; pass <code>true</code> to escape occurrences of
+    <code>&lt;/script</code> in strings
+</li>
+</ul>
+
+</li>
+</ul>
+
+
+</div>
+
+</div>
+
+<div id="outline-container-1-4-2" class="outline-4">
+<h4 id="sec-1-4-2"><span class="section-number-4">1.4.2</span> Beautifier shortcoming &ndash; no more comments </h4>
+<div class="outline-text-4" id="text-1-4-2">
+
+
+<p>
+The beautifier can be used as a general purpose indentation tool.  It's
+useful when you want to make a minified file readable.  One limitation,
+though, is that it discards all comments, so you don't really want to use it
+to reformat your code, unless you don't have, or don't care about, comments.
+</p>
+<p>
+In fact it's not the beautifier who discards comments &mdash; they are dumped at
+the parsing stage, when we build the initial AST.  Comments don't really
+make sense in the AST, and while we could add nodes for them, it would be
+inconvenient because we'd have to add special rules to ignore them at all
+the processing stages.
+</p>
+</div>
+
+</div>
+
+<div id="outline-container-1-4-3" class="outline-4">
+<h4 id="sec-1-4-3"><span class="section-number-4">1.4.3</span> Use as a code pre-processor </h4>
+<div class="outline-text-4" id="text-1-4-3">
+
+
+<p>
+The <code>--define</code> option can be used, particularly when combined with the
+constant folding logic, as a form of pre-processor to enable or remove
+particular constructions, such as might be used for instrumenting
+development code, or to produce variations aimed at a specific
+platform.
+</p>
+<p>
+The code below illustrates the way this can be done, and how the
+symbol replacement is performed.
+</p>
+
+
+
+<pre class="src src-js">CLAUSE1: <span class="org-keyword">if</span> (<span class="org-keyword">typeof</span> DEVMODE === <span class="org-string">'undefined'</span>) {
+    DEVMODE = <span class="org-constant">true</span>;
+}
+
+<span class="org-function-name">CLAUSE2</span>: <span class="org-keyword">function</span> init() {
+    <span class="org-keyword">if</span> (DEVMODE) {
+        console.log(<span class="org-string">"init() called"</span>);
+    }
+    ....
+    DEVMODE &amp;amp;&amp;amp; console.log(<span class="org-string">"init() complete"</span>);
+}
+
+<span class="org-function-name">CLAUSE3</span>: <span class="org-keyword">function</span> reportDeviceStatus(<span class="org-variable-name">device</span>) {
+    <span class="org-keyword">var</span> <span class="org-variable-name">DEVMODE</span> = device.mode, <span class="org-variable-name">DEVNAME</span> = device.name;
+    <span class="org-keyword">if</span> (DEVMODE === <span class="org-string">'open'</span>) {
+        ....
+    }
+}
+</pre>
+
+
+<p>
+When the above code is normally executed, the undeclared global
+variable <code>DEVMODE</code> will be assigned the value <b>true</b> (see <code>CLAUSE1</code>)
+and so the <code>init()</code> function (<code>CLAUSE2</code>) will write messages to the
+console log when executed, but in <code>CLAUSE3</code> a locally declared
+variable will mask access to the <code>DEVMODE</code> global symbol.
+</p>
+<p>
+If the above code is processed by UglifyJS with an argument of
+<code>--define DEVMODE=false</code> then UglifyJS will replace <code>DEVMODE</code> with the
+boolean constant value <b>false</b> within <code>CLAUSE1</code> and <code>CLAUSE2</code>, but it
+will leave <code>CLAUSE3</code> as it stands because there <code>DEVMODE</code> resolves to
+a validly declared variable.
+</p>
+<p>
+And more so, the constant-folding features of UglifyJS will recognise
+that the <code>if</code> condition of <code>CLAUSE1</code> is thus always false, and so will
+remove the test and body of <code>CLAUSE1</code> altogether (including the
+otherwise slightly problematical statement <code>false = true;</code> which it
+will have formed by replacing <code>DEVMODE</code> in the body).  Similarly,
+within <code>CLAUSE2</code> both calls to <code>console.log()</code> will be removed
+altogether.
+</p>
+<p>
+In this way you can mimic, to a limited degree, the functionality of
+the C/C++ pre-processor to enable or completely remove blocks
+depending on how certain symbols are defined - perhaps using UglifyJS
+to generate different versions of source aimed at different
+environments
+</p>
+<p>
+It is recommmended (but not made mandatory) that symbols designed for
+this purpose are given names consisting of <code>UPPER_CASE_LETTERS</code> to
+distinguish them from other (normal) symbols and avoid the sort of
+clash that <code>CLAUSE3</code> above illustrates.
+</p>
+</div>
+</div>
+
+</div>
+
+<div id="outline-container-1-5" class="outline-3">
+<h3 id="sec-1-5"><span class="section-number-3">1.5</span> Compression &ndash; how good is it? </h3>
+<div class="outline-text-3" id="text-1-5">
+
+
+<p>
+Here are updated statistics.  (I also updated my Google Closure and YUI
+installations).
+</p>
+<p>
+We're still a lot better than YUI in terms of compression, though slightly
+slower.  We're still a lot faster than Closure, and compression after gzip
+is comparable.
+</p>
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+<caption></caption>
+<colgroup><col class="left" /><col class="left" /><col class="right" /><col class="left" /><col class="right" /><col class="left" /><col class="right" />
+</colgroup>
+<thead>
+<tr><th scope="col" class="left">File</th><th scope="col" class="left">UglifyJS</th><th scope="col" class="right">UglifyJS+gzip</th><th scope="col" class="left">Closure</th><th scope="col" class="right">Closure+gzip</th><th scope="col" class="left">YUI</th><th scope="col" class="right">YUI+gzip</th></tr>
+</thead>
+<tbody>
+<tr><td class="left">jquery-1.6.2.js</td><td class="left">91001 (0:01.59)</td><td class="right">31896</td><td class="left">90678 (0:07.40)</td><td class="right">31979</td><td class="left">101527 (0:01.82)</td><td class="right">34646</td></tr>
+<tr><td class="left">paper.js</td><td class="left">142023 (0:01.65)</td><td class="right">43334</td><td class="left">134301 (0:07.42)</td><td class="right">42495</td><td class="left">173383 (0:01.58)</td><td class="right">48785</td></tr>
+<tr><td class="left">prototype.js</td><td class="left">88544 (0:01.09)</td><td class="right">26680</td><td class="left">86955 (0:06.97)</td><td class="right">26326</td><td class="left">92130 (0:00.79)</td><td class="right">28624</td></tr>
+<tr><td class="left">thelib-full.js (DynarchLIB)</td><td class="left">251939 (0:02.55)</td><td class="right">72535</td><td class="left">249911 (0:09.05)</td><td class="right">72696</td><td class="left">258869 (0:01.94)</td><td class="right">76584</td></tr>
+</tbody>
+</table>
+
+
+</div>
+
+</div>
+
+<div id="outline-container-1-6" class="outline-3">
+<h3 id="sec-1-6"><span class="section-number-3">1.6</span> Bugs? </h3>
+<div class="outline-text-3" id="text-1-6">
+
+
+<p>
+Unfortunately, for the time being there is no automated test suite.  But I
+ran the compressor manually on non-trivial code, and then I tested that the
+generated code works as expected.  A few hundred times.
+</p>
+<p>
+DynarchLIB was started in times when there was no good JS minifier.
+Therefore I was quite religious about trying to write short code manually,
+and as such DL contains a lot of syntactic hacks<sup><a class="footref" name="fnr.1" href="#fn.1">1</a></sup> such as “foo == bar ?  a
+= 10 : b = 20”, though the more readable version would clearly be to use
+“if/else”.
+</p>
+<p>
+Since the parser/compressor runs fine on DL and jQuery, I'm quite confident
+that it's solid enough for production use.  If you can identify any bugs,
+I'd love to hear about them (<a href="http://groups.google.com/group/uglifyjs">use the Google Group</a> or email me directly).
+</p>
+</div>
+
+</div>
+
+<div id="outline-container-1-7" class="outline-3">
+<h3 id="sec-1-7"><span class="section-number-3">1.7</span> Links </h3>
+<div class="outline-text-3" id="text-1-7">
+
+
+<ul>
+<li>Twitter: <a href="http://twitter.com/UglifyJS">@UglifyJS</a>
+</li>
+<li>Project at GitHub: <a href="http://github.com/mishoo/UglifyJS">http://github.com/mishoo/UglifyJS</a>
+</li>
+<li>Google Group: <a href="http://groups.google.com/group/uglifyjs">http://groups.google.com/group/uglifyjs</a>
+</li>
+<li>Common Lisp JS parser: <a href="http://marijn.haverbeke.nl/parse-js/">http://marijn.haverbeke.nl/parse-js/</a>
+</li>
+<li>JS-to-Lisp compiler: <a href="http://github.com/marijnh/js">http://github.com/marijnh/js</a>
+</li>
+<li>Common Lisp JS uglifier: <a href="http://github.com/mishoo/cl-uglify-js">http://github.com/mishoo/cl-uglify-js</a>
+</li>
+</ul>
+
+
+</div>
+
+</div>
+
+<div id="outline-container-1-8" class="outline-3">
+<h3 id="sec-1-8"><span class="section-number-3">1.8</span> License </h3>
+<div class="outline-text-3" id="text-1-8">
+
+
+<p>
+UglifyJS is released under the BSD license:
+</p>
+
+
+
+<pre class="example">Copyright 2010 (c) Mihai Bazon &lt;[email protected]&gt;
+Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above
+      copyright notice, this list of conditions and the following
+      disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials
+      provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+</pre>
+
+
+<div id="footnotes">
+<h2 class="footnotes">Footnotes: </h2>
+<div id="text-footnotes">
+<p class="footnote"><sup><a class="footnum" name="fn.1" href="#fnr.1">1</a></sup> I even reported a few bugs and suggested some fixes in the original
+    <a href="http://marijn.haverbeke.nl/parse-js/">parse-js</a> library, and Marijn pushed fixes literally in minutes.
+</p></div>
+</div>
+</div>
+
+</div>
+</div>
+</div>
+
+<div id="postamble">
+<p class="date">Date: 2011-12-09 14:59:08 EET</p>
+<p class="author">Author: Mihai Bazon</p>
+<p class="creator">Org version 7.7 with Emacs version 23</p>
+<a href="http://validator.w3.org/check?uri=referer">Validate XHTML 1.0</a>
+
+</div>
+</body>
+</html>

+ 578 - 0
utils/node_modules/uglify-js/README.org

@@ -0,0 +1,578 @@
+#+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier
+#+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier
+#+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript
+#+STYLE: <link rel="stylesheet" type="text/css" href="docstyle.css" />
+#+AUTHOR: Mihai Bazon
+#+EMAIL: [email protected]
+
+* UglifyJS --- a JavaScript parser/compressor/beautifier
+
+This package implements a general-purpose JavaScript
+parser/compressor/beautifier toolkit.  It is developed on [[http://nodejs.org/][NodeJS]], but it
+should work on any JavaScript platform supporting the CommonJS module system
+(and if your platform of choice doesn't support CommonJS, you can easily
+implement it, or discard the =exports.*= lines from UglifyJS sources).
+
+The tokenizer/parser generates an abstract syntax tree from JS code.  You
+can then traverse the AST to learn more about the code, or do various
+manipulations on it.  This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a
+port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn
+Haverbeke]].
+
+( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of
+UglifyJS. )
+
+The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and
+manipulates the AST generated by the parser to provide the following:
+
+- ability to re-generate JavaScript code from the AST.  Optionally
+  indented---you can use this if you want to “beautify” a program that has
+  been compressed, so that you can inspect the source.  But you can also run
+  our code generator to print out an AST without any whitespace, so you
+  achieve compression as well.
+
+- shorten variable names (usually to single characters).  Our mangler will
+  analyze the code and generate proper variable names, depending on scope
+  and usage, and is smart enough to deal with globals defined elsewhere, or
+  with =eval()= calls or =with{}= statements.  In short, if =eval()= or
+  =with{}= are used in some scope, then all variables in that scope and any
+  variables in the parent scopes will remain unmangled, and any references
+  to such variables remain unmangled as well.
+
+- various small optimizations that may lead to faster code but certainly
+  lead to smaller code.  Where possible, we do the following:
+
+  - foo["bar"]  ==>  foo.bar
+
+  - remove block brackets ={}=
+
+  - join consecutive var declarations:
+    var a = 10; var b = 20; ==> var a=10,b=20;
+
+  - resolve simple constant expressions: 1 +2 * 3 ==> 7.  We only do the
+    replacement if the result occupies less bytes; for example 1/3 would
+    translate to 0.333333333333, so in this case we don't replace it.
+
+  - consecutive statements in blocks are merged into a sequence; in many
+    cases, this leaves blocks with a single statement, so then we can remove
+    the block brackets.
+
+  - various optimizations for IF statements:
+
+    - if (foo) bar(); else baz(); ==> foo?bar():baz();
+    - if (!foo) bar(); else baz(); ==> foo?baz():bar();
+    - if (foo) bar(); ==> foo&&bar();
+    - if (!foo) bar(); ==> foo||bar();
+    - if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
+    - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
+
+  - remove some unreachable code and warn about it (code that follows a
+    =return=, =throw=, =break= or =continue= statement, except
+    function/variable declarations).
+
+  - act a limited version of a pre-processor (c.f. the pre-processor of
+    C/C++) to allow you to safely replace selected global symbols with
+    specified values.  When combined with the optimisations above this can
+    make UglifyJS operate slightly more like a compilation process, in
+    that when certain symbols are replaced by constant values, entire code
+    blocks may be optimised away as unreachable.
+
+** <<Unsafe transformations>>
+
+The following transformations can in theory break code, although they're
+probably safe in most practical cases.  To enable them you need to pass the
+=--unsafe= flag.
+
+*** Calls involving the global Array constructor
+
+The following transformations occur:
+
+#+BEGIN_SRC js
+new Array(1, 2, 3, 4)  => [1,2,3,4]
+Array(a, b, c)         => [a,b,c]
+new Array(5)           => Array(5)
+new Array(a)           => Array(a)
+#+END_SRC
+
+These are all safe if the Array name isn't redefined.  JavaScript does allow
+one to globally redefine Array (and pretty much everything, in fact) but I
+personally don't see why would anyone do that.
+
+UglifyJS does handle the case where Array is redefined locally, or even
+globally but with a =function= or =var= declaration.  Therefore, in the
+following cases UglifyJS *doesn't touch* calls or instantiations of Array:
+
+#+BEGIN_SRC js
+// case 1.  globally declared variable
+  var Array;
+  new Array(1, 2, 3);
+  Array(a, b);
+
+  // or (can be declared later)
+  new Array(1, 2, 3);
+  var Array;
+
+  // or (can be a function)
+  new Array(1, 2, 3);
+  function Array() { ... }
+
+// case 2.  declared in a function
+  (function(){
+    a = new Array(1, 2, 3);
+    b = Array(5, 6);
+    var Array;
+  })();
+
+  // or
+  (function(Array){
+    return Array(5, 6, 7);
+  })();
+
+  // or
+  (function(){
+    return new Array(1, 2, 3, 4);
+    function Array() { ... }
+  })();
+
+  // etc.
+#+END_SRC
+
+*** =obj.toString()= ==> =obj+“”=
+
+** Install (NPM)
+
+UglifyJS is now available through NPM --- =npm install uglify-js= should do
+the job.
+
+** Install latest code from GitHub
+
+#+BEGIN_SRC sh
+## clone the repository
+mkdir -p /where/you/wanna/put/it
+cd /where/you/wanna/put/it
+git clone git://github.com/mishoo/UglifyJS.git
+
+## make the module available to Node
+mkdir -p ~/.node_libraries/
+cd ~/.node_libraries/
+ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
+
+## and if you want the CLI script too:
+mkdir -p ~/bin
+cd ~/bin
+ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
+  # (then add ~/bin to your $PATH if it's not there already)
+#+END_SRC
+
+** Usage
+
+There is a command-line tool that exposes the functionality of this library
+for your shell-scripting needs:
+
+#+BEGIN_SRC sh
+uglifyjs [ options... ] [ filename ]
+#+END_SRC
+
+=filename= should be the last argument and should name the file from which
+to read the JavaScript code.  If you don't specify it, it will read code
+from STDIN.
+
+Supported options:
+
+- =-b= or =--beautify= --- output indented code; when passed, additional
+  options control the beautifier:
+
+  - =-i N= or =--indent N= --- indentation level (number of spaces)
+
+  - =-q= or =--quote-keys= --- quote keys in literal objects (by default,
+    only keys that cannot be identifier names will be quotes).
+
+- =-c= or =----consolidate-primitive-values= --- consolidates null, Boolean,
+  and String values. Known as aliasing in the Closure Compiler. Worsens the
+  data compression ratio of gzip.
+
+- =--ascii= --- pass this argument to encode non-ASCII characters as
+  =\uXXXX= sequences.  By default UglifyJS won't bother to do it and will
+  output Unicode characters instead.  (the output is always encoded in UTF8,
+  but if you pass this option you'll only get ASCII).
+
+- =-nm= or =--no-mangle= --- don't mangle names.
+
+- =-nmf= or =--no-mangle-functions= -- in case you want to mangle variable
+  names, but not touch function names.
+
+- =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various
+  optimizations that result in smaller, less readable code).
+
+- =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too
+  (by default we don't do this).
+
+- =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass
+  =--no-squeeze=) it will reduce consecutive statements in blocks into a
+  sequence.  For example, "a = 10; b = 20; foo();" will be written as
+  "a=10,b=20,foo();".  In various occasions, this allows us to discard the
+  block brackets (since the block becomes a single statement).  This is ON
+  by default because it seems safe and saves a few hundred bytes on some
+  libs that I tested it on, but pass =--no-seqs= to disable it.
+
+- =--no-dead-code= --- by default, UglifyJS will remove code that is
+  obviously unreachable (code that follows a =return=, =throw=, =break= or
+  =continue= statement and is not a function/variable declaration).  Pass
+  this option to disable this optimization.
+
+- =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial
+  comment tokens in the generated code (assumed to be copyright information
+  etc.).  If you pass this it will discard it.
+
+- =-o filename= or =--output filename= --- put the result in =filename=.  If
+  this isn't given, the result goes to standard output (or see next one).
+
+- =--overwrite= --- if the code is read from a file (not from STDIN) and you
+  pass =--overwrite= then the output will be written in the same file.
+
+- =--ast= --- pass this if you want to get the Abstract Syntax Tree instead
+  of JavaScript as output.  Useful for debugging or learning more about the
+  internals.
+
+- =-v= or =--verbose= --- output some notes on STDERR (for now just how long
+  each operation takes).
+
+- =-d SYMBOL[=VALUE]= or =--define SYMBOL[=VALUE]= --- will replace
+  all instances of the specified symbol where used as an identifier
+  (except where symbol has properly declared by a var declaration or
+  use as function parameter or similar) with the specified value. This
+  argument may be specified multiple times to define multiple
+  symbols - if no value is specified the symbol will be replaced with
+  the value =true=, or you can specify a numeric value (such as
+  =1024=), a quoted string value (such as ="object"= or
+  ='https://github.com'=), or the name of another symbol or keyword
+  (such as =null= or =document=).
+  This allows you, for example, to assign meaningful names to key
+  constant values but discard the symbolic names in the uglified
+  version for brevity/efficiency, or when used wth care, allows
+  UglifyJS to operate as a form of *conditional compilation*
+  whereby defining appropriate values may, by dint of the constant
+  folding and dead code removal features above, remove entire
+  superfluous code blocks (e.g. completely remove instrumentation or
+  trace code for production use).
+  Where string values are being defined, the handling of quotes are
+  likely to be subject to the specifics of your command shell
+  environment, so you may need to experiment with quoting styles
+  depending on your platform, or you may find the option
+  =--define-from-module= more suitable for use.
+
+- =-define-from-module SOMEMODULE= --- will load the named module (as
+  per the NodeJS =require()= function) and iterate all the exported
+  properties of the module defining them as symbol names to be defined
+  (as if by the =--define= option) per the name of each property
+  (i.e. without the module name prefix) and given the value of the
+  property. This is a much easier way to handle and document groups of
+  symbols to be defined rather than a large number of =--define=
+  options.
+
+- =--unsafe= --- enable other additional optimizations that are known to be
+  unsafe in some contrived situations, but could still be generally useful.
+  For now only these:
+
+  - foo.toString()  ==>  foo+""
+  - new Array(x,...)  ==> [x,...]
+  - new Array(x) ==> Array(x)
+
+- =--max-line-len= (default 32K characters) --- add a newline after around
+  32K characters.  I've seen both FF and Chrome croak when all the code was
+  on a single line of around 670K.  Pass --max-line-len 0 to disable this
+  safety feature.
+
+- =--reserved-names= --- some libraries rely on certain names to be used, as
+  pointed out in issue #92 and #81, so this option allow you to exclude such
+  names from the mangler.  For example, to keep names =require= and =$super=
+  intact you'd specify --reserved-names "require,$super".
+
+- =--inline-script= -- when you want to include the output literally in an
+  HTML =<script>= tag you can use this option to prevent =</script= from
+  showing up in the output.
+
+- =--lift-vars= -- when you pass this, UglifyJS will apply the following
+  transformations (see the notes in API, =ast_lift_variables=):
+
+  - put all =var= declarations at the start of the scope
+  - make sure a variable is declared only once
+  - discard unused function arguments
+  - discard unused inner (named) functions
+  - finally, try to merge assignments into that one =var= declaration, if
+    possible.
+
+*** API
+
+To use the library from JavaScript, you'd do the following (example for
+NodeJS):
+
+#+BEGIN_SRC js
+var jsp = require("uglify-js").parser;
+var pro = require("uglify-js").uglify;
+
+var orig_code = "... JS code here";
+var ast = jsp.parse(orig_code); // parse code and get the initial AST
+ast = pro.ast_mangle(ast); // get a new AST with mangled names
+ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
+var final_code = pro.gen_code(ast); // compressed code here
+#+END_SRC
+
+The above performs the full compression that is possible right now.  As you
+can see, there are a sequence of steps which you can apply.  For example if
+you want compressed output but for some reason you don't want to mangle
+variable names, you would simply skip the line that calls
+=pro.ast_mangle(ast)=.
+
+Some of these functions take optional arguments.  Here's a description:
+
+- =jsp.parse(code, strict_semicolons)= -- parses JS code and returns an AST.
+  =strict_semicolons= is optional and defaults to =false=.  If you pass
+  =true= then the parser will throw an error when it expects a semicolon and
+  it doesn't find it.  For most JS code you don't want that, but it's useful
+  if you want to strictly sanitize your code.
+
+- =pro.ast_lift_variables(ast)= -- merge and move =var= declarations to the
+  scop of the scope; discard unused function arguments or variables; discard
+  unused (named) inner functions.  It also tries to merge assignments
+  following the =var= declaration into it.
+
+  If your code is very hand-optimized concerning =var= declarations, this
+  lifting variable declarations might actually increase size.  For me it
+  helps out.  On jQuery it adds 865 bytes (243 after gzip).  YMMV.  Also
+  note that (since it's not enabled by default) this operation isn't yet
+  heavily tested (please report if you find issues!).
+
+  Note that although it might increase the image size (on jQuery it gains
+  865 bytes, 243 after gzip) it's technically more correct: in certain
+  situations, dead code removal might drop variable declarations, which
+  would not happen if the variables are lifted in advance.
+
+  Here's an example of what it does:
+
+#+BEGIN_SRC js
+function f(a, b, c, d, e) {
+    var q;
+    var w;
+    w = 10;
+    q = 20;
+    for (var i = 1; i < 10; ++i) {
+        var boo = foo(a);
+    }
+    for (var i = 0; i < 1; ++i) {
+        var boo = bar(c);
+    }
+    function foo(){ ... }
+    function bar(){ ... }
+    function baz(){ ... }
+}
+
+// transforms into ==>
+
+function f(a, b, c) {
+    var i, boo, w = 10, q = 20;
+    for (i = 1; i < 10; ++i) {
+        boo = foo(a);
+    }
+    for (i = 0; i < 1; ++i) {
+        boo = bar(c);
+    }
+    function foo() { ... }
+    function bar() { ... }
+}
+#+END_SRC
+
+- =pro.ast_mangle(ast, options)= -- generates a new AST containing mangled
+  (compressed) variable and function names.  It supports the following
+  options:
+
+  - =toplevel= -- mangle toplevel names (by default we don't touch them).
+  - =except= -- an array of names to exclude from compression.
+  - =defines= -- an object with properties named after symbols to
+    replace (see the =--define= option for the script) and the values
+    representing the AST replacement value.
+
+- =pro.ast_squeeze(ast, options)= -- employs further optimizations designed
+  to reduce the size of the code that =gen_code= would generate from the
+  AST.  Returns a new AST.  =options= can be a hash; the supported options
+  are:
+
+  - =make_seqs= (default true) which will cause consecutive statements in a
+    block to be merged using the "sequence" (comma) operator
+
+  - =dead_code= (default true) which will remove unreachable code.
+
+- =pro.gen_code(ast, options)= -- generates JS code from the AST.  By
+  default it's minified, but using the =options= argument you can get nicely
+  formatted output.  =options= is, well, optional :-) and if you pass it it
+  must be an object and supports the following properties (below you can see
+  the default values):
+
+  - =beautify: false= -- pass =true= if you want indented output
+  - =indent_start: 0= (only applies when =beautify= is =true=) -- initial
+    indentation in spaces
+  - =indent_level: 4= (only applies when =beautify= is =true=) --
+    indentation level, in spaces (pass an even number)
+  - =quote_keys: false= -- if you pass =true= it will quote all keys in
+    literal objects
+  - =space_colon: false= (only applies when =beautify= is =true=) -- wether
+    to put a space before the colon in object literals
+  - =ascii_only: false= -- pass =true= if you want to encode non-ASCII
+    characters as =\uXXXX=.
+  - =inline_script: false= -- pass =true= to escape occurrences of
+    =</script= in strings
+
+*** Beautifier shortcoming -- no more comments
+
+The beautifier can be used as a general purpose indentation tool.  It's
+useful when you want to make a minified file readable.  One limitation,
+though, is that it discards all comments, so you don't really want to use it
+to reformat your code, unless you don't have, or don't care about, comments.
+
+In fact it's not the beautifier who discards comments --- they are dumped at
+the parsing stage, when we build the initial AST.  Comments don't really
+make sense in the AST, and while we could add nodes for them, it would be
+inconvenient because we'd have to add special rules to ignore them at all
+the processing stages.
+
+*** Use as a code pre-processor
+
+The =--define= option can be used, particularly when combined with the
+constant folding logic, as a form of pre-processor to enable or remove
+particular constructions, such as might be used for instrumenting
+development code, or to produce variations aimed at a specific
+platform.
+
+The code below illustrates the way this can be done, and how the
+symbol replacement is performed.
+
+#+BEGIN_SRC js
+CLAUSE1: if (typeof DEVMODE === 'undefined') {
+    DEVMODE = true;
+}
+
+CLAUSE2: function init() {
+    if (DEVMODE) {
+        console.log("init() called");
+    }
+    ....
+    DEVMODE &amp;&amp; console.log("init() complete");
+}
+
+CLAUSE3: function reportDeviceStatus(device) {
+    var DEVMODE = device.mode, DEVNAME = device.name;
+    if (DEVMODE === 'open') {
+        ....
+    }
+}
+#+END_SRC
+
+When the above code is normally executed, the undeclared global
+variable =DEVMODE= will be assigned the value *true* (see =CLAUSE1=)
+and so the =init()= function (=CLAUSE2=) will write messages to the
+console log when executed, but in =CLAUSE3= a locally declared
+variable will mask access to the =DEVMODE= global symbol.
+
+If the above code is processed by UglifyJS with an argument of
+=--define DEVMODE=false= then UglifyJS will replace =DEVMODE= with the
+boolean constant value *false* within =CLAUSE1= and =CLAUSE2=, but it
+will leave =CLAUSE3= as it stands because there =DEVMODE= resolves to
+a validly declared variable.
+
+And more so, the constant-folding features of UglifyJS will recognise
+that the =if= condition of =CLAUSE1= is thus always false, and so will
+remove the test and body of =CLAUSE1= altogether (including the
+otherwise slightly problematical statement =false = true;= which it
+will have formed by replacing =DEVMODE= in the body).  Similarly,
+within =CLAUSE2= both calls to =console.log()= will be removed
+altogether.
+
+In this way you can mimic, to a limited degree, the functionality of
+the C/C++ pre-processor to enable or completely remove blocks
+depending on how certain symbols are defined - perhaps using UglifyJS
+to generate different versions of source aimed at different
+environments
+
+It is recommmended (but not made mandatory) that symbols designed for
+this purpose are given names consisting of =UPPER_CASE_LETTERS= to
+distinguish them from other (normal) symbols and avoid the sort of
+clash that =CLAUSE3= above illustrates.
+
+** Compression -- how good is it?
+
+Here are updated statistics.  (I also updated my Google Closure and YUI
+installations).
+
+We're still a lot better than YUI in terms of compression, though slightly
+slower.  We're still a lot faster than Closure, and compression after gzip
+is comparable.
+
+| File                        | UglifyJS         | UglifyJS+gzip | Closure          | Closure+gzip | YUI              | YUI+gzip |
+|-----------------------------+------------------+---------------+------------------+--------------+------------------+----------|
+| jquery-1.6.2.js             | 91001 (0:01.59)  |         31896 | 90678 (0:07.40)  |        31979 | 101527 (0:01.82) |    34646 |
+| paper.js                    | 142023 (0:01.65) |         43334 | 134301 (0:07.42) |        42495 | 173383 (0:01.58) |    48785 |
+| prototype.js                | 88544 (0:01.09)  |         26680 | 86955 (0:06.97)  |        26326 | 92130 (0:00.79)  |    28624 |
+| thelib-full.js (DynarchLIB) | 251939 (0:02.55) |         72535 | 249911 (0:09.05) |        72696 | 258869 (0:01.94) |    76584 |
+
+** Bugs?
+
+Unfortunately, for the time being there is no automated test suite.  But I
+ran the compressor manually on non-trivial code, and then I tested that the
+generated code works as expected.  A few hundred times.
+
+DynarchLIB was started in times when there was no good JS minifier.
+Therefore I was quite religious about trying to write short code manually,
+and as such DL contains a lot of syntactic hacks[1] such as “foo == bar ?  a
+= 10 : b = 20”, though the more readable version would clearly be to use
+“if/else”.
+
+Since the parser/compressor runs fine on DL and jQuery, I'm quite confident
+that it's solid enough for production use.  If you can identify any bugs,
+I'd love to hear about them ([[http://groups.google.com/group/uglifyjs][use the Google Group]] or email me directly).
+
+[1] I even reported a few bugs and suggested some fixes in the original
+    [[http://marijn.haverbeke.nl/parse-js/][parse-js]] library, and Marijn pushed fixes literally in minutes.
+
+** Links
+
+- Twitter: [[http://twitter.com/UglifyJS][@UglifyJS]]
+- Project at GitHub: [[http://github.com/mishoo/UglifyJS][http://github.com/mishoo/UglifyJS]]
+- Google Group: [[http://groups.google.com/group/uglifyjs][http://groups.google.com/group/uglifyjs]]
+- Common Lisp JS parser: [[http://marijn.haverbeke.nl/parse-js/][http://marijn.haverbeke.nl/parse-js/]]
+- JS-to-Lisp compiler: [[http://github.com/marijnh/js][http://github.com/marijnh/js]]
+- Common Lisp JS uglifier: [[http://github.com/mishoo/cl-uglify-js][http://github.com/mishoo/cl-uglify-js]]
+
+** License
+
+UglifyJS is released under the BSD license:
+
+#+BEGIN_EXAMPLE
+Copyright 2010 (c) Mihai Bazon <[email protected]>
+Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above
+      copyright notice, this list of conditions and the following
+      disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials
+      provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+#+END_EXAMPLE

+ 332 - 0
utils/node_modules/uglify-js/bin/uglifyjs

@@ -0,0 +1,332 @@
+#!/usr/bin/env node
+// -*- js -*-
+
+global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
+var fs = require("fs");
+var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js
+    consolidator = uglify.consolidator,
+    jsp = uglify.parser,
+    pro = uglify.uglify;
+
+var options = {
+        ast: false,
+        consolidate: false,
+        mangle: true,
+        mangle_toplevel: false,
+        no_mangle_functions: false,
+        squeeze: true,
+        make_seqs: true,
+        dead_code: true,
+        verbose: false,
+        show_copyright: true,
+        out_same_file: false,
+        max_line_length: 32 * 1024,
+        unsafe: false,
+        reserved_names: null,
+        defines: { },
+        lift_vars: false,
+        codegen_options: {
+                ascii_only: false,
+                beautify: false,
+                indent_level: 4,
+                indent_start: 0,
+                quote_keys: false,
+                space_colon: false,
+                inline_script: false
+        },
+        make: false,
+        output: true            // stdout
+};
+
+var args = jsp.slice(process.argv, 2);
+var filename;
+
+out: while (args.length > 0) {
+        var v = args.shift();
+        switch (v) {
+            case "-b":
+            case "--beautify":
+                options.codegen_options.beautify = true;
+                break;
+            case "-c":
+            case "--consolidate-primitive-values":
+                options.consolidate = true;
+                break;
+            case "-i":
+            case "--indent":
+                options.codegen_options.indent_level = args.shift();
+                break;
+            case "-q":
+            case "--quote-keys":
+                options.codegen_options.quote_keys = true;
+                break;
+            case "-mt":
+            case "--mangle-toplevel":
+                options.mangle_toplevel = true;
+                break;
+            case "-nmf":
+            case "--no-mangle-functions":
+                options.no_mangle_functions = true;
+                break;
+            case "--no-mangle":
+            case "-nm":
+                options.mangle = false;
+                break;
+            case "--no-squeeze":
+            case "-ns":
+                options.squeeze = false;
+                break;
+            case "--no-seqs":
+                options.make_seqs = false;
+                break;
+            case "--no-dead-code":
+                options.dead_code = false;
+                break;
+            case "--no-copyright":
+            case "-nc":
+                options.show_copyright = false;
+                break;
+            case "-o":
+            case "--output":
+                options.output = args.shift();
+                break;
+            case "--overwrite":
+                options.out_same_file = true;
+                break;
+            case "-v":
+            case "--verbose":
+                options.verbose = true;
+                break;
+            case "--ast":
+                options.ast = true;
+                break;
+            case "--unsafe":
+                options.unsafe = true;
+                break;
+            case "--max-line-len":
+                options.max_line_length = parseInt(args.shift(), 10);
+                break;
+            case "--reserved-names":
+                options.reserved_names = args.shift().split(",");
+                break;
+            case "--lift-vars":
+                options.lift_vars = true;
+                break;
+            case "-d":
+            case "--define":
+                 var defarg = args.shift();
+                 try {
+                     var defsym = function(sym) {
+                             // KEYWORDS_ATOM doesn't include NaN or Infinity - should we check
+                             // for them too ?? We don't check reserved words and the like as the
+                             // define values are only substituted AFTER parsing
+                             if (jsp.KEYWORDS_ATOM.hasOwnProperty(sym)) {
+                                 throw "Don't define values for inbuilt constant '"+sym+"'";
+                             }
+                             return sym;
+                         },
+                         defval = function(v) {
+                             if (v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/)) {
+                                 return [ "string", RegExp.$1 ];
+                             }
+                             else if (!isNaN(parseFloat(v))) {
+                                 return [ "num", parseFloat(v) ];
+                             }
+                             else if (v.match(/^[a-z\$_][a-z\$_0-9]*$/i)) {
+                                 return [ "name", v ];
+                             }
+                             else if (!v.match(/"/)) {
+                                 return [ "string", v ];
+                             }
+                             else if (!v.match(/'/)) {
+                                 return [ "string", v ];
+                             }
+                             throw "Can't understand the specified value: "+v;
+                         };
+                     if (defarg.match(/^([a-z_\$][a-z_\$0-9]*)(=(.*))?$/i)) {
+                         var sym = defsym(RegExp.$1),
+                             val = RegExp.$2 ? defval(RegExp.$2.substr(1)) : [ 'name', 'true' ];
+                         options.defines[sym] = val;
+                     }
+                     else {
+                         throw "The --define option expects SYMBOL[=value]";
+                     }
+                 } catch(ex) {
+                     sys.print("ERROR: In option --define "+defarg+"\n"+ex+"\n");
+                     process.exit(1);
+                 }
+                 break;
+            case "--define-from-module":
+                var defmodarg = args.shift(),
+                    defmodule = require(defmodarg),
+                    sym,
+                    val;
+                for (sym in defmodule) {
+                    if (defmodule.hasOwnProperty(sym)) {
+                        options.defines[sym] = function(val) {
+                            if (typeof val == "string")
+                                return [ "string", val ];
+                            if (typeof val == "number")
+                                return [ "num", val ];
+                            if (val === true)
+                                return [ 'name', 'true' ];
+                            if (val === false)
+                                return [ 'name', 'false' ];
+                            if (val === null)
+                                return [ 'name', 'null' ];
+                            if (val === undefined)
+                                return [ 'name', 'undefined' ];
+                            sys.print("ERROR: In option --define-from-module "+defmodarg+"\n");
+                            sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n");
+                            process.exit(1);
+                            return null;
+                        }(defmodule[sym]);
+                    }
+                }
+                break;
+            case "--ascii":
+                options.codegen_options.ascii_only = true;
+                break;
+            case "--make":
+                options.make = true;
+                break;
+            case "--inline-script":
+                options.codegen_options.inline_script = true;
+                break;
+            default:
+                filename = v;
+                break out;
+        }
+}
+
+if (options.verbose) {
+        pro.set_logger(function(msg){
+                sys.debug(msg);
+        });
+}
+
+jsp.set_logger(function(msg){
+        sys.debug(msg);
+});
+
+if (options.make) {
+        options.out_same_file = false; // doesn't make sense in this case
+        var makefile = JSON.parse(fs.readFileSync(filename || "Makefile.uglify.js").toString());
+        output(makefile.files.map(function(file){
+                var code = fs.readFileSync(file.name);
+                if (file.module) {
+                        code = "!function(exports, global){global = this;\n" + code + "\n;this." + file.module + " = exports;}({})";
+                }
+                else if (file.hide) {
+                        code = "(function(){" + code + "}());";
+                }
+                return squeeze_it(code);
+        }).join("\n"));
+}
+else if (filename) {
+        fs.readFile(filename, "utf8", function(err, text){
+                if (err) throw err;
+                output(squeeze_it(text));
+        });
+}
+else {
+        var stdin = process.openStdin();
+        stdin.setEncoding("utf8");
+        var text = "";
+        stdin.on("data", function(chunk){
+                text += chunk;
+        });
+        stdin.on("end", function() {
+                output(squeeze_it(text));
+        });
+}
+
+function output(text) {
+        var out;
+        if (options.out_same_file && filename)
+                options.output = filename;
+        if (options.output === true) {
+                out = process.stdout;
+        } else {
+                out = fs.createWriteStream(options.output, {
+                        flags: "w",
+                        encoding: "utf8",
+                        mode: 0644
+                });
+        }
+        out.write(text.replace(/;*$/, ";"));
+        if (options.output !== true) {
+                out.end();
+        }
+};
+
+// --------- main ends here.
+
+function show_copyright(comments) {
+        var ret = "";
+        for (var i = 0; i < comments.length; ++i) {
+                var c = comments[i];
+                if (c.type == "comment1") {
+                        ret += "//" + c.value + "\n";
+                } else {
+                        ret += "/*" + c.value + "*/";
+                }
+        }
+        return ret;
+};
+
+function squeeze_it(code) {
+        var result = "";
+        if (options.show_copyright) {
+                var tok = jsp.tokenizer(code), c;
+                c = tok();
+                result += show_copyright(c.comments_before);
+        }
+        try {
+                var ast = time_it("parse", function(){ return jsp.parse(code); });
+                if (options.consolidate) ast = time_it("consolidate", function(){
+                        return consolidator.ast_consolidate(ast);
+                });
+                if (options.lift_vars) {
+                        ast = time_it("lift", function(){ return pro.ast_lift_variables(ast); });
+                }
+                if (options.mangle) ast = time_it("mangle", function(){
+                        return pro.ast_mangle(ast, {
+                                toplevel     : options.mangle_toplevel,
+                                defines      : options.defines,
+                                except       : options.reserved_names,
+                                no_functions : options.no_mangle_functions
+                        });
+                });
+                if (options.squeeze) ast = time_it("squeeze", function(){
+                        ast = pro.ast_squeeze(ast, {
+                                make_seqs  : options.make_seqs,
+                                dead_code  : options.dead_code,
+                                keep_comps : !options.unsafe
+                        });
+                        if (options.unsafe)
+                                ast = pro.ast_squeeze_more(ast);
+                        return ast;
+                });
+                if (options.ast)
+                        return sys.inspect(ast, null, null);
+                result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
+                if (!options.codegen_options.beautify && options.max_line_length) {
+                        result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
+                }
+                return result;
+        } catch(ex) {
+                sys.debug(ex.stack);
+                sys.debug(sys.inspect(ex));
+                sys.debug(JSON.stringify(ex));
+                process.exit(1);
+        }
+};
+
+function time_it(name, cont) {
+        if (!options.verbose)
+                return cont();
+        var t1 = new Date().getTime();
+        try { return cont(); }
+        finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
+};

+ 75 - 0
utils/node_modules/uglify-js/docstyle.css

@@ -0,0 +1,75 @@
+html { font-family: "Lucida Grande","Trebuchet MS",sans-serif; font-size: 12pt; }
+body { max-width: 60em; }
+.title  { text-align: center; }
+.todo   { color: red; }
+.done   { color: green; }
+.tag    { background-color:lightblue; font-weight:normal }
+.target { }
+.timestamp { color: grey }
+.timestamp-kwd { color: CadetBlue }
+p.verse { margin-left: 3% }
+pre {
+  border: 1pt solid #AEBDCC;
+  background-color: #F3F5F7;
+  padding: 5pt;
+  font-family: monospace;
+  font-size: 90%;
+  overflow:auto;
+}
+pre.src {
+  background-color: #eee; color: #112; border: 1px solid #000;
+}
+table { border-collapse: collapse; }
+td, th { vertical-align: top; }
+dt { font-weight: bold; }
+div.figure { padding: 0.5em; }
+div.figure p { text-align: center; }
+.linenr { font-size:smaller }
+.code-highlighted {background-color:#ffff00;}
+.org-info-js_info-navigation { border-style:none; }
+#org-info-js_console-label { font-size:10px; font-weight:bold;
+  white-space:nowrap; }
+.org-info-js_search-highlight {background-color:#ffff00; color:#000000;
+  font-weight:bold; }
+
+sup {
+  vertical-align: baseline;
+  position: relative;
+  top: -0.5em;
+  font-size: 80%;
+}
+
+sup a:link, sup a:visited {
+  text-decoration: none;
+  color: #c00;
+}
+
+sup a:before { content: "["; color: #999; }
+sup a:after { content: "]"; color: #999; }
+
+h1.title { border-bottom: 4px solid #000; padding-bottom: 5px; margin-bottom: 2em; }
+
+#postamble {
+  color: #777;
+  font-size: 90%;
+  padding-top: 1em; padding-bottom: 1em; border-top: 1px solid #999;
+  margin-top: 2em;
+  padding-left: 2em;
+  padding-right: 2em;
+  text-align: right;
+}
+
+#postamble p { margin: 0; }
+
+#footnotes { border-top: 1px solid #000; }
+
+h1 { font-size: 200% }
+h2 { font-size: 175% }
+h3 { font-size: 150% }
+h4 { font-size: 125% }
+
+h1, h2, h3, h4 { font-family: "Bookman",Georgia,"Times New Roman",serif; font-weight: normal; }
+
+@media print {
+  html { font-size: 11pt; }
+}

+ 2599 - 0
utils/node_modules/uglify-js/lib/consolidator.js

@@ -0,0 +1,2599 @@
+/**
+ * @preserve Copyright 2012 Robert Gust-Bardon <http://robert.gust-bardon.org/>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer.
+ *
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @fileoverview Enhances <a href="https://github.com/mishoo/UglifyJS/"
+ * >UglifyJS</a> with consolidation of null, Boolean, and String values.
+ * <p>Also known as aliasing, this feature has been deprecated in <a href=
+ * "http://closure-compiler.googlecode.com/">the Closure Compiler</a> since its
+ * initial release, where it is unavailable from the <abbr title=
+ * "command line interface">CLI</a>. The Closure Compiler allows one to log and
+ * influence this process. In contrast, this implementation does not introduce
+ * any variable declarations in global code and derives String values from
+ * identifier names used as property accessors.</p>
+ * <p>Consolidating literals may worsen the data compression ratio when an <a
+ * href="http://tools.ietf.org/html/rfc2616#section-3.5">encoding
+ * transformation</a> is applied. For instance, <a href=
+ * "http://code.jquery.com/jquery-1.7.1.js">jQuery 1.7.1</a> takes 248235 bytes.
+ * Building it with <a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">
+ * UglifyJS v1.2.5</a> results in 93647 bytes (37.73% of the original) which are
+ * then compressed to 33154 bytes (13.36% of the original) using <a href=
+ * "http://linux.die.net/man/1/gzip">gzip(1)</a>. Building it with the same
+ * version of UglifyJS 1.2.5 patched with the implementation of consolidation
+ * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison
+ * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes
+ * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned
+ * 33154 bytes).</p>
+ * <p>Written in <a href="http://es5.github.com/#x4.2.2">the strict variant</a>
+ * of <a href="http://es5.github.com/">ECMA-262 5.1 Edition</a>. Encoded in <a
+ * href="http://tools.ietf.org/html/rfc3629">UTF-8</a>. Follows <a href=
+ * "http://google-styleguide.googlecode.com/svn-history/r76/trunk/javascriptguide.xml"
+ * >Revision 2.28 of the Google JavaScript Style Guide</a> (except for the
+ * discouraged use of the {@code function} tag and the {@code namespace} tag).
+ * 100% typed for the <a href=
+ * "http://closure-compiler.googlecode.com/files/compiler-20120123.tar.gz"
+ * >Closure Compiler Version 1741</a>.</p>
+ * <p>Should you find this software useful, please consider <a href=
+ * "https://paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JZLW72X8FD4WG"
+ * >a donation</a>.</p>
+ * @author follow.me@RGustBardon (Robert Gust-Bardon)
+ * @supported Tested with:
+ *     <ul>
+ *     <li><a href="http://nodejs.org/dist/v0.6.10/">Node v0.6.10</a>,</li>
+ *     <li><a href="https://github.com/mishoo/UglifyJS/tarball/v1.2.5">UglifyJS
+ *       v1.2.5</a>.</li>
+ *     </ul>
+ */
+
+/*global console:false, exports:true, module:false, require:false */
+/*jshint sub:true */
+/**
+ * Consolidates null, Boolean, and String values found inside an <abbr title=
+ * "abstract syntax tree">AST</abbr>.
+ * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object
+ *     representing an <abbr title="abstract syntax tree">AST</abbr>.
+ * @return {!TSyntacticCodeUnit} An array-like object representing an <abbr
+ *     title="abstract syntax tree">AST</abbr> with its null, Boolean, and
+ *     String values consolidated.
+ */
+// TODO(user) Consolidation of mathematical values found in numeric literals.
+// TODO(user) Unconsolidation.
+// TODO(user) Consolidation of ECMA-262 6th Edition programs.
+// TODO(user) Rewrite in ECMA-262 6th Edition.
+exports['ast_consolidate'] = function(oAbstractSyntaxTree) {
+  'use strict';
+  /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
+        latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
+        onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
+        sub:false, trailing:true */
+
+  var _,
+      /**
+       * A record consisting of data about one or more source elements.
+       * @constructor
+       * @nosideeffects
+       */
+      TSourceElementsData = function() {
+        /**
+         * The category of the elements.
+         * @type {number}
+         * @see ESourceElementCategories
+         */
+        this.nCategory = ESourceElementCategories.N_OTHER;
+        /**
+         * The number of occurrences (within the elements) of each primitive
+         * value that could be consolidated.
+         * @type {!Array.<!Object.<string, number>>}
+         */
+        this.aCount = [];
+        this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {};
+        this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {};
+        this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] =
+            {};
+        /**
+         * Identifier names found within the elements.
+         * @type {!Array.<string>}
+         */
+        this.aIdentifiers = [];
+        /**
+         * Prefixed representation Strings of each primitive value that could be
+         * consolidated within the elements.
+         * @type {!Array.<string>}
+         */
+        this.aPrimitiveValues = [];
+      },
+      /**
+       * A record consisting of data about a primitive value that could be
+       * consolidated.
+       * @constructor
+       * @nosideeffects
+       */
+      TPrimitiveValue = function() {
+        /**
+         * The difference in the number of terminal symbols between the original
+         * source text and the one with the primitive value consolidated. If the
+         * difference is positive, the primitive value is considered worthwhile.
+         * @type {number}
+         */
+        this.nSaving = 0;
+        /**
+         * An identifier name of the variable that will be declared and assigned
+         * the primitive value if the primitive value is consolidated.
+         * @type {string}
+         */
+        this.sName = '';
+      },
+      /**
+       * A record consisting of data on what to consolidate within the range of
+       * source elements that is currently being considered.
+       * @constructor
+       * @nosideeffects
+       */
+      TSolution = function() {
+        /**
+         * An object whose keys are prefixed representation Strings of each
+         * primitive value that could be consolidated within the elements and
+         * whose values are corresponding data about those primitive values.
+         * @type {!Object.<string, {nSaving: number, sName: string}>}
+         * @see TPrimitiveValue
+         */
+        this.oPrimitiveValues = {};
+        /**
+         * The difference in the number of terminal symbols between the original
+         * source text and the one with all the worthwhile primitive values
+         * consolidated.
+         * @type {number}
+         * @see TPrimitiveValue#nSaving
+         */
+        this.nSavings = 0;
+      },
+      /**
+       * The processor of <abbr title="abstract syntax tree">AST</abbr>s found
+       * in UglifyJS.
+       * @namespace
+       * @type {!TProcessor}
+       */
+      oProcessor = (/** @type {!TProcessor} */ require('./process')),
+      /**
+       * A record consisting of a number of constants that represent the
+       * difference in the number of terminal symbols between a source text with
+       * a modified syntactic code unit and the original one.
+       * @namespace
+       * @type {!Object.<string, number>}
+       */
+      oWeights = {
+        /**
+         * The difference in the number of punctuators required by the bracket
+         * notation and the dot notation.
+         * <p><code>'[]'.length - '.'.length</code></p>
+         * @const
+         * @type {number}
+         */
+        N_PROPERTY_ACCESSOR: 1,
+        /**
+         * The number of punctuators required by a variable declaration with an
+         * initialiser.
+         * <p><code>':'.length + ';'.length</code></p>
+         * @const
+         * @type {number}
+         */
+        N_VARIABLE_DECLARATION: 2,
+        /**
+         * The number of terminal symbols required to introduce a variable
+         * statement (excluding its variable declaration list).
+         * <p><code>'var '.length</code></p>
+         * @const
+         * @type {number}
+         */
+        N_VARIABLE_STATEMENT_AFFIXATION: 4,
+        /**
+         * The number of terminal symbols needed to enclose source elements
+         * within a function call with no argument values to a function with an
+         * empty parameter list.
+         * <p><code>'(function(){}());'.length</code></p>
+         * @const
+         * @type {number}
+         */
+        N_CLOSURE: 17
+      },
+      /**
+       * Categories of primary expressions from which primitive values that
+       * could be consolidated are derivable.
+       * @namespace
+       * @enum {number}
+       */
+      EPrimaryExpressionCategories = {
+        /**
+         * Identifier names used as property accessors.
+         * @type {number}
+         */
+        N_IDENTIFIER_NAMES: 0,
+        /**
+         * String literals.
+         * @type {number}
+         */
+        N_STRING_LITERALS: 1,
+        /**
+         * Null and Boolean literals.
+         * @type {number}
+         */
+        N_NULL_AND_BOOLEAN_LITERALS: 2
+      },
+      /**
+       * Prefixes of primitive values that could be consolidated.
+       * The String values of the prefixes must have same number of characters.
+       * The prefixes must not be used in any properties defined in any version
+       * of <a href=
+       * "http://www.ecma-international.org/publications/standards/Ecma-262.htm"
+       * >ECMA-262</a>.
+       * @namespace
+       * @enum {string}
+       */
+      EValuePrefixes = {
+        /**
+         * Identifies String values.
+         * @type {string}
+         */
+        S_STRING: '#S',
+        /**
+         * Identifies null and Boolean values.
+         * @type {string}
+         */
+        S_SYMBOLIC: '#O'
+      },
+      /**
+       * Categories of source elements in terms of their appropriateness of
+       * having their primitive values consolidated.
+       * @namespace
+       * @enum {number}
+       */
+      ESourceElementCategories = {
+        /**
+         * Identifies a source element that includes the <a href=
+         * "http://es5.github.com/#x12.10">{@code with}</a> statement.
+         * @type {number}
+         */
+        N_WITH: 0,
+        /**
+         * Identifies a source element that includes the <a href=
+         * "http://es5.github.com/#x15.1.2.1">{@code eval}</a> identifier name.
+         * @type {number}
+         */
+        N_EVAL: 1,
+        /**
+         * Identifies a source element that must be excluded from the process
+         * unless its whole scope is examined.
+         * @type {number}
+         */
+        N_EXCLUDABLE: 2,
+        /**
+         * Identifies source elements not posing any problems.
+         * @type {number}
+         */
+        N_OTHER: 3
+      },
+      /**
+       * The list of literals (other than the String ones) whose primitive
+       * values can be consolidated.
+       * @const
+       * @type {!Array.<string>}
+       */
+      A_OTHER_SUBSTITUTABLE_LITERALS = [
+        'null',   // The null literal.
+        'false',  // The Boolean literal {@code false}.
+        'true'    // The Boolean literal {@code true}.
+      ];
+
+  (/**
+    * Consolidates all worthwhile primitive values in a syntactic code unit.
+    * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object
+    *     representing the branch of the abstract syntax tree representing the
+    *     syntactic code unit along with its scope.
+    * @see TPrimitiveValue#nSaving
+    */
+   function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) {
+     var _,
+         /**
+          * Indicates whether the syntactic code unit represents global code.
+          * @type {boolean}
+          */
+         bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0],
+         /**
+          * Indicates whether the whole scope is being examined.
+          * @type {boolean}
+          */
+         bIsWhollyExaminable = !bIsGlobal,
+         /**
+          * An array-like object representing source elements that constitute a
+          * syntactic code unit.
+          * @type {!TSyntacticCodeUnit}
+          */
+         oSourceElements,
+         /**
+          * A record consisting of data about the source element that is
+          * currently being examined.
+          * @type {!TSourceElementsData}
+          */
+         oSourceElementData,
+         /**
+          * The scope of the syntactic code unit.
+          * @type {!TScope}
+          */
+         oScope,
+         /**
+          * An instance of an object that allows the traversal of an <abbr
+          * title="abstract syntax tree">AST</abbr>.
+          * @type {!TWalker}
+          */
+         oWalker,
+         /**
+          * An object encompassing collections of functions used during the
+          * traversal of an <abbr title="abstract syntax tree">AST</abbr>.
+          * @namespace
+          * @type {!Object.<string, !Object.<string, function(...[*])>>}
+          */
+         oWalkers = {
+           /**
+            * A collection of functions used during the surveyance of source
+            * elements.
+            * @namespace
+            * @type {!Object.<string, function(...[*])>}
+            */
+           oSurveySourceElement: {
+             /**#nocode+*/  // JsDoc Toolkit 2.4.0 hides some of the keys.
+             /**
+              * Classifies the source element as excludable if it does not
+              * contain a {@code with} statement or the {@code eval} identifier
+              * name. Adds the identifier of the function and its formal
+              * parameters to the list of identifier names found.
+              * @param {string} sIdentifier The identifier of the function.
+              * @param {!Array.<string>} aFormalParameterList Formal parameters.
+              * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
+              */
+             'defun': function(
+                 sIdentifier,
+                 aFormalParameterList,
+                 oFunctionBody) {
+               fClassifyAsExcludable();
+               fAddIdentifier(sIdentifier);
+               aFormalParameterList.forEach(fAddIdentifier);
+             },
+             /**
+              * Increments the count of the number of occurrences of the String
+              * value that is equivalent to the sequence of terminal symbols
+              * that constitute the encountered identifier name.
+              * @param {!TSyntacticCodeUnit} oExpression The nonterminal
+              *     MemberExpression.
+              * @param {string} sIdentifierName The identifier name used as the
+              *     property accessor.
+              * @return {!Array} The encountered branch of an <abbr title=
+              *     "abstract syntax tree">AST</abbr> with its nonterminal
+              *     MemberExpression traversed.
+              */
+             'dot': function(oExpression, sIdentifierName) {
+               fCountPrimaryExpression(
+                   EPrimaryExpressionCategories.N_IDENTIFIER_NAMES,
+                   EValuePrefixes.S_STRING + sIdentifierName);
+               return ['dot', oWalker.walk(oExpression), sIdentifierName];
+             },
+             /**
+              * Adds the optional identifier of the function and its formal
+              * parameters to the list of identifier names found.
+              * @param {?string} sIdentifier The optional identifier of the
+              *     function.
+              * @param {!Array.<string>} aFormalParameterList Formal parameters.
+              * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
+              */
+             'function': function(
+                 sIdentifier,
+                 aFormalParameterList,
+                 oFunctionBody) {
+               if ('string' === typeof sIdentifier) {
+                 fAddIdentifier(sIdentifier);
+               }
+               aFormalParameterList.forEach(fAddIdentifier);
+             },
+             /**
+              * Either increments the count of the number of occurrences of the
+              * encountered null or Boolean value or classifies a source element
+              * as containing the {@code eval} identifier name.
+              * @param {string} sIdentifier The identifier encountered.
+              */
+             'name': function(sIdentifier) {
+               if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) {
+                 fCountPrimaryExpression(
+                     EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS,
+                     EValuePrefixes.S_SYMBOLIC + sIdentifier);
+               } else {
+                 if ('eval' === sIdentifier) {
+                   oSourceElementData.nCategory =
+                       ESourceElementCategories.N_EVAL;
+                 }
+                 fAddIdentifier(sIdentifier);
+               }
+             },
+             /**
+              * Classifies the source element as excludable if it does not
+              * contain a {@code with} statement or the {@code eval} identifier
+              * name.
+              * @param {TSyntacticCodeUnit} oExpression The expression whose
+              *     value is to be returned.
+              */
+             'return': function(oExpression) {
+               fClassifyAsExcludable();
+             },
+             /**
+              * Increments the count of the number of occurrences of the
+              * encountered String value.
+              * @param {string} sStringValue The String value of the string
+              *     literal encountered.
+              */
+             'string': function(sStringValue) {
+               if (sStringValue.length > 0) {
+                 fCountPrimaryExpression(
+                     EPrimaryExpressionCategories.N_STRING_LITERALS,
+                     EValuePrefixes.S_STRING + sStringValue);
+               }
+             },
+             /**
+              * Adds the identifier reserved for an exception to the list of
+              * identifier names found.
+              * @param {!TSyntacticCodeUnit} oTry A block of code in which an
+              *     exception can occur.
+              * @param {Array} aCatch The identifier reserved for an exception
+              *     and a block of code to handle the exception.
+              * @param {TSyntacticCodeUnit} oFinally An optional block of code
+              *     to be evaluated regardless of whether an exception occurs.
+              */
+             'try': function(oTry, aCatch, oFinally) {
+               if (Array.isArray(aCatch)) {
+                 fAddIdentifier(aCatch[0]);
+               }
+             },
+             /**
+              * Classifies the source element as excludable if it does not
+              * contain a {@code with} statement or the {@code eval} identifier
+              * name. Adds the identifier of each declared variable to the list
+              * of identifier names found.
+              * @param {!Array.<!Array>} aVariableDeclarationList Variable
+              *     declarations.
+              */
+             'var': function(aVariableDeclarationList) {
+               fClassifyAsExcludable();
+               aVariableDeclarationList.forEach(fAddVariable);
+             },
+             /**
+              * Classifies a source element as containing the {@code with}
+              * statement.
+              * @param {!TSyntacticCodeUnit} oExpression An expression whose
+              *     value is to be converted to a value of type Object and
+              *     become the binding object of a new object environment
+              *     record of a new lexical environment in which the statement
+              *     is to be executed.
+              * @param {!TSyntacticCodeUnit} oStatement The statement to be
+              *     executed in the augmented lexical environment.
+              * @return {!Array} An empty array to stop the traversal.
+              */
+             'with': function(oExpression, oStatement) {
+               oSourceElementData.nCategory = ESourceElementCategories.N_WITH;
+               return [];
+             }
+             /**#nocode-*/  // JsDoc Toolkit 2.4.0 hides some of the keys.
+           },
+           /**
+            * A collection of functions used while looking for nested functions.
+            * @namespace
+            * @type {!Object.<string, function(...[*])>}
+            */
+           oExamineFunctions: {
+             /**#nocode+*/  // JsDoc Toolkit 2.4.0 hides some of the keys.
+             /**
+              * Orders an examination of a nested function declaration.
+              * @this {!TSyntacticCodeUnit} An array-like object representing
+              *     the branch of an <abbr title="abstract syntax tree"
+              *     >AST</abbr> representing the syntactic code unit along with
+              *     its scope.
+              * @return {!Array} An empty array to stop the traversal.
+              */
+             'defun': function() {
+               fExamineSyntacticCodeUnit(this);
+               return [];
+             },
+             /**
+              * Orders an examination of a nested function expression.
+              * @this {!TSyntacticCodeUnit} An array-like object representing
+              *     the branch of an <abbr title="abstract syntax tree"
+              *     >AST</abbr> representing the syntactic code unit along with
+              *     its scope.
+              * @return {!Array} An empty array to stop the traversal.
+              */
+             'function': function() {
+               fExamineSyntacticCodeUnit(this);
+               return [];
+             }
+             /**#nocode-*/  // JsDoc Toolkit 2.4.0 hides some of the keys.
+           }
+         },
+         /**
+          * Records containing data about source elements.
+          * @type {Array.<TSourceElementsData>}
+          */
+         aSourceElementsData = [],
+         /**
+          * The index (in the source text order) of the source element
+          * immediately following a <a href="http://es5.github.com/#x14.1"
+          * >Directive Prologue</a>.
+          * @type {number}
+          */
+         nAfterDirectivePrologue = 0,
+         /**
+          * The index (in the source text order) of the source element that is
+          * currently being considered.
+          * @type {number}
+          */
+         nPosition,
+         /**
+          * The index (in the source text order) of the source element that is
+          * the last element of the range of source elements that is currently
+          * being considered.
+          * @type {(undefined|number)}
+          */
+         nTo,
+         /**
+          * Initiates the traversal of a source element.
+          * @param {!TWalker} oWalker An instance of an object that allows the
+          *     traversal of an abstract syntax tree.
+          * @param {!TSyntacticCodeUnit} oSourceElement A source element from
+          *     which the traversal should commence.
+          * @return {function(): !TSyntacticCodeUnit} A function that is able to
+          *     initiate the traversal from a given source element.
+          */
+         cContext = function(oWalker, oSourceElement) {
+           /**
+            * @return {!TSyntacticCodeUnit} A function that is able to
+            *     initiate the traversal from a given source element.
+            */
+           var fLambda = function() {
+             return oWalker.walk(oSourceElement);
+           };
+
+           return fLambda;
+         },
+         /**
+          * Classifies the source element as excludable if it does not
+          * contain a {@code with} statement or the {@code eval} identifier
+          * name.
+          */
+         fClassifyAsExcludable = function() {
+           if (oSourceElementData.nCategory ===
+               ESourceElementCategories.N_OTHER) {
+             oSourceElementData.nCategory =
+                 ESourceElementCategories.N_EXCLUDABLE;
+           }
+         },
+         /**
+          * Adds an identifier to the list of identifier names found.
+          * @param {string} sIdentifier The identifier to be added.
+          */
+         fAddIdentifier = function(sIdentifier) {
+           if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) {
+             oSourceElementData.aIdentifiers.push(sIdentifier);
+           }
+         },
+         /**
+          * Adds the identifier of a variable to the list of identifier names
+          * found.
+          * @param {!Array} aVariableDeclaration A variable declaration.
+          */
+         fAddVariable = function(aVariableDeclaration) {
+           fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]);
+         },
+         /**
+          * Increments the count of the number of occurrences of the prefixed
+          * String representation attributed to the primary expression.
+          * @param {number} nCategory The category of the primary expression.
+          * @param {string} sName The prefixed String representation attributed
+          *     to the primary expression.
+          */
+         fCountPrimaryExpression = function(nCategory, sName) {
+           if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) {
+             oSourceElementData.aCount[nCategory][sName] = 0;
+             if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) {
+               oSourceElementData.aPrimitiveValues.push(sName);
+             }
+           }
+           oSourceElementData.aCount[nCategory][sName] += 1;
+         },
+         /**
+          * Consolidates all worthwhile primitive values in a range of source
+          *     elements.
+          * @param {number} nFrom The index (in the source text order) of the
+          *     source element that is the first element of the range.
+          * @param {number} nTo The index (in the source text order) of the
+          *     source element that is the last element of the range.
+          * @param {boolean} bEnclose Indicates whether the range should be
+          *     enclosed within a function call with no argument values to a
+          *     function with an empty parameter list if any primitive values
+          *     are consolidated.
+          * @see TPrimitiveValue#nSaving
+          */
+         fExamineSourceElements = function(nFrom, nTo, bEnclose) {
+           var _,
+               /**
+                * The index of the last mangled name.
+                * @type {number}
+                */
+               nIndex = oScope.cname,
+               /**
+                * The index of the source element that is currently being
+                * considered.
+                * @type {number}
+                */
+               nPosition,
+               /**
+                * A collection of functions used during the consolidation of
+                * primitive values and identifier names used as property
+                * accessors.
+                * @namespace
+                * @type {!Object.<string, function(...[*])>}
+                */
+               oWalkersTransformers = {
+                 /**
+                  * If the String value that is equivalent to the sequence of
+                  * terminal symbols that constitute the encountered identifier
+                  * name is worthwhile, a syntactic conversion from the dot
+                  * notation to the bracket notation ensues with that sequence
+                  * being substituted by an identifier name to which the value
+                  * is assigned.
+                  * Applies to property accessors that use the dot notation.
+                  * @param {!TSyntacticCodeUnit} oExpression The nonterminal
+                  *     MemberExpression.
+                  * @param {string} sIdentifierName The identifier name used as
+                  *     the property accessor.
+                  * @return {!Array} A syntactic code unit that is equivalent to
+                  *     the one encountered.
+                  * @see TPrimitiveValue#nSaving
+                  */
+                 'dot': function(oExpression, sIdentifierName) {
+                   /**
+                    * The prefixed String value that is equivalent to the
+                    * sequence of terminal symbols that constitute the
+                    * encountered identifier name.
+                    * @type {string}
+                    */
+                   var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName;
+
+                   return oSolutionBest.oPrimitiveValues.hasOwnProperty(
+                       sPrefixed) &&
+                       oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
+                       ['sub',
+                        oWalker.walk(oExpression),
+                        ['name',
+                         oSolutionBest.oPrimitiveValues[sPrefixed].sName]] :
+                       ['dot', oWalker.walk(oExpression), sIdentifierName];
+                 },
+                 /**
+                  * If the encountered identifier is a null or Boolean literal
+                  * and its value is worthwhile, the identifier is substituted
+                  * by an identifier name to which that value is assigned.
+                  * Applies to identifier names.
+                  * @param {string} sIdentifier The identifier encountered.
+                  * @return {!Array} A syntactic code unit that is equivalent to
+                  *     the one encountered.
+                  * @see TPrimitiveValue#nSaving
+                  */
+                 'name': function(sIdentifier) {
+                   /**
+                    * The prefixed representation String of the identifier.
+                    * @type {string}
+                    */
+                   var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier;
+
+                   return [
+                     'name',
+                     oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) &&
+                     oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
+                     oSolutionBest.oPrimitiveValues[sPrefixed].sName :
+                     sIdentifier
+                   ];
+                 },
+                 /**
+                  * If the encountered String value is worthwhile, it is
+                  * substituted by an identifier name to which that value is
+                  * assigned.
+                  * Applies to String values.
+                  * @param {string} sStringValue The String value of the string
+                  *     literal encountered.
+                  * @return {!Array} A syntactic code unit that is equivalent to
+                  *     the one encountered.
+                  * @see TPrimitiveValue#nSaving
+                  */
+                 'string': function(sStringValue) {
+                   /**
+                    * The prefixed representation String of the primitive value
+                    * of the literal.
+                    * @type {string}
+                    */
+                   var sPrefixed =
+                       EValuePrefixes.S_STRING + sStringValue;
+
+                   return oSolutionBest.oPrimitiveValues.hasOwnProperty(
+                       sPrefixed) &&
+                       oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
+                       ['name',
+                        oSolutionBest.oPrimitiveValues[sPrefixed].sName] :
+                       ['string', sStringValue];
+                 }
+               },
+               /**
+                * Such data on what to consolidate within the range of source
+                * elements that is currently being considered that lead to the
+                * greatest known reduction of the number of the terminal symbols
+                * in comparison to the original source text.
+                * @type {!TSolution}
+                */
+               oSolutionBest = new TSolution(),
+               /**
+                * Data representing an ongoing attempt to find a better
+                * reduction of the number of the terminal symbols in comparison
+                * to the original source text than the best one that is
+                * currently known.
+                * @type {!TSolution}
+                * @see oSolutionBest
+                */
+               oSolutionCandidate = new TSolution(),
+               /**
+                * A record consisting of data about the range of source elements
+                * that is currently being examined.
+                * @type {!TSourceElementsData}
+                */
+               oSourceElementsData = new TSourceElementsData(),
+               /**
+                * Variable declarations for each primitive value that is to be
+                * consolidated within the elements.
+                * @type {!Array.<!Array>}
+                */
+               aVariableDeclarations = [],
+               /**
+                * Augments a list with a prefixed representation String.
+                * @param {!Array.<string>} aList A list that is to be augmented.
+                * @return {function(string)} A function that augments a list
+                *     with a prefixed representation String.
+                */
+               cAugmentList = function(aList) {
+                 /**
+                  * @param {string} sPrefixed Prefixed representation String of
+                  *     a primitive value that could be consolidated within the
+                  *     elements.
+                  */
+                 var fLambda = function(sPrefixed) {
+                   if (-1 === aList.indexOf(sPrefixed)) {
+                     aList.push(sPrefixed);
+                   }
+                 };
+
+                 return fLambda;
+               },
+               /**
+                * Adds the number of occurrences of a primitive value of a given
+                * category that could be consolidated in the source element with
+                * a given index to the count of occurrences of that primitive
+                * value within the range of source elements that is currently
+                * being considered.
+                * @param {number} nPosition The index (in the source text order)
+                *     of a source element.
+                * @param {number} nCategory The category of the primary
+                *     expression from which the primitive value is derived.
+                * @return {function(string)} A function that performs the
+                *     addition.
+                * @see cAddOccurrencesInCategory
+                */
+               cAddOccurrences = function(nPosition, nCategory) {
+                 /**
+                  * @param {string} sPrefixed The prefixed representation String
+                  *     of a primitive value.
+                  */
+                 var fLambda = function(sPrefixed) {
+                   if (!oSourceElementsData.aCount[nCategory].hasOwnProperty(
+                           sPrefixed)) {
+                     oSourceElementsData.aCount[nCategory][sPrefixed] = 0;
+                   }
+                   oSourceElementsData.aCount[nCategory][sPrefixed] +=
+                       aSourceElementsData[nPosition].aCount[nCategory][
+                           sPrefixed];
+                 };
+
+                 return fLambda;
+               },
+               /**
+                * Adds the number of occurrences of each primitive value of a
+                * given category that could be consolidated in the source
+                * element with a given index to the count of occurrences of that
+                * primitive values within the range of source elements that is
+                * currently being considered.
+                * @param {number} nPosition The index (in the source text order)
+                *     of a source element.
+                * @return {function(number)} A function that performs the
+                *     addition.
+                * @see fAddOccurrences
+                */
+               cAddOccurrencesInCategory = function(nPosition) {
+                 /**
+                  * @param {number} nCategory The category of the primary
+                  *     expression from which the primitive value is derived.
+                  */
+                 var fLambda = function(nCategory) {
+                   Object.keys(
+                       aSourceElementsData[nPosition].aCount[nCategory]
+                   ).forEach(cAddOccurrences(nPosition, nCategory));
+                 };
+
+                 return fLambda;
+               },
+               /**
+                * Adds the number of occurrences of each primitive value that
+                * could be consolidated in the source element with a given index
+                * to the count of occurrences of that primitive values within
+                * the range of source elements that is currently being
+                * considered.
+                * @param {number} nPosition The index (in the source text order)
+                *     of a source element.
+                */
+               fAddOccurrences = function(nPosition) {
+                 Object.keys(aSourceElementsData[nPosition].aCount).forEach(
+                     cAddOccurrencesInCategory(nPosition));
+               },
+               /**
+                * Creates a variable declaration for a primitive value if that
+                * primitive value is to be consolidated within the elements.
+                * @param {string} sPrefixed Prefixed representation String of a
+                *     primitive value that could be consolidated within the
+                *     elements.
+                * @see aVariableDeclarations
+                */
+               cAugmentVariableDeclarations = function(sPrefixed) {
+                 if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) {
+                   aVariableDeclarations.push([
+                     oSolutionBest.oPrimitiveValues[sPrefixed].sName,
+                     [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ?
+                      'name' : 'string',
+                      sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)]
+                   ]);
+                 }
+               },
+               /**
+                * Sorts primitive values with regard to the difference in the
+                * number of terminal symbols between the original source text
+                * and the one with those primitive values consolidated.
+                * @param {string} sPrefixed0 The prefixed representation String
+                *     of the first of the two primitive values that are being
+                *     compared.
+                * @param {string} sPrefixed1 The prefixed representation String
+                *     of the second of the two primitive values that are being
+                *     compared.
+                * @return {number}
+                *     <dl>
+                *         <dt>-1</dt>
+                *         <dd>if the first primitive value must be placed before
+                *              the other one,</dd>
+                *         <dt>0</dt>
+                *         <dd>if the first primitive value may be placed before
+                *              the other one,</dd>
+                *         <dt>1</dt>
+                *         <dd>if the first primitive value must not be placed
+                *              before the other one.</dd>
+                *     </dl>
+                * @see TSolution.oPrimitiveValues
+                */
+               cSortPrimitiveValues = function(sPrefixed0, sPrefixed1) {
+                 /**
+                  * The difference between:
+                  * <ol>
+                  * <li>the difference in the number of terminal symbols
+                  *     between the original source text and the one with the
+                  *     first primitive value consolidated, and</li>
+                  * <li>the difference in the number of terminal symbols
+                  *     between the original source text and the one with the
+                  *     second primitive value consolidated.</li>
+                  * </ol>
+                  * @type {number}
+                  */
+                 var nDifference =
+                     oSolutionCandidate.oPrimitiveValues[sPrefixed0].nSaving -
+                     oSolutionCandidate.oPrimitiveValues[sPrefixed1].nSaving;
+
+                 return nDifference > 0 ? -1 : nDifference < 0 ? 1 : 0;
+               },
+               /**
+                * Assigns an identifier name to a primitive value and calculates
+                * whether instances of that primitive value are worth
+                * consolidating.
+                * @param {string} sPrefixed The prefixed representation String
+                *     of a primitive value that is being evaluated.
+                */
+               fEvaluatePrimitiveValue = function(sPrefixed) {
+                 var _,
+                     /**
+                      * The index of the last mangled name.
+                      * @type {number}
+                      */
+                     nIndex,
+                     /**
+                      * The representation String of the primitive value that is
+                      * being evaluated.
+                      * @type {string}
+                      */
+                     sName =
+                         sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length),
+                     /**
+                      * The number of source characters taken up by the
+                      * representation String of the primitive value that is
+                      * being evaluated.
+                      * @type {number}
+                      */
+                     nLengthOriginal = sName.length,
+                     /**
+                      * The number of source characters taken up by the
+                      * identifier name that could substitute the primitive
+                      * value that is being evaluated.
+                      * substituted.
+                      * @type {number}
+                      */
+                     nLengthSubstitution,
+                     /**
+                      * The number of source characters taken up by by the
+                      * representation String of the primitive value that is
+                      * being evaluated when it is represented by a string
+                      * literal.
+                      * @type {number}
+                      */
+                     nLengthString = oProcessor.make_string(sName).length;
+
+                 oSolutionCandidate.oPrimitiveValues[sPrefixed] =
+                     new TPrimitiveValue();
+                 do {  // Find an identifier unused in this or any nested scope.
+                   nIndex = oScope.cname;
+                   oSolutionCandidate.oPrimitiveValues[sPrefixed].sName =
+                       oScope.next_mangled();
+                 } while (-1 !== oSourceElementsData.aIdentifiers.indexOf(
+                     oSolutionCandidate.oPrimitiveValues[sPrefixed].sName));
+                 nLengthSubstitution = oSolutionCandidate.oPrimitiveValues[
+                     sPrefixed].sName.length;
+                 if (0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC)) {
+                   // foo:null, or foo:null;
+                   oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
+                       nLengthSubstitution + nLengthOriginal +
+                       oWeights.N_VARIABLE_DECLARATION;
+                   // null vs foo
+                   oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
+                       oSourceElementsData.aCount[
+                           EPrimaryExpressionCategories.
+                               N_NULL_AND_BOOLEAN_LITERALS][sPrefixed] *
+                       (nLengthOriginal - nLengthSubstitution);
+                 } else {
+                   // foo:'fromCharCode';
+                   oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving -=
+                       nLengthSubstitution + nLengthString +
+                       oWeights.N_VARIABLE_DECLARATION;
+                   // .fromCharCode vs [foo]
+                   if (oSourceElementsData.aCount[
+                           EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
+                       ].hasOwnProperty(sPrefixed)) {
+                     oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
+                         oSourceElementsData.aCount[
+                             EPrimaryExpressionCategories.N_IDENTIFIER_NAMES
+                         ][sPrefixed] *
+                         (nLengthOriginal - nLengthSubstitution -
+                          oWeights.N_PROPERTY_ACCESSOR);
+                   }
+                   // 'fromCharCode' vs foo
+                   if (oSourceElementsData.aCount[
+                           EPrimaryExpressionCategories.N_STRING_LITERALS
+                       ].hasOwnProperty(sPrefixed)) {
+                     oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving +=
+                         oSourceElementsData.aCount[
+                             EPrimaryExpressionCategories.N_STRING_LITERALS
+                         ][sPrefixed] *
+                         (nLengthString - nLengthSubstitution);
+                   }
+                 }
+                 if (oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving >
+                     0) {
+                   oSolutionCandidate.nSavings +=
+                       oSolutionCandidate.oPrimitiveValues[sPrefixed].nSaving;
+                 } else {
+                   oScope.cname = nIndex; // Free the identifier name.
+                 }
+               },
+               /**
+                * Adds a variable declaration to an existing variable statement.
+                * @param {!Array} aVariableDeclaration A variable declaration
+                *     with an initialiser.
+                */
+               cAddVariableDeclaration = function(aVariableDeclaration) {
+                 (/** @type {!Array} */ oSourceElements[nFrom][1]).unshift(
+                     aVariableDeclaration);
+               };
+
+           if (nFrom > nTo) {
+             return;
+           }
+           // If the range is a closure, reuse the closure.
+           if (nFrom === nTo &&
+               'stat' === oSourceElements[nFrom][0] &&
+               'call' === oSourceElements[nFrom][1][0] &&
+               'function' === oSourceElements[nFrom][1][1][0]) {
+             fExamineSyntacticCodeUnit(oSourceElements[nFrom][1][1]);
+             return;
+           }
+           // Create a list of all derived primitive values within the range.
+           for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
+             aSourceElementsData[nPosition].aPrimitiveValues.forEach(
+                 cAugmentList(oSourceElementsData.aPrimitiveValues));
+           }
+           if (0 === oSourceElementsData.aPrimitiveValues.length) {
+             return;
+           }
+           for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
+             // Add the number of occurrences to the total count.
+             fAddOccurrences(nPosition);
+             // Add identifiers of this or any nested scope to the list.
+             aSourceElementsData[nPosition].aIdentifiers.forEach(
+                 cAugmentList(oSourceElementsData.aIdentifiers));
+           }
+           // Distribute identifier names among derived primitive values.
+           do {  // If there was any progress, find a better distribution.
+             oSolutionBest = oSolutionCandidate;
+             if (Object.keys(oSolutionCandidate.oPrimitiveValues).length > 0) {
+               // Sort primitive values descending by their worthwhileness.
+               oSourceElementsData.aPrimitiveValues.sort(cSortPrimitiveValues);
+             }
+             oSolutionCandidate = new TSolution();
+             oSourceElementsData.aPrimitiveValues.forEach(
+                 fEvaluatePrimitiveValue);
+             oScope.cname = nIndex;
+           } while (oSolutionCandidate.nSavings > oSolutionBest.nSavings);
+           // Take the necessity of adding a variable statement into account.
+           if ('var' !== oSourceElements[nFrom][0]) {
+             oSolutionBest.nSavings -= oWeights.N_VARIABLE_STATEMENT_AFFIXATION;
+           }
+           if (bEnclose) {
+             // Take the necessity of forming a closure into account.
+             oSolutionBest.nSavings -= oWeights.N_CLOSURE;
+           }
+           if (oSolutionBest.nSavings > 0) {
+             // Create variable declarations suitable for UglifyJS.
+             Object.keys(oSolutionBest.oPrimitiveValues).forEach(
+                 cAugmentVariableDeclarations);
+             // Rewrite expressions that contain worthwhile primitive values.
+             for (nPosition = nFrom; nPosition <= nTo; nPosition += 1) {
+               oWalker = oProcessor.ast_walker();
+               oSourceElements[nPosition] =
+                   oWalker.with_walkers(
+                       oWalkersTransformers,
+                       cContext(oWalker, oSourceElements[nPosition]));
+             }
+             if ('var' === oSourceElements[nFrom][0]) {  // Reuse the statement.
+               (/** @type {!Array.<!Array>} */ aVariableDeclarations.reverse(
+                   )).forEach(cAddVariableDeclaration);
+             } else {  // Add a variable statement.
+               Array.prototype.splice.call(
+                   oSourceElements,
+                   nFrom,
+                   0,
+                   ['var', aVariableDeclarations]);
+               nTo += 1;
+             }
+             if (bEnclose) {
+               // Add a closure.
+               Array.prototype.splice.call(
+                   oSourceElements,
+                   nFrom,
+                   0,
+                   ['stat', ['call', ['function', null, [], []], []]]);
+               // Copy source elements into the closure.
+               for (nPosition = nTo + 1; nPosition > nFrom; nPosition -= 1) {
+                 Array.prototype.unshift.call(
+                     oSourceElements[nFrom][1][1][3],
+                     oSourceElements[nPosition]);
+               }
+               // Remove source elements outside the closure.
+               Array.prototype.splice.call(
+                   oSourceElements,
+                   nFrom + 1,
+                   nTo - nFrom + 1);
+             }
+           }
+           if (bEnclose) {
+             // Restore the availability of identifier names.
+             oScope.cname = nIndex;
+           }
+         };
+
+     oSourceElements = (/** @type {!TSyntacticCodeUnit} */
+         oSyntacticCodeUnit[bIsGlobal ? 1 : 3]);
+     if (0 === oSourceElements.length) {
+       return;
+     }
+     oScope = bIsGlobal ? oSyntacticCodeUnit.scope : oSourceElements.scope;
+     // Skip a Directive Prologue.
+     while (nAfterDirectivePrologue < oSourceElements.length &&
+            'stat' === oSourceElements[nAfterDirectivePrologue][0] &&
+            'string' === oSourceElements[nAfterDirectivePrologue][1][0]) {
+       nAfterDirectivePrologue += 1;
+       aSourceElementsData.push(null);
+     }
+     if (oSourceElements.length === nAfterDirectivePrologue) {
+       return;
+     }
+     for (nPosition = nAfterDirectivePrologue;
+          nPosition < oSourceElements.length;
+          nPosition += 1) {
+       oSourceElementData = new TSourceElementsData();
+       oWalker = oProcessor.ast_walker();
+       // Classify a source element.
+       // Find its derived primitive values and count their occurrences.
+       // Find all identifiers used (including nested scopes).
+       oWalker.with_walkers(
+           oWalkers.oSurveySourceElement,
+           cContext(oWalker, oSourceElements[nPosition]));
+       // Establish whether the scope is still wholly examinable.
+       bIsWhollyExaminable = bIsWhollyExaminable &&
+           ESourceElementCategories.N_WITH !== oSourceElementData.nCategory &&
+           ESourceElementCategories.N_EVAL !== oSourceElementData.nCategory;
+       aSourceElementsData.push(oSourceElementData);
+     }
+     if (bIsWhollyExaminable) {  // Examine the whole scope.
+       fExamineSourceElements(
+           nAfterDirectivePrologue,
+           oSourceElements.length - 1,
+           false);
+     } else {  // Examine unexcluded ranges of source elements.
+       for (nPosition = oSourceElements.length - 1;
+            nPosition >= nAfterDirectivePrologue;
+            nPosition -= 1) {
+         oSourceElementData = (/** @type {!TSourceElementsData} */
+             aSourceElementsData[nPosition]);
+         if (ESourceElementCategories.N_OTHER ===
+             oSourceElementData.nCategory) {
+           if ('undefined' === typeof nTo) {
+             nTo = nPosition;  // Indicate the end of a range.
+           }
+           // Examine the range if it immediately follows a Directive Prologue.
+           if (nPosition === nAfterDirectivePrologue) {
+             fExamineSourceElements(nPosition, nTo, true);
+           }
+         } else {
+           if ('undefined' !== typeof nTo) {
+             // Examine the range that immediately follows this source element.
+             fExamineSourceElements(nPosition + 1, nTo, true);
+             nTo = void 0;  // Obliterate the range.
+           }
+           // Examine nested functions.
+           oWalker = oProcessor.ast_walker();
+           oWalker.with_walkers(
+               oWalkers.oExamineFunctions,
+               cContext(oWalker, oSourceElements[nPosition]));
+         }
+       }
+     }
+   }(oAbstractSyntaxTree = oProcessor.ast_add_scope(oAbstractSyntaxTree)));
+  return oAbstractSyntaxTree;
+};
+/*jshint sub:false */
+
+
+if (require.main === module) {
+  (function() {
+    'use strict';
+    /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
+         latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
+         onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
+         sub:false, trailing:true */
+
+    var _,
+        /**
+         * NodeJS module for unit testing.
+         * @namespace
+         * @type {!TAssert}
+         * @see http://nodejs.org/docs/v0.6.10/api/all.html#assert
+         */
+        oAssert = (/** @type {!TAssert} */ require('assert')),
+        /**
+         * The parser of ECMA-262 found in UglifyJS.
+         * @namespace
+         * @type {!TParser}
+         */
+        oParser = (/** @type {!TParser} */ require('./parse-js')),
+        /**
+         * The processor of <abbr title="abstract syntax tree">AST</abbr>s
+         * found in UglifyJS.
+         * @namespace
+         * @type {!TProcessor}
+         */
+        oProcessor = (/** @type {!TProcessor} */ require('./process')),
+        /**
+         * An instance of an object that allows the traversal of an <abbr
+         * title="abstract syntax tree">AST</abbr>.
+         * @type {!TWalker}
+         */
+        oWalker,
+        /**
+         * A collection of functions for the removal of the scope information
+         * during the traversal of an <abbr title="abstract syntax tree"
+         * >AST</abbr>.
+         * @namespace
+         * @type {!Object.<string, function(...[*])>}
+         */
+        oWalkersPurifiers = {
+          /**#nocode+*/  // JsDoc Toolkit 2.4.0 hides some of the keys.
+          /**
+           * Deletes the scope information from the branch of the abstract
+           * syntax tree representing the encountered function declaration.
+           * @param {string} sIdentifier The identifier of the function.
+           * @param {!Array.<string>} aFormalParameterList Formal parameters.
+           * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
+           */
+          'defun': function(
+              sIdentifier,
+              aFormalParameterList,
+              oFunctionBody) {
+            delete oFunctionBody.scope;
+          },
+          /**
+           * Deletes the scope information from the branch of the abstract
+           * syntax tree representing the encountered function expression.
+           * @param {?string} sIdentifier The optional identifier of the
+           *     function.
+           * @param {!Array.<string>} aFormalParameterList Formal parameters.
+           * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
+           */
+          'function': function(
+              sIdentifier,
+              aFormalParameterList,
+              oFunctionBody) {
+            delete oFunctionBody.scope;
+          }
+          /**#nocode-*/  // JsDoc Toolkit 2.4.0 hides some of the keys.
+        },
+        /**
+         * Initiates the traversal of a source element.
+         * @param {!TWalker} oWalker An instance of an object that allows the
+         *     traversal of an abstract syntax tree.
+         * @param {!TSyntacticCodeUnit} oSourceElement A source element from
+         *     which the traversal should commence.
+         * @return {function(): !TSyntacticCodeUnit} A function that is able to
+         *     initiate the traversal from a given source element.
+         */
+        cContext = function(oWalker, oSourceElement) {
+          /**
+           * @return {!TSyntacticCodeUnit} A function that is able to
+           *     initiate the traversal from a given source element.
+           */
+          var fLambda = function() {
+            return oWalker.walk(oSourceElement);
+          };
+
+          return fLambda;
+        },
+        /**
+         * A record consisting of configuration for the code generation phase.
+         * @type {!Object}
+         */
+        oCodeGenerationOptions = {
+          beautify: true
+        },
+        /**
+         * Tests whether consolidation of an ECMAScript program yields expected
+         * results.
+         * @param {{
+         *       sTitle: string,
+         *       sInput: string,
+         *       sOutput: string
+         *     }} oUnitTest A record consisting of data about a unit test: its
+         *     name, an ECMAScript program, and, if consolidation is to take
+         *     place, the resulting ECMAScript program.
+         */
+        cAssert = function(oUnitTest) {
+          var _,
+              /**
+               * An array-like object representing the <abbr title=
+               * "abstract syntax tree">AST</abbr> obtained after consolidation.
+               * @type {!TSyntacticCodeUnit}
+               */
+              oSyntacticCodeUnitActual =
+                  exports.ast_consolidate(oParser.parse(oUnitTest.sInput)),
+              /**
+               * An array-like object representing the expected <abbr title=
+               * "abstract syntax tree">AST</abbr>.
+               * @type {!TSyntacticCodeUnit}
+               */
+              oSyntacticCodeUnitExpected = oParser.parse(
+                  oUnitTest.hasOwnProperty('sOutput') ?
+                  oUnitTest.sOutput : oUnitTest.sInput);
+
+          delete oSyntacticCodeUnitActual.scope;
+          oWalker = oProcessor.ast_walker();
+          oWalker.with_walkers(
+              oWalkersPurifiers,
+              cContext(oWalker, oSyntacticCodeUnitActual));
+          try {
+            oAssert.deepEqual(
+                oSyntacticCodeUnitActual,
+                oSyntacticCodeUnitExpected);
+          } catch (oException) {
+            console.error(
+                '########## A unit test has failed.\n' +
+                oUnitTest.sTitle + '\n' +
+                '#####  actual code  (' +
+                oProcessor.gen_code(oSyntacticCodeUnitActual).length +
+                ' bytes)\n' +
+                oProcessor.gen_code(
+                    oSyntacticCodeUnitActual,
+                    oCodeGenerationOptions) + '\n' +
+                '##### expected code (' +
+                oProcessor.gen_code(oSyntacticCodeUnitExpected).length +
+                ' bytes)\n' +
+                oProcessor.gen_code(
+                    oSyntacticCodeUnitExpected,
+                    oCodeGenerationOptions));
+          }
+        };
+
+    [
+      // 7.6.1 Reserved Words.
+      {
+        sTitle:
+            'Omission of keywords while choosing an identifier name.',
+        sInput:
+            '(function() {' +
+            '  var a, b, c, d, e, f, g, h, i, j, k, l, m,' +
+            '      n, o, p, q, r, s, t, u, v, w, x, y, z,' +
+            '      A, B, C, D, E, F, G, H, I, J, K, L, M,' +
+            '      N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
+            '      $, _,' +
+            '      aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am,' +
+            '      an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az,' +
+            '      aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM,' +
+            '      aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ,' +
+            '      a$, a_,' +
+            '      ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm,' +
+            '      bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz,' +
+            '      bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM,' +
+            '      bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ,' +
+            '      b$, b_,' +
+            '      ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm,' +
+            '      cn, co, cp, cq, cr, cs, ct, cu, cv, cw, cx, cy, cz,' +
+            '      cA, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM,' +
+            '      cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ,' +
+            '      c$, c_,' +
+            '      da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm,' +
+            '      dn, dq, dr, ds, dt, du, dv, dw, dx, dy, dz,' +
+            '      dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM,' +
+            '      dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ,' +
+            '      d$, d_;' +
+            '  void ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",' +
+            '        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var dp =' +
+            '      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",' +
+            '      a, b, c, d, e, f, g, h, i, j, k, l, m,' +
+            '      n, o, p, q, r, s, t, u, v, w, x, y, z,' +
+            '      A, B, C, D, E, F, G, H, I, J, K, L, M,' +
+            '      N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
+            '      $, _,' +
+            '      aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am,' +
+            '      an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az,' +
+            '      aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM,' +
+            '      aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ,' +
+            '      a$, a_,' +
+            '      ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm,' +
+            '      bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz,' +
+            '      bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM,' +
+            '      bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, bX, bY, bZ,' +
+            '      b$, b_,' +
+            '      ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm,' +
+            '      cn, co, cp, cq, cr, cs, ct, cu, cv, cw, cx, cy, cz,' +
+            '      cA, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM,' +
+            '      cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ,' +
+            '      c$, c_,' +
+            '      da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm,' +
+            '      dn, dq, dr, ds, dt, du, dv, dw, dx, dy, dz,' +
+            '      dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM,' +
+            '      dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ,' +
+            '      d$, d_;' +
+            '  void [dp, dp];' +
+            '}());'
+      },
+      // 7.8.1 Null Literals.
+      {
+        sTitle:
+            'Evaluation with regard to the null value.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [null, null, null];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [null, null];' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var a = null, foo;' +
+            '  void [a, a, a];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [null, null];' +
+            '}());'
+      },
+      // 7.8.2 Boolean Literals.
+      {
+        sTitle:
+            'Evaluation with regard to the false value.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [false, false, false];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [false, false];' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var a = false, foo;' +
+            '  void [a, a, a];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [false, false];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Evaluation with regard to the true value.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [true, true, true];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [true, true];' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var a = true, foo;' +
+            '  void [a, a, a];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void [true, true];' +
+            '}());'
+      },
+      // 7.8.4 String Literals.
+      {
+        sTitle:
+            'Evaluation with regard to the String value of a string literal.',
+        sInput:
+            '(function() {' +
+            '  var foo;' +
+            '  void ["abcd", "abcd", "abc", "abc"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcd", foo;' +
+            '  void [a, a, "abc", "abc"];' +
+            '}());'
+      },
+      // 7.8.5 Regular Expression Literals.
+      {
+        sTitle:
+            'Preservation of the pattern of a regular expression literal.',
+        sInput:
+            'void [/abcdefghijklmnopqrstuvwxyz/, /abcdefghijklmnopqrstuvwxyz/];'
+      },
+      {
+        sTitle:
+            'Preservation of the flags of a regular expression literal.',
+        sInput:
+            'void [/(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim,' +
+            '      /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim,' +
+            '      /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim, /(?:)/gim];'
+      },
+      // 10.2 Lexical Environments.
+      {
+        sTitle:
+            'Preservation of identifier names in the same scope.',
+        sInput:
+            '/*jshint shadow:true */' +
+            'var a;' +
+            'function b(i) {' +
+            '}' +
+            'for (var c; 0 === Math.random(););' +
+            'for (var d in {});' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            'void [b(a), b(c), b(d)];' +
+            'void [typeof e];' +
+            'i: for (; 0 === Math.random();) {' +
+            '  if (42 === (new Date()).getMinutes()) {' +
+            '    continue i;' +
+            '  } else {' +
+            '    break i;' +
+            '  }' +
+            '}' +
+            'try {' +
+            '} catch (f) {' +
+            '} finally {' +
+            '}' +
+            '(function g(h) {' +
+            '}());' +
+            'void [{' +
+            '  i: 42,' +
+            '  "j": 42,' +
+            '  \'k\': 42' +
+            '}];' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];',
+        sOutput:
+            '/*jshint shadow:true */' +
+            'var a;' +
+            'function b(i) {' +
+            '}' +
+            'for (var c; 0 === Math.random(););' +
+            'for (var d in {});' +
+            '(function() {' +
+            '  var i = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [i];' +
+            '  void [b(a), b(c), b(d)];' +
+            '  void [typeof e];' +
+            '  i: for (; 0 === Math.random();) {' +
+            '    if (42 === (new Date()).getMinutes()) {' +
+            '      continue i;' +
+            '    } else {' +
+            '      break i;' +
+            '    }' +
+            '  }' +
+            '  try {' +
+            '  } catch (f) {' +
+            '  } finally {' +
+            '  }' +
+            '  (function g(h) {' +
+            '  }());' +
+            '  void [{' +
+            '    i: 42,' +
+            '    "j": 42,' +
+            '    \'k\': 42' +
+            '  }];' +
+            '  void [i];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Preservation of identifier names in nested function code.',
+        sInput:
+            '(function() {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  (function() {' +
+            '    var a;' +
+            '    for (var b; 0 === Math.random(););' +
+            '    for (var c in {});' +
+            '    void [typeof d];' +
+            '    h: for (; 0 === Math.random();) {' +
+            '      if (42 === (new Date()).getMinutes()) {' +
+            '        continue h;' +
+            '      } else {' +
+            '        break h;' +
+            '      }' +
+            '    }' +
+            '    try {' +
+            '    } catch (e) {' +
+            '    } finally {' +
+            '    }' +
+            '    (function f(g) {' +
+            '    }());' +
+            '    void [{' +
+            '      h: 42,' +
+            '      "i": 42,' +
+            '      \'j\': 42' +
+            '    }];' +
+            '  }());' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var h = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [h];' +
+            '  (function() {' +
+            '    var a;' +
+            '    for (var b; 0 === Math.random(););' +
+            '    for (var c in {});' +
+            '    void [typeof d];' +
+            '    h: for (; 0 === Math.random();) {' +
+            '      if (42 === (new Date()).getMinutes()) {' +
+            '        continue h;' +
+            '      } else {' +
+            '        break h;' +
+            '      }' +
+            '    }' +
+            '    try {' +
+            '    } catch (e) {' +
+            '    } finally {' +
+            '    }' +
+            '    (function f(g) {' +
+            '    }());' +
+            '    void [{' +
+            '      h: 42,' +
+            '      "i": 42,' +
+            '      \'j\': 42' +
+            '    }];' +
+            '  }());' +
+            '  void [h];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Consolidation of a closure with other source elements.',
+        sInput:
+            '(function(foo) {' +
+            '}("abcdefghijklmnopqrstuvwxyz"));' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  (function(foo) {' +
+            '  })(a);' +
+            '  void [a];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Consolidation of function code instead of a sole closure.',
+        sInput:
+            '(function(foo, bar) {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"));',
+        sOutput:
+            '(function(foo, bar) {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"));'
+      },
+      // 11.1.5 Object Initialiser.
+      {
+        sTitle:
+            'Preservation of property names of an object initialiser.',
+        sInput:
+            'var foo = {' +
+            '  abcdefghijklmnopqrstuvwxyz: 42,' +
+            '  "zyxwvutsrqponmlkjihgfedcba": 42,' +
+            '  \'mlkjihgfedcbanopqrstuvwxyz\': 42' +
+            '};' +
+            'void [' +
+            '  foo.abcdefghijklmnopqrstuvwxyz,' +
+            '  "zyxwvutsrqponmlkjihgfedcba",' +
+            '  \'mlkjihgfedcbanopqrstuvwxyz\'' +
+            '];'
+      },
+      {
+        sTitle:
+            'Evaluation with regard to String values derived from identifier ' +
+            'names used as property accessors.',
+        sInput:
+            '(function() {' +
+            '  var foo;' +
+            '  void [' +
+            '    Math.abcdefghij,' +
+            '    Math.abcdefghij,' +
+            '    Math.abcdefghi,' +
+            '    Math.abcdefghi' +
+            '  ];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcdefghij", foo;' +
+            '  void [' +
+            '    Math[a],' +
+            '    Math[a],' +
+            '    Math.abcdefghi,' +
+            '    Math.abcdefghi' +
+            '  ];' +
+            '}());'
+      },
+      // 11.2.1 Property Accessors.
+      {
+        sTitle:
+            'Preservation of identifiers in the nonterminal MemberExpression.',
+        sInput:
+            'void [' +
+            '  Math.E,' +
+            '  Math.LN10,' +
+            '  Math.LN2,' +
+            '  Math.LOG2E,' +
+            '  Math.LOG10E,' +
+            '  Math.PI,' +
+            '  Math.SQRT1_2,' +
+            '  Math.SQRT2,' +
+            '  Math.abs,' +
+            '  Math.acos' +
+            '];'
+      },
+      // 12.2 Variable Statement.
+      {
+        sTitle:
+            'Preservation of the identifier of a variable that is being ' +
+            'declared in a variable statement.',
+        sInput:
+            '(function() {' +
+            '  var abcdefghijklmnopqrstuvwxyz;' +
+            '  void [abcdefghijklmnopqrstuvwxyz];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Exclusion of a variable statement in global code.',
+        sInput:
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            'var foo = "abcdefghijklmnopqrstuvwxyz",' +
+            '    bar = "abcdefghijklmnopqrstuvwxyz";' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];'
+      },
+      {
+        sTitle:
+            'Exclusion of a variable statement in function code that ' +
+            'contains a with statement.',
+        sInput:
+            '(function() {' +
+            '  with ({});' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  var foo;' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Exclusion of a variable statement in function code that ' +
+            'contains a direct call to the eval function.',
+        sInput:
+            '/*jshint evil:true */' +
+            'void [' +
+            '  function() {' +
+            '    eval("");' +
+            '    void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '    var foo;' +
+            '    void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  }' +
+            '];'
+      },
+      {
+        sTitle:
+            'Consolidation within a variable statement in global code.',
+        sInput:
+            'var foo = function() {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '};',
+        sOutput:
+            'var foo = function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '};'
+      },
+      {
+        sTitle:
+            'Consolidation within a variable statement excluded in function ' +
+            'code due to the presence of a with statement.',
+        sInput:
+            '(function() {' +
+            '  with ({});' +
+            '  var foo = function() {' +
+            '    void ["abcdefghijklmnopqrstuvwxyz",' +
+            '          "abcdefghijklmnopqrstuvwxyz"];' +
+            '  };' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  with ({});' +
+            '  var foo = function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  };' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Consolidation within a variable statement excluded in function ' +
+            'code due to the presence of a direct call to the eval function.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  eval("");' +
+            '  var foo = function() {' +
+            '    void ["abcdefghijklmnopqrstuvwxyz",' +
+            '          "abcdefghijklmnopqrstuvwxyz"];' +
+            '  };' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  eval("");' +
+            '  var foo = function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  };' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Inclusion of a variable statement in function code that ' +
+            'contains no with statement and no direct call to the eval ' +
+            'function.',
+        sInput:
+            '(function() {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  var foo;' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a];' +
+            '  var foo;' +
+            '  void [a];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Ignorance with regard to a variable statement in global code.',
+        sInput:
+            'var foo = "abcdefghijklmnopqrstuvwxyz";' +
+            'void ["abcdefghijklmnopqrstuvwxyz",' +
+            '      "abcdefghijklmnopqrstuvwxyz"];',
+        sOutput:
+            'var foo = "abcdefghijklmnopqrstuvwxyz";' +
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());'
+      },
+      // 12.4 Expression Statement.
+      {
+        sTitle:
+            'Preservation of identifiers in an expression statement.',
+        sInput:
+            'void [typeof abcdefghijklmnopqrstuvwxyz,' +
+            '      typeof abcdefghijklmnopqrstuvwxyz];'
+      },
+      // 12.6.3 The {@code for} Statement.
+      {
+        sTitle:
+            'Preservation of identifiers in the variable declaration list of ' +
+            'a for statement.',
+        sInput:
+            'for (var abcdefghijklmnopqrstuvwxyz; 0 === Math.random(););' +
+            'for (var abcdefghijklmnopqrstuvwxyz; 0 === Math.random(););'
+      },
+      // 12.6.4 The {@code for-in} Statement.
+      {
+        sTitle:
+            'Preservation of identifiers in the variable declaration list of ' +
+            'a for-in statement.',
+        sInput:
+            'for (var abcdefghijklmnopqrstuvwxyz in {});' +
+            'for (var abcdefghijklmnopqrstuvwxyz in {});'
+      },
+      // 12.7 The {@code continue} Statement.
+      {
+        sTitle:
+            'Preservation of the identifier in a continue statement.',
+        sInput:
+            'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
+            '  continue abcdefghijklmnopqrstuvwxyz;' +
+            '}' +
+            'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
+            '  continue abcdefghijklmnopqrstuvwxyz;' +
+            '}'
+      },
+      // 12.8 The {@code break} Statement.
+      {
+        sTitle:
+            'Preservation of the identifier in a break statement.',
+        sInput:
+            'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
+            '  break abcdefghijklmnopqrstuvwxyz;' +
+            '}' +
+            'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random();) {' +
+            '  break abcdefghijklmnopqrstuvwxyz;' +
+            '}'
+      },
+      // 12.9 The {@code return} Statement.
+      {
+        sTitle:
+            'Exclusion of a return statement in function code that contains ' +
+            'a with statement.',
+        sInput:
+            '(function() {' +
+            '  with ({});' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  if (0 === Math.random()) {' +
+            '    return;' +
+            '  } else {' +
+            '  }' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Exclusion of a return statement in function code that contains ' +
+            'a direct call to the eval function.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  eval("");' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  if (0 === Math.random()) {' +
+            '    return;' +
+            '  } else {' +
+            '  }' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Consolidation within a return statement excluded in function ' +
+            'code due to the presence of a with statement.',
+        sInput:
+            '(function() {' +
+            '  with ({});' +
+            '  return function() {' +
+            '    void ["abcdefghijklmnopqrstuvwxyz",' +
+            '          "abcdefghijklmnopqrstuvwxyz"];' +
+            '  };' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  with ({});' +
+            '  return function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  };' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Consolidation within a return statement excluded in function ' +
+            'code due to the presence of a direct call to the eval function.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  eval("");' +
+            '  return function() {' +
+            '    void ["abcdefghijklmnopqrstuvwxyz",' +
+            '          "abcdefghijklmnopqrstuvwxyz"];' +
+            '  };' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  eval("");' +
+            '  return function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  };' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Inclusion of a return statement in function code that contains ' +
+            'no with statement and no direct call to the eval function.',
+        sInput:
+            '(function() {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '  if (0 === Math.random()) {' +
+            '    return;' +
+            '  } else {' +
+            '  }' +
+            '  void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a];' +
+            '  if (0 === Math.random()) {' +
+            '    return;' +
+            '  } else {' +
+            '  }' +
+            '  void [a];' +
+            '}());'
+      },
+      // 12.10 The {@code with} Statement.
+      {
+        sTitle:
+            'Preservation of the statement in a with statement.',
+        sInput:
+            'with ({}) {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}'
+      },
+      {
+        sTitle:
+            'Exclusion of a with statement in the same syntactic code unit.',
+        sInput:
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            'with ({' +
+            '  foo: "abcdefghijklmnopqrstuvwxyz",' +
+            '  bar: "abcdefghijklmnopqrstuvwxyz"' +
+            '}) {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];'
+      },
+      {
+        sTitle:
+            'Exclusion of a with statement in nested function code.',
+        sInput:
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '(function() {' +
+            '  with ({' +
+            '    foo: "abcdefghijklmnopqrstuvwxyz",' +
+            '    bar: "abcdefghijklmnopqrstuvwxyz"' +
+            '  }) {' +
+            '    void ["abcdefghijklmnopqrstuvwxyz",' +
+            '          "abcdefghijklmnopqrstuvwxyz"];' +
+            '  }' +
+            '}());' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];'
+      },
+      // 12.12 Labelled Statements.
+      {
+        sTitle:
+            'Preservation of the label of a labelled statement.',
+        sInput:
+            'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random(););' +
+            'abcdefghijklmnopqrstuvwxyz: for (; 0 === Math.random(););'
+      },
+      // 12.14 The {@code try} Statement.
+      {
+        sTitle:
+            'Preservation of the identifier in the catch clause of a try' +
+            'statement.',
+        sInput:
+            'try {' +
+            '} catch (abcdefghijklmnopqrstuvwxyz) {' +
+            '} finally {' +
+            '}' +
+            'try {' +
+            '} catch (abcdefghijklmnopqrstuvwxyz) {' +
+            '} finally {' +
+            '}'
+      },
+      // 13 Function Definition.
+      {
+        sTitle:
+            'Preservation of the identifier of a function declaration.',
+        sInput:
+            'function abcdefghijklmnopqrstuvwxyz() {' +
+            '}' +
+            'void [abcdefghijklmnopqrstuvwxyz];'
+      },
+      {
+        sTitle:
+            'Preservation of the identifier of a function expression.',
+        sInput:
+            'void [' +
+            '  function abcdefghijklmnopqrstuvwxyz() {' +
+            '  },' +
+            '  function abcdefghijklmnopqrstuvwxyz() {' +
+            '  }' +
+            '];'
+      },
+      {
+        sTitle:
+            'Preservation of a formal parameter of a function declaration.',
+        sInput:
+            'function foo(abcdefghijklmnopqrstuvwxyz) {' +
+            '}' +
+            'function bar(abcdefghijklmnopqrstuvwxyz) {' +
+            '}'
+      },
+      {
+        sTitle:
+            'Preservation of a formal parameter in a function expression.',
+        sInput:
+            'void [' +
+            '  function(abcdefghijklmnopqrstuvwxyz) {' +
+            '  },' +
+            '  function(abcdefghijklmnopqrstuvwxyz) {' +
+            '  }' +
+            '];'
+      },
+      {
+        sTitle:
+            'Exclusion of a function declaration.',
+        sInput:
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            'function foo() {' +
+            '}' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];'
+      },
+      {
+        sTitle:
+            'Consolidation within a function declaration.',
+        sInput:
+            'function foo() {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}',
+        sOutput:
+            'function foo() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}'
+      },
+      // 14 Program.
+      {
+        sTitle:
+            'Preservation of a program without source elements.',
+        sInput:
+            ''
+      },
+      // 14.1 Directive Prologues and the Use Strict Directive.
+      {
+        sTitle:
+            'Preservation of a Directive Prologue in global code.',
+        sInput:
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            '\'zyxwvutsrqponmlkjihgfedcba\';'
+      },
+      {
+        sTitle:
+            'Preservation of a Directive Prologue in a function declaration.',
+        sInput:
+            'function foo() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  \'zyxwvutsrqponmlkjihgfedcba\';' +
+            '}'
+      },
+      {
+        sTitle:
+            'Preservation of a Directive Prologue in a function expression.',
+        sInput:
+            'void [' +
+            '  function() {' +
+            '    "abcdefghijklmnopqrstuvwxyz";' +
+            '    \'zyxwvutsrqponmlkjihgfedcba\';' +
+            '  }' +
+            '];'
+      },
+      {
+        sTitle:
+            'Ignorance with regard to a Directive Prologue in global code.',
+        sInput:
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            'void ["abcdefghijklmnopqrstuvwxyz",' +
+            '      "abcdefghijklmnopqrstuvwxyz"];',
+        sOutput:
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Ignorance with regard to a Directive Prologue in a function' +
+            'declaration.',
+        sInput:
+            'function foo() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}',
+        sOutput:
+            'function foo() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}'
+      },
+      {
+        sTitle:
+            'Ignorance with regard to a Directive Prologue in a function' +
+            'expression.',
+        sInput:
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());'
+      },
+      // 15.1 The Global Object.
+      {
+        sTitle:
+            'Preservation of a property of the global object.',
+        sInput:
+            'void [undefined, undefined, undefined, undefined, undefined];'
+      },
+      // 15.1.2.1.1 Direct Call to Eval.
+      {
+        sTitle:
+            'Exclusion of a direct call to the eval function in the same ' +
+            'syntactic code unit.',
+        sInput:
+            '/*jshint evil:true */' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            'eval("");' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];'
+      },
+      {
+        sTitle:
+            'Exclusion of a direct call to the eval function in nested ' +
+            'function code.',
+        sInput:
+            '/*jshint evil:true */' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];' +
+            '(function() {' +
+            '  eval("");' +
+            '}());' +
+            'void ["abcdefghijklmnopqrstuvwxyz"];'
+      },
+      {
+        sTitle:
+            'Consolidation within a direct call to the eval function.',
+        sInput:
+            '/*jshint evil:true */' +
+            'eval(function() {' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            'eval(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());'
+      },
+      // Consolidation proper.
+      {
+        sTitle:
+            'No consolidation if it does not result in a reduction of the ' +
+            'number of source characters.',
+        sInput:
+            '(function() {' +
+            '  var foo;' +
+            '  void ["ab", "ab", "abc", "abc"];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Identification of a range of source elements at the beginning ' +
+            'of global code.',
+        sInput:
+            '/*jshint evil:true */' +
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            'void ["abcdefghijklmnopqrstuvwxyz",' +
+            '      "abcdefghijklmnopqrstuvwxyz"];' +
+            'eval("");',
+        sOutput:
+            '/*jshint evil:true */' +
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());' +
+            'eval("");'
+      },
+      {
+        sTitle:
+            'Identification of a range of source elements in the middle of ' +
+            'global code.',
+        sInput:
+            '/*jshint evil:true */' +
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            'eval("");' +
+            'void ["abcdefghijklmnopqrstuvwxyz",' +
+            '      "abcdefghijklmnopqrstuvwxyz"];' +
+            'eval("");',
+        sOutput:
+            '/*jshint evil:true */' +
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            'eval("");' +
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());' +
+            'eval("");'
+      },
+      {
+        sTitle:
+            'Identification of a range of source elements at the end of ' +
+            'global code.',
+        sInput:
+            '/*jshint evil:true */' +
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            'eval("");' +
+            'void ["abcdefghijklmnopqrstuvwxyz",' +
+            '      "abcdefghijklmnopqrstuvwxyz"];',
+        sOutput:
+            '/*jshint evil:true */' +
+            '"abcdefghijklmnopqrstuvwxyz";' +
+            'eval("");' +
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Identification of a range of source elements at the beginning ' +
+            'of function code.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '  eval("");' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  (function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  }());' +
+            '  eval("");' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Identification of a range of source elements in the middle of ' +
+            'function code.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  eval("");' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '  eval("");' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  eval("");' +
+            '  (function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  }());' +
+            '  eval("");' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Identification of a range of source elements at the end of ' +
+            'function code.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  eval("");' +
+            '  void ["abcdefghijklmnopqrstuvwxyz",' +
+            '        "abcdefghijklmnopqrstuvwxyz"];' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  "abcdefghijklmnopqrstuvwxyz";' +
+            '  eval("");' +
+            '  (function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '    void [a, a];' +
+            '  }());' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Evaluation with regard to String values of String literals and ' +
+            'String values derived from identifier names used as property' +
+            'accessors.',
+        sInput:
+            '(function() {' +
+            '  var foo;' +
+            '  void ["abcdefg", Math.abcdefg, "abcdef", Math.abcdef];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcdefg", foo;' +
+            '  void [a, Math[a], "abcdef", Math.abcdef];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Evaluation with regard to the necessity of adding a variable ' +
+            'statement.',
+        sInput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  void ["abcdefgh", "abcdefgh"];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  void ["abcdefg", "abcdefg"];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var foo;' +
+            '  void ["abcd", "abcd"];' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var a = "abcdefgh";' +
+            '  void [a, a];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  void ["abcdefg", "abcdefg"];' +
+            '}());' +
+            'eval("");' +
+            '(function() {' +
+            '  var a = "abcd", foo;' +
+            '  void [a, a];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Evaluation with regard to the necessity of enclosing source ' +
+            'elements.',
+        sInput:
+            '/*jshint evil:true */' +
+            'void ["abcdefghijklmnopqrstuvwxy", "abcdefghijklmnopqrstuvwxy"];' +
+            'eval("");' +
+            'void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' +
+            'eval("");' +
+            '(function() {' +
+            '  void ["abcdefgh", "abcdefgh"];' +
+            '}());' +
+            '(function() {' +
+            '  void ["abcdefghijklmnopqrstuvwxy",' +
+            '        "abcdefghijklmnopqrstuvwxy"];' +
+            '  eval("");' +
+            '  void ["abcdefghijklmnopqrstuvwx",' +
+            '        "abcdefghijklmnopqrstuvwx"];' +
+            '  eval("");' +
+            '  (function() {' +
+            '    void ["abcdefgh", "abcdefgh"];' +
+            '  }());' +
+            '}());',
+        sOutput:
+            '/*jshint evil:true */' +
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxy";' +
+            '  void [a, a];' +
+            '}());' +
+            'eval("");' +
+            'void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' +
+            'eval("");' +
+            '(function() {' +
+            '  var a = "abcdefgh";' +
+            '  void [a, a];' +
+            '}());' +
+            '(function() {' +
+            '  (function() {' +
+            '    var a = "abcdefghijklmnopqrstuvwxy";' +
+            '    void [a, a];' +
+            '  }());' +
+            '  eval("");' +
+            '  void ["abcdefghijklmnopqrstuvwx", "abcdefghijklmnopqrstuvwx"];' +
+            '  eval("");' +
+            '  (function() {' +
+            '    var a = "abcdefgh";' +
+            '    void [a, a];' +
+            '  }());' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Employment of a closure while consolidating in global code.',
+        sInput:
+            'void ["abcdefghijklmnopqrstuvwxyz",' +
+            '      "abcdefghijklmnopqrstuvwxyz"];',
+        sOutput:
+            '(function() {' +
+            '  var a = "abcdefghijklmnopqrstuvwxyz";' +
+            '  void [a, a];' +
+            '}());'
+      },
+      {
+        sTitle:
+            'Assignment of a shorter identifier to a value whose ' +
+            'consolidation results in a greater reduction of the number of ' +
+            'source characters.',
+        sInput:
+            '(function() {' +
+            '  var b, c, d, e, f, g, h, i, j, k, l, m,' +
+            '      n, o, p, q, r, s, t, u, v, w, x, y, z,' +
+            '      A, B, C, D, E, F, G, H, I, J, K, L, M,' +
+            '      N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
+            '      $, _;' +
+            '  void ["abcde", "abcde", "edcba", "edcba", "edcba"];' +
+            '}());',
+        sOutput:
+            '(function() {' +
+            '  var a = "edcba",' +
+            '      b, c, d, e, f, g, h, i, j, k, l, m,' +
+            '      n, o, p, q, r, s, t, u, v, w, x, y, z,' +
+            '      A, B, C, D, E, F, G, H, I, J, K, L, M,' +
+            '      N, O, P, Q, R, S, T, U, V, W, X, Y, Z,' +
+            '      $, _;' +
+            '  void ["abcde", "abcde", a, a, a];' +
+            '}());'
+      }
+    ].forEach(cAssert);
+  }());
+}
+
+/* Local Variables:      */
+/* mode: js              */
+/* coding: utf-8         */
+/* indent-tabs-mode: nil */
+/* tab-width: 2          */
+/* End:                  */
+/* vim: set ft=javascript fenc=utf-8 et ts=2 sts=2 sw=2: */
+/* :mode=javascript:noTabs=true:tabSize=2:indentSize=2:deepIndent=true: */
+

+ 75 - 0
utils/node_modules/uglify-js/lib/object-ast.js

@@ -0,0 +1,75 @@
+var jsp = require("./parse-js"),
+    pro = require("./process");
+
+var BY_TYPE = {};
+
+function HOP(obj, prop) {
+        return Object.prototype.hasOwnProperty.call(obj, prop);
+};
+
+function AST_Node(parent) {
+        this.parent = parent;
+};
+
+AST_Node.prototype.init = function(){};
+
+function DEFINE_NODE_CLASS(type, props, methods) {
+        var base = methods && methods.BASE || AST_Node;
+        if (!base) base = AST_Node;
+        function D(parent, data) {
+                base.apply(this, arguments);
+                if (props) props.forEach(function(name, i){
+                        this["_" + name] = data[i];
+                });
+                this.init();
+        };
+        var P = D.prototype = new AST_Node;
+        P.node_type = function(){ return type };
+        if (props) props.forEach(function(name){
+                var propname = "_" + name;
+                P["set_" + name] = function(val) {
+                        this[propname] = val;
+                        return this;
+                };
+                P["get_" + name] = function() {
+                        return this[propname];
+                };
+        });
+        if (type != null) BY_TYPE[type] = D;
+        if (methods) for (var i in methods) if (HOP(methods, i)) {
+                P[i] = methods[i];
+        }
+        return D;
+};
+
+var AST_String_Node = DEFINE_NODE_CLASS("string", ["value"]);
+var AST_Number_Node = DEFINE_NODE_CLASS("num", ["value"]);
+var AST_Name_Node = DEFINE_NODE_CLASS("name", ["value"]);
+
+var AST_Statlist_Node = DEFINE_NODE_CLASS(null, ["body"]);
+var AST_Root_Node = DEFINE_NODE_CLASS("toplevel", null, { BASE: AST_Statlist_Node });
+var AST_Block_Node = DEFINE_NODE_CLASS("block", null, { BASE: AST_Statlist_Node });
+var AST_Splice_Node = DEFINE_NODE_CLASS("splice", null, { BASE: AST_Statlist_Node });
+
+var AST_Var_Node = DEFINE_NODE_CLASS("var", ["definitions"]);
+var AST_Const_Node = DEFINE_NODE_CLASS("const", ["definitions"]);
+
+var AST_Try_Node = DEFINE_NODE_CLASS("try", ["body", "catch", "finally"]);
+var AST_Throw_Node = DEFINE_NODE_CLASS("throw", ["exception"]);
+
+var AST_New_Node = DEFINE_NODE_CLASS("new", ["constructor", "arguments"]);
+
+var AST_Switch_Node = DEFINE_NODE_CLASS("switch", ["expression", "branches"]);
+var AST_Switch_Branch_Node = DEFINE_NODE_CLASS(null, ["expression", "body"]);
+
+var AST_Break_Node = DEFINE_NODE_CLASS("break", ["label"]);
+var AST_Continue_Node = DEFINE_NODE_CLASS("continue", ["label"]);
+var AST_Assign_Node = DEFINE_NODE_CLASS("assign", ["operator", "lvalue", "rvalue"]);
+var AST_Dot_Node = DEFINE_NODE_CLASS("dot", ["expression", "name"]);
+var AST_Call_Node = DEFINE_NODE_CLASS("call", ["function", "arguments"]);
+
+var AST_Lambda_Node = DEFINE_NODE_CLASS(null, ["name", "arguments", "body"])
+var AST_Function_Node = DEFINE_NODE_CLASS("function", null, AST_Lambda_Node);
+var AST_Defun_Node = DEFINE_NODE_CLASS("defun", null, AST_Lambda_Node);
+
+var AST_If_Node = DEFINE_NODE_CLASS("if", ["condition", "then", "else"]);

文件差异内容过多而无法显示
+ 203 - 0
utils/node_modules/uglify-js/lib/parse-js.js


+ 2011 - 0
utils/node_modules/uglify-js/lib/process.js

@@ -0,0 +1,2011 @@
+/***********************************************************************
+
+  A JavaScript tokenizer / parser / beautifier / compressor.
+
+  This version is suitable for Node.js.  With minimal changes (the
+  exports stuff) it should work on any JS platform.
+
+  This file implements some AST processors.  They work on data built
+  by parse-js.
+
+  Exported functions:
+
+    - ast_mangle(ast, options) -- mangles the variable/function names
+      in the AST.  Returns an AST.
+
+    - ast_squeeze(ast) -- employs various optimizations to make the
+      final generated code even smaller.  Returns an AST.
+
+    - gen_code(ast, options) -- generates JS code from the AST.  Pass
+      true (or an object, see the code for some options) as second
+      argument to get "pretty" (indented) code.
+
+  -------------------------------- (C) ---------------------------------
+
+                           Author: Mihai Bazon
+                         <[email protected]>
+                       http://mihai.bazon.net/blog
+
+  Distributed under the BSD license:
+
+    Copyright 2010 (c) Mihai Bazon <[email protected]>
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions
+    are met:
+
+        * Redistributions of source code must retain the above
+          copyright notice, this list of conditions and the following
+          disclaimer.
+
+        * Redistributions in binary form must reproduce the above
+          copyright notice, this list of conditions and the following
+          disclaimer in the documentation and/or other materials
+          provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+    SUCH DAMAGE.
+
+ ***********************************************************************/
+
+var jsp = require("./parse-js"),
+    slice = jsp.slice,
+    member = jsp.member,
+    PRECEDENCE = jsp.PRECEDENCE,
+    OPERATORS = jsp.OPERATORS;
+
+/* -----[ helper for AST traversal ]----- */
+
+function ast_walker() {
+        function _vardefs(defs) {
+                return [ this[0], MAP(defs, function(def){
+                        var a = [ def[0] ];
+                        if (def.length > 1)
+                                a[1] = walk(def[1]);
+                        return a;
+                }) ];
+        };
+        function _block(statements) {
+                var out = [ this[0] ];
+                if (statements != null)
+                        out.push(MAP(statements, walk));
+                return out;
+        };
+        var walkers = {
+                "string": function(str) {
+                        return [ this[0], str ];
+                },
+                "num": function(num) {
+                        return [ this[0], num ];
+                },
+                "name": function(name) {
+                        return [ this[0], name ];
+                },
+                "toplevel": function(statements) {
+                        return [ this[0], MAP(statements, walk) ];
+                },
+                "block": _block,
+                "splice": _block,
+                "var": _vardefs,
+                "const": _vardefs,
+                "try": function(t, c, f) {
+                        return [
+                                this[0],
+                                MAP(t, walk),
+                                c != null ? [ c[0], MAP(c[1], walk) ] : null,
+                                f != null ? MAP(f, walk) : null
+                        ];
+                },
+                "throw": function(expr) {
+                        return [ this[0], walk(expr) ];
+                },
+                "new": function(ctor, args) {
+                        return [ this[0], walk(ctor), MAP(args, walk) ];
+                },
+                "switch": function(expr, body) {
+                        return [ this[0], walk(expr), MAP(body, function(branch){
+                                return [ branch[0] ? walk(branch[0]) : null,
+                                         MAP(branch[1], walk) ];
+                        }) ];
+                },
+                "break": function(label) {
+                        return [ this[0], label ];
+                },
+                "continue": function(label) {
+                        return [ this[0], label ];
+                },
+                "conditional": function(cond, t, e) {
+                        return [ this[0], walk(cond), walk(t), walk(e) ];
+                },
+                "assign": function(op, lvalue, rvalue) {
+                        return [ this[0], op, walk(lvalue), walk(rvalue) ];
+                },
+                "dot": function(expr) {
+                        return [ this[0], walk(expr) ].concat(slice(arguments, 1));
+                },
+                "call": function(expr, args) {
+                        return [ this[0], walk(expr), MAP(args, walk) ];
+                },
+                "function": function(name, args, body) {
+                        return [ this[0], name, args.slice(), MAP(body, walk) ];
+                },
+                "debugger": function() {
+                        return [ this[0] ];
+                },
+                "defun": function(name, args, body) {
+                        return [ this[0], name, args.slice(), MAP(body, walk) ];
+                },
+                "if": function(conditional, t, e) {
+                        return [ this[0], walk(conditional), walk(t), walk(e) ];
+                },
+                "for": function(init, cond, step, block) {
+                        return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
+                },
+                "for-in": function(vvar, key, hash, block) {
+                        return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
+                },
+                "while": function(cond, block) {
+                        return [ this[0], walk(cond), walk(block) ];
+                },
+                "do": function(cond, block) {
+                        return [ this[0], walk(cond), walk(block) ];
+                },
+                "return": function(expr) {
+                        return [ this[0], walk(expr) ];
+                },
+                "binary": function(op, left, right) {
+                        return [ this[0], op, walk(left), walk(right) ];
+                },
+                "unary-prefix": function(op, expr) {
+                        return [ this[0], op, walk(expr) ];
+                },
+                "unary-postfix": function(op, expr) {
+                        return [ this[0], op, walk(expr) ];
+                },
+                "sub": function(expr, subscript) {
+                        return [ this[0], walk(expr), walk(subscript) ];
+                },
+                "object": function(props) {
+                        return [ this[0], MAP(props, function(p){
+                                return p.length == 2
+                                        ? [ p[0], walk(p[1]) ]
+                                        : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
+                        }) ];
+                },
+                "regexp": function(rx, mods) {
+                        return [ this[0], rx, mods ];
+                },
+                "array": function(elements) {
+                        return [ this[0], MAP(elements, walk) ];
+                },
+                "stat": function(stat) {
+                        return [ this[0], walk(stat) ];
+                },
+                "seq": function() {
+                        return [ this[0] ].concat(MAP(slice(arguments), walk));
+                },
+                "label": function(name, block) {
+                        return [ this[0], name, walk(block) ];
+                },
+                "with": function(expr, block) {
+                        return [ this[0], walk(expr), walk(block) ];
+                },
+                "atom": function(name) {
+                        return [ this[0], name ];
+                }
+        };
+
+        var user = {};
+        var stack = [];
+        function walk(ast) {
+                if (ast == null)
+                        return null;
+                try {
+                        stack.push(ast);
+                        var type = ast[0];
+                        var gen = user[type];
+                        if (gen) {
+                                var ret = gen.apply(ast, ast.slice(1));
+                                if (ret != null)
+                                        return ret;
+                        }
+                        gen = walkers[type];
+                        return gen.apply(ast, ast.slice(1));
+                } finally {
+                        stack.pop();
+                }
+        };
+
+        function dive(ast) {
+                if (ast == null)
+                        return null;
+                try {
+                        stack.push(ast);
+                        return walkers[ast[0]].apply(ast, ast.slice(1));
+                } finally {
+                        stack.pop();
+                }
+        };
+
+        function with_walkers(walkers, cont){
+                var save = {}, i;
+                for (i in walkers) if (HOP(walkers, i)) {
+                        save[i] = user[i];
+                        user[i] = walkers[i];
+                }
+                var ret = cont();
+                for (i in save) if (HOP(save, i)) {
+                        if (!save[i]) delete user[i];
+                        else user[i] = save[i];
+                }
+                return ret;
+        };
+
+        return {
+                walk: walk,
+                dive: dive,
+                with_walkers: with_walkers,
+                parent: function() {
+                        return stack[stack.length - 2]; // last one is current node
+                },
+                stack: function() {
+                        return stack;
+                }
+        };
+};
+
+/* -----[ Scope and mangling ]----- */
+
+function Scope(parent) {
+        this.names = {};        // names defined in this scope
+        this.mangled = {};      // mangled names (orig.name => mangled)
+        this.rev_mangled = {};  // reverse lookup (mangled => orig.name)
+        this.cname = -1;        // current mangled name
+        this.refs = {};         // names referenced from this scope
+        this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes
+        this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes
+        this.parent = parent;   // parent scope
+        this.children = [];     // sub-scopes
+        if (parent) {
+                this.level = parent.level + 1;
+                parent.children.push(this);
+        } else {
+                this.level = 0;
+        }
+};
+
+var base54 = (function(){
+        var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
+        return function(num) {
+                var ret = "", base = 54;
+                do {
+                        ret += DIGITS.charAt(num % base);
+                        num = Math.floor(num / base);
+                        base = 64;
+                } while (num > 0);
+                return ret;
+        };
+})();
+
+Scope.prototype = {
+        has: function(name) {
+                for (var s = this; s; s = s.parent)
+                        if (HOP(s.names, name))
+                                return s;
+        },
+        has_mangled: function(mname) {
+                for (var s = this; s; s = s.parent)
+                        if (HOP(s.rev_mangled, mname))
+                                return s;
+        },
+        toJSON: function() {
+                return {
+                        names: this.names,
+                        uses_eval: this.uses_eval,
+                        uses_with: this.uses_with
+                };
+        },
+
+        next_mangled: function() {
+                // we must be careful that the new mangled name:
+                //
+                // 1. doesn't shadow a mangled name from a parent
+                //    scope, unless we don't reference the original
+                //    name from this scope OR from any sub-scopes!
+                //    This will get slow.
+                //
+                // 2. doesn't shadow an original name from a parent
+                //    scope, in the event that the name is not mangled
+                //    in the parent scope and we reference that name
+                //    here OR IN ANY SUBSCOPES!
+                //
+                // 3. doesn't shadow a name that is referenced but not
+                //    defined (possibly global defined elsewhere).
+                for (;;) {
+                        var m = base54(++this.cname), prior;
+
+                        // case 1.
+                        prior = this.has_mangled(m);
+                        if (prior && this.refs[prior.rev_mangled[m]] === prior)
+                                continue;
+
+                        // case 2.
+                        prior = this.has(m);
+                        if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m))
+                                continue;
+
+                        // case 3.
+                        if (HOP(this.refs, m) && this.refs[m] == null)
+                                continue;
+
+                        // I got "do" once. :-/
+                        if (!is_identifier(m))
+                                continue;
+
+                        return m;
+                }
+        },
+        set_mangle: function(name, m) {
+                this.rev_mangled[m] = name;
+                return this.mangled[name] = m;
+        },
+        get_mangled: function(name, newMangle) {
+                if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use
+                var s = this.has(name);
+                if (!s) return name; // not in visible scope, no mangle
+                if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope
+                if (!newMangle) return name;                      // not found and no mangling requested
+                return s.set_mangle(name, s.next_mangled());
+        },
+        references: function(name) {
+                return name && !this.parent || this.uses_with || this.uses_eval || this.refs[name];
+        },
+        define: function(name, type) {
+                if (name != null) {
+                        if (type == "var" || !HOP(this.names, name))
+                                this.names[name] = type || "var";
+                        return name;
+                }
+        }
+};
+
+function ast_add_scope(ast) {
+
+        var current_scope = null;
+        var w = ast_walker(), walk = w.walk;
+        var having_eval = [];
+
+        function with_new_scope(cont) {
+                current_scope = new Scope(current_scope);
+                current_scope.labels = new Scope();
+                var ret = current_scope.body = cont();
+                ret.scope = current_scope;
+                current_scope = current_scope.parent;
+                return ret;
+        };
+
+        function define(name, type) {
+                return current_scope.define(name, type);
+        };
+
+        function reference(name) {
+                current_scope.refs[name] = true;
+        };
+
+        function _lambda(name, args, body) {
+                var is_defun = this[0] == "defun";
+                return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){
+                        if (!is_defun) define(name, "lambda");
+                        MAP(args, function(name){ define(name, "arg") });
+                        return MAP(body, walk);
+                })];
+        };
+
+        function _vardefs(type) {
+                return function(defs) {
+                        MAP(defs, function(d){
+                                define(d[0], type);
+                                if (d[1]) reference(d[0]);
+                        });
+                };
+        };
+
+        function _breacont(label) {
+                if (label)
+                        current_scope.labels.refs[label] = true;
+        };
+
+        return with_new_scope(function(){
+                // process AST
+                var ret = w.with_walkers({
+                        "function": _lambda,
+                        "defun": _lambda,
+                        "label": function(name, stat) { current_scope.labels.define(name) },
+                        "break": _breacont,
+                        "continue": _breacont,
+                        "with": function(expr, block) {
+                                for (var s = current_scope; s; s = s.parent)
+                                        s.uses_with = true;
+                        },
+                        "var": _vardefs("var"),
+                        "const": _vardefs("const"),
+                        "try": function(t, c, f) {
+                                if (c != null) return [
+                                        this[0],
+                                        MAP(t, walk),
+                                        [ define(c[0], "catch"), MAP(c[1], walk) ],
+                                        f != null ? MAP(f, walk) : null
+                                ];
+                        },
+                        "name": function(name) {
+                                if (name == "eval")
+                                        having_eval.push(current_scope);
+                                reference(name);
+                        }
+                }, function(){
+                        return walk(ast);
+                });
+
+                // the reason why we need an additional pass here is
+                // that names can be used prior to their definition.
+
+                // scopes where eval was detected and their parents
+                // are marked with uses_eval, unless they define the
+                // "eval" name.
+                MAP(having_eval, function(scope){
+                        if (!scope.has("eval")) while (scope) {
+                                scope.uses_eval = true;
+                                scope = scope.parent;
+                        }
+                });
+
+                // for referenced names it might be useful to know
+                // their origin scope.  current_scope here is the
+                // toplevel one.
+                function fixrefs(scope, i) {
+                        // do children first; order shouldn't matter
+                        for (i = scope.children.length; --i >= 0;)
+                                fixrefs(scope.children[i]);
+                        for (i in scope.refs) if (HOP(scope.refs, i)) {
+                                // find origin scope and propagate the reference to origin
+                                for (var origin = scope.has(i), s = scope; s; s = s.parent) {
+                                        s.refs[i] = origin;
+                                        if (s === origin) break;
+                                }
+                        }
+                };
+                fixrefs(current_scope);
+
+                return ret;
+        });
+
+};
+
+/* -----[ mangle names ]----- */
+
+function ast_mangle(ast, options) {
+        var w = ast_walker(), walk = w.walk, scope;
+        options = options || {};
+
+        function get_mangled(name, newMangle) {
+                if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel
+                if (options.except && member(name, options.except))
+                        return name;
+                return scope.get_mangled(name, newMangle);
+        };
+
+        function get_define(name) {
+                if (options.defines) {
+                        // we always lookup a defined symbol for the current scope FIRST, so declared
+                        // vars trump a DEFINE symbol, but if no such var is found, then match a DEFINE value
+                        if (!scope.has(name)) {
+                                if (HOP(options.defines, name)) {
+                                        return options.defines[name];
+                                }
+                        }
+                        return null;
+                }
+        };
+
+        function _lambda(name, args, body) {
+                if (!options.no_functions) {
+                        var is_defun = this[0] == "defun", extra;
+                        if (name) {
+                                if (is_defun) name = get_mangled(name);
+                                else if (body.scope.references(name)) {
+                                        extra = {};
+                                        if (!(scope.uses_eval || scope.uses_with))
+                                                name = extra[name] = scope.next_mangled();
+                                        else
+                                                extra[name] = name;
+                                }
+                                else name = null;
+                        }
+                }
+                body = with_scope(body.scope, function(){
+                        args = MAP(args, function(name){ return get_mangled(name) });
+                        return MAP(body, walk);
+                }, extra);
+                return [ this[0], name, args, body ];
+        };
+
+        function with_scope(s, cont, extra) {
+                var _scope = scope;
+                scope = s;
+                if (extra) for (var i in extra) if (HOP(extra, i)) {
+                        s.set_mangle(i, extra[i]);
+                }
+                for (var i in s.names) if (HOP(s.names, i)) {
+                        get_mangled(i, true);
+                }
+                var ret = cont();
+                ret.scope = s;
+                scope = _scope;
+                return ret;
+        };
+
+        function _vardefs(defs) {
+                return [ this[0], MAP(defs, function(d){
+                        return [ get_mangled(d[0]), walk(d[1]) ];
+                }) ];
+        };
+
+        function _breacont(label) {
+                if (label) return [ this[0], scope.labels.get_mangled(label) ];
+        };
+
+        return w.with_walkers({
+                "function": _lambda,
+                "defun": function() {
+                        // move function declarations to the top when
+                        // they are not in some block.
+                        var ast = _lambda.apply(this, arguments);
+                        switch (w.parent()[0]) {
+                            case "toplevel":
+                            case "function":
+                            case "defun":
+                                return MAP.at_top(ast);
+                        }
+                        return ast;
+                },
+                "label": function(label, stat) {
+                        if (scope.labels.refs[label]) return [
+                                this[0],
+                                scope.labels.get_mangled(label, true),
+                                walk(stat)
+                        ];
+                        return walk(stat);
+                },
+                "break": _breacont,
+                "continue": _breacont,
+                "var": _vardefs,
+                "const": _vardefs,
+                "name": function(name) {
+                        return get_define(name) || [ this[0], get_mangled(name) ];
+                },
+                "try": function(t, c, f) {
+                        return [ this[0],
+                                 MAP(t, walk),
+                                 c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
+                                 f != null ? MAP(f, walk) : null ];
+                },
+                "toplevel": function(body) {
+                        var self = this;
+                        return with_scope(self.scope, function(){
+                                return [ self[0], MAP(body, walk) ];
+                        });
+                }
+        }, function() {
+                return walk(ast_add_scope(ast));
+        });
+};
+
+/* -----[
+   - compress foo["bar"] into foo.bar,
+   - remove block brackets {} where possible
+   - join consecutive var declarations
+   - various optimizations for IFs:
+     - if (cond) foo(); else bar();  ==>  cond?foo():bar();
+     - if (cond) foo();  ==>  cond&&foo();
+     - if (foo) return bar(); else return baz();  ==> return foo?bar():baz(); // also for throw
+     - if (foo) return bar(); else something();  ==> {if(foo)return bar();something()}
+   ]----- */
+
+var warn = function(){};
+
+function best_of(ast1, ast2) {
+        return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
+};
+
+function last_stat(b) {
+        if (b[0] == "block" && b[1] && b[1].length > 0)
+                return b[1][b[1].length - 1];
+        return b;
+}
+
+function aborts(t) {
+        if (t) switch (last_stat(t)[0]) {
+            case "return":
+            case "break":
+            case "continue":
+            case "throw":
+                return true;
+        }
+};
+
+function boolean_expr(expr) {
+        return ( (expr[0] == "unary-prefix"
+                  && member(expr[1], [ "!", "delete" ])) ||
+
+                 (expr[0] == "binary"
+                  && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
+
+                 (expr[0] == "binary"
+                  && member(expr[1], [ "&&", "||" ])
+                  && boolean_expr(expr[2])
+                  && boolean_expr(expr[3])) ||
+
+                 (expr[0] == "conditional"
+                  && boolean_expr(expr[2])
+                  && boolean_expr(expr[3])) ||
+
+                 (expr[0] == "assign"
+                  && expr[1] === true
+                  && boolean_expr(expr[3])) ||
+
+                 (expr[0] == "seq"
+                  && boolean_expr(expr[expr.length - 1]))
+               );
+};
+
+function empty(b) {
+        return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
+};
+
+function is_string(node) {
+        return (node[0] == "string" ||
+                node[0] == "unary-prefix" && node[1] == "typeof" ||
+                node[0] == "binary" && node[1] == "+" &&
+                (is_string(node[2]) || is_string(node[3])));
+};
+
+var when_constant = (function(){
+
+        var $NOT_CONSTANT = {};
+
+        // this can only evaluate constant expressions.  If it finds anything
+        // not constant, it throws $NOT_CONSTANT.
+        function evaluate(expr) {
+                switch (expr[0]) {
+                    case "string":
+                    case "num":
+                        return expr[1];
+                    case "name":
+                    case "atom":
+                        switch (expr[1]) {
+                            case "true": return true;
+                            case "false": return false;
+                            case "null": return null;
+                        }
+                        break;
+                    case "unary-prefix":
+                        switch (expr[1]) {
+                            case "!": return !evaluate(expr[2]);
+                            case "typeof": return typeof evaluate(expr[2]);
+                            case "~": return ~evaluate(expr[2]);
+                            case "-": return -evaluate(expr[2]);
+                            case "+": return +evaluate(expr[2]);
+                        }
+                        break;
+                    case "binary":
+                        var left = expr[2], right = expr[3];
+                        switch (expr[1]) {
+                            case "&&"         : return evaluate(left) &&         evaluate(right);
+                            case "||"         : return evaluate(left) ||         evaluate(right);
+                            case "|"          : return evaluate(left) |          evaluate(right);
+                            case "&"          : return evaluate(left) &          evaluate(right);
+                            case "^"          : return evaluate(left) ^          evaluate(right);
+                            case "+"          : return evaluate(left) +          evaluate(right);
+                            case "*"          : return evaluate(left) *          evaluate(right);
+                            case "/"          : return evaluate(left) /          evaluate(right);
+                            case "%"          : return evaluate(left) %          evaluate(right);
+                            case "-"          : return evaluate(left) -          evaluate(right);
+                            case "<<"         : return evaluate(left) <<         evaluate(right);
+                            case ">>"         : return evaluate(left) >>         evaluate(right);
+                            case ">>>"        : return evaluate(left) >>>        evaluate(right);
+                            case "=="         : return evaluate(left) ==         evaluate(right);
+                            case "==="        : return evaluate(left) ===        evaluate(right);
+                            case "!="         : return evaluate(left) !=         evaluate(right);
+                            case "!=="        : return evaluate(left) !==        evaluate(right);
+                            case "<"          : return evaluate(left) <          evaluate(right);
+                            case "<="         : return evaluate(left) <=         evaluate(right);
+                            case ">"          : return evaluate(left) >          evaluate(right);
+                            case ">="         : return evaluate(left) >=         evaluate(right);
+                            case "in"         : return evaluate(left) in         evaluate(right);
+                            case "instanceof" : return evaluate(left) instanceof evaluate(right);
+                        }
+                }
+                throw $NOT_CONSTANT;
+        };
+
+        return function(expr, yes, no) {
+                try {
+                        var val = evaluate(expr), ast;
+                        switch (typeof val) {
+                            case "string": ast =  [ "string", val ]; break;
+                            case "number": ast =  [ "num", val ]; break;
+                            case "boolean": ast =  [ "name", String(val) ]; break;
+                            default:
+                                if (val === null) { ast = [ "atom", "null" ]; break; }
+                                throw new Error("Can't handle constant of type: " + (typeof val));
+                        }
+                        return yes.call(expr, ast, val);
+                } catch(ex) {
+                        if (ex === $NOT_CONSTANT) {
+                                if (expr[0] == "binary"
+                                    && (expr[1] == "===" || expr[1] == "!==")
+                                    && ((is_string(expr[2]) && is_string(expr[3]))
+                                        || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) {
+                                        expr[1] = expr[1].substr(0, 2);
+                                }
+                                else if (no && expr[0] == "binary"
+                                         && (expr[1] == "||" || expr[1] == "&&")) {
+                                    // the whole expression is not constant but the lval may be...
+                                    try {
+                                        var lval = evaluate(expr[2]);
+                                        expr = ((expr[1] == "&&" && (lval ? expr[3] : lval))    ||
+                                                (expr[1] == "||" && (lval ? lval    : expr[3])) ||
+                                                expr);
+                                    } catch(ex2) {
+                                        // IGNORE... lval is not constant
+                                    }
+                                }
+                                return no ? no.call(expr, expr) : null;
+                        }
+                        else throw ex;
+                }
+        };
+
+})();
+
+function warn_unreachable(ast) {
+        if (!empty(ast))
+                warn("Dropping unreachable code: " + gen_code(ast, true));
+};
+
+function prepare_ifs(ast) {
+        var w = ast_walker(), walk = w.walk;
+        // In this first pass, we rewrite ifs which abort with no else with an
+        // if-else.  For example:
+        //
+        // if (x) {
+        //     blah();
+        //     return y;
+        // }
+        // foobar();
+        //
+        // is rewritten into:
+        //
+        // if (x) {
+        //     blah();
+        //     return y;
+        // } else {
+        //     foobar();
+        // }
+        function redo_if(statements) {
+                statements = MAP(statements, walk);
+
+                for (var i = 0; i < statements.length; ++i) {
+                        var fi = statements[i];
+                        if (fi[0] != "if") continue;
+
+                        if (fi[3] && walk(fi[3])) continue;
+
+                        var t = walk(fi[2]);
+                        if (!aborts(t)) continue;
+
+                        var conditional = walk(fi[1]);
+
+                        var e_body = redo_if(statements.slice(i + 1));
+                        var e = e_body.length == 1 ? e_body[0] : [ "block", e_body ];
+
+                        return statements.slice(0, i).concat([ [
+                                fi[0],          // "if"
+                                conditional,    // conditional
+                                t,              // then
+                                e               // else
+                        ] ]);
+                }
+
+                return statements;
+        };
+
+        function redo_if_lambda(name, args, body) {
+                body = redo_if(body);
+                return [ this[0], name, args, body ];
+        };
+
+        function redo_if_block(statements) {
+                return [ this[0], statements != null ? redo_if(statements) : null ];
+        };
+
+        return w.with_walkers({
+                "defun": redo_if_lambda,
+                "function": redo_if_lambda,
+                "block": redo_if_block,
+                "splice": redo_if_block,
+                "toplevel": function(statements) {
+                        return [ this[0], redo_if(statements) ];
+                },
+                "try": function(t, c, f) {
+                        return [
+                                this[0],
+                                redo_if(t),
+                                c != null ? [ c[0], redo_if(c[1]) ] : null,
+                                f != null ? redo_if(f) : null
+                        ];
+                }
+        }, function() {
+                return walk(ast);
+        });
+};
+
+function for_side_effects(ast, handler) {
+        var w = ast_walker(), walk = w.walk;
+        var $stop = {}, $restart = {};
+        function stop() { throw $stop };
+        function restart() { throw $restart };
+        function found(){ return handler.call(this, this, w, stop, restart) };
+        function unary(op) {
+                if (op == "++" || op == "--")
+                        return found.apply(this, arguments);
+        };
+        return w.with_walkers({
+                "try": found,
+                "throw": found,
+                "return": found,
+                "new": found,
+                "switch": found,
+                "break": found,
+                "continue": found,
+                "assign": found,
+                "call": found,
+                "if": found,
+                "for": found,
+                "for-in": found,
+                "while": found,
+                "do": found,
+                "return": found,
+                "unary-prefix": unary,
+                "unary-postfix": unary,
+                "defun": found
+        }, function(){
+                while (true) try {
+                        walk(ast);
+                        break;
+                } catch(ex) {
+                        if (ex === $stop) break;
+                        if (ex === $restart) continue;
+                        throw ex;
+                }
+        });
+};
+
+function ast_lift_variables(ast) {
+        var w = ast_walker(), walk = w.walk, scope;
+        function do_body(body, env) {
+                var _scope = scope;
+                scope = env;
+                body = MAP(body, walk);
+                var hash = {}, names = MAP(env.names, function(type, name){
+                        if (type != "var") return MAP.skip;
+                        if (!env.references(name)) return MAP.skip;
+                        hash[name] = true;
+                        return [ name ];
+                });
+                if (names.length > 0) {
+                        // looking for assignments to any of these variables.
+                        // we can save considerable space by moving the definitions
+                        // in the var declaration.
+                        for_side_effects([ "block", body ], function(ast, walker, stop, restart) {
+                                if (ast[0] == "assign"
+                                    && ast[1] === true
+                                    && ast[2][0] == "name"
+                                    && HOP(hash, ast[2][1])) {
+                                        // insert the definition into the var declaration
+                                        for (var i = names.length; --i >= 0;) {
+                                                if (names[i][0] == ast[2][1]) {
+                                                        if (names[i][1]) // this name already defined, we must stop
+                                                                stop();
+                                                        names[i][1] = ast[3]; // definition
+                                                        names.push(names.splice(i, 1)[0]);
+                                                        break;
+                                                }
+                                        }
+                                        // remove this assignment from the AST.
+                                        var p = walker.parent();
+                                        if (p[0] == "seq") {
+                                                var a = p[2];
+                                                a.unshift(0, p.length);
+                                                p.splice.apply(p, a);
+                                        }
+                                        else if (p[0] == "stat") {
+                                                p.splice(0, p.length, "block"); // empty statement
+                                        }
+                                        else {
+                                                stop();
+                                        }
+                                        restart();
+                                }
+                                stop();
+                        });
+                        body.unshift([ "var", names ]);
+                }
+                scope = _scope;
+                return body;
+        };
+        function _vardefs(defs) {
+                var ret = null;
+                for (var i = defs.length; --i >= 0;) {
+                        var d = defs[i];
+                        if (!d[1]) continue;
+                        d = [ "assign", true, [ "name", d[0] ], d[1] ];
+                        if (ret == null) ret = d;
+                        else ret = [ "seq", d, ret ];
+                }
+                if (ret == null) {
+                        if (w.parent()[0] == "for-in")
+                                return [ "name", defs[0][0] ];
+                        return MAP.skip;
+                }
+                return [ "stat", ret ];
+        };
+        function _toplevel(body) {
+                return [ this[0], do_body(body, this.scope) ];
+        };
+        return w.with_walkers({
+                "function": function(name, args, body){
+                        for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
+                                args.pop();
+                        if (!body.scope.references(name)) name = null;
+                        return [ this[0], name, args, do_body(body, body.scope) ];
+                },
+                "defun": function(name, args, body){
+                        if (!scope.references(name)) return MAP.skip;
+                        for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
+                                args.pop();
+                        return [ this[0], name, args, do_body(body, body.scope) ];
+                },
+                "var": _vardefs,
+                "toplevel": _toplevel
+        }, function(){
+                return walk(ast_add_scope(ast));
+        });
+};
+
+function ast_squeeze(ast, options) {
+        options = defaults(options, {
+                make_seqs   : true,
+                dead_code   : true,
+                no_warnings : false,
+                keep_comps  : true
+        });
+
+        var w = ast_walker(), walk = w.walk;
+
+        function negate(c) {
+                var not_c = [ "unary-prefix", "!", c ];
+                switch (c[0]) {
+                    case "unary-prefix":
+                        return c[1] == "!" && boolean_expr(c[2]) ? c[2] : not_c;
+                    case "seq":
+                        c = slice(c);
+                        c[c.length - 1] = negate(c[c.length - 1]);
+                        return c;
+                    case "conditional":
+                        return best_of(not_c, [ "conditional", c[1], negate(c[2]), negate(c[3]) ]);
+                    case "binary":
+                        var op = c[1], left = c[2], right = c[3];
+                        if (!options.keep_comps) switch (op) {
+                            case "<="  : return [ "binary", ">", left, right ];
+                            case "<"   : return [ "binary", ">=", left, right ];
+                            case ">="  : return [ "binary", "<", left, right ];
+                            case ">"   : return [ "binary", "<=", left, right ];
+                        }
+                        switch (op) {
+                            case "=="  : return [ "binary", "!=", left, right ];
+                            case "!="  : return [ "binary", "==", left, right ];
+                            case "===" : return [ "binary", "!==", left, right ];
+                            case "!==" : return [ "binary", "===", left, right ];
+                            case "&&"  : return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
+                            case "||"  : return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
+                        }
+                        break;
+                }
+                return not_c;
+        };
+
+        function make_conditional(c, t, e) {
+                var make_real_conditional = function() {
+                        if (c[0] == "unary-prefix" && c[1] == "!") {
+                                return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
+                        } else {
+                                return e ? best_of(
+                                        [ "conditional", c, t, e ],
+                                        [ "conditional", negate(c), e, t ]
+                                ) : [ "binary", "&&", c, t ];
+                        }
+                };
+                // shortcut the conditional if the expression has a constant value
+                return when_constant(c, function(ast, val){
+                        warn_unreachable(val ? e : t);
+                        return          (val ? t : e);
+                }, make_real_conditional);
+        };
+
+        function rmblock(block) {
+                if (block != null && block[0] == "block" && block[1]) {
+                        if (block[1].length == 1)
+                                block = block[1][0];
+                        else if (block[1].length == 0)
+                                block = [ "block" ];
+                }
+                return block;
+        };
+
+        function _lambda(name, args, body) {
+                return [ this[0], name, args, tighten(body, "lambda") ];
+        };
+
+        // this function does a few things:
+        // 1. discard useless blocks
+        // 2. join consecutive var declarations
+        // 3. remove obviously dead code
+        // 4. transform consecutive statements using the comma operator
+        // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... }
+        function tighten(statements, block_type) {
+                statements = MAP(statements, walk);
+
+                statements = statements.reduce(function(a, stat){
+                        if (stat[0] == "block") {
+                                if (stat[1]) {
+                                        a.push.apply(a, stat[1]);
+                                }
+                        } else {
+                                a.push(stat);
+                        }
+                        return a;
+                }, []);
+
+                statements = (function(a, prev){
+                        statements.forEach(function(cur){
+                                if (prev && ((cur[0] == "var" && prev[0] == "var") ||
+                                             (cur[0] == "const" && prev[0] == "const"))) {
+                                        prev[1] = prev[1].concat(cur[1]);
+                                } else {
+                                        a.push(cur);
+                                        prev = cur;
+                                }
+                        });
+                        return a;
+                })([]);
+
+                if (options.dead_code) statements = (function(a, has_quit){
+                        statements.forEach(function(st){
+                                if (has_quit) {
+                                        if (st[0] == "function" || st[0] == "defun") {
+                                                a.push(st);
+                                        }
+                                        else if (st[0] == "var" || st[0] == "const") {
+                                                if (!options.no_warnings)
+                                                        warn("Variables declared in unreachable code");
+                                                st[1] = MAP(st[1], function(def){
+                                                        if (def[1] && !options.no_warnings)
+                                                                warn_unreachable([ "assign", true, [ "name", def[0] ], def[1] ]);
+                                                        return [ def[0] ];
+                                                });
+                                                a.push(st);
+                                        }
+                                        else if (!options.no_warnings)
+                                                warn_unreachable(st);
+                                }
+                                else {
+                                        a.push(st);
+                                        if (member(st[0], [ "return", "throw", "break", "continue" ]))
+                                                has_quit = true;
+                                }
+                        });
+                        return a;
+                })([]);
+
+                if (options.make_seqs) statements = (function(a, prev) {
+                        statements.forEach(function(cur){
+                                if (prev && prev[0] == "stat" && cur[0] == "stat") {
+                                        prev[1] = [ "seq", prev[1], cur[1] ];
+                                } else {
+                                        a.push(cur);
+                                        prev = cur;
+                                }
+                        });
+                        if (a.length >= 2
+                            && a[a.length-2][0] == "stat"
+                            && (a[a.length-1][0] == "return" || a[a.length-1][0] == "throw")
+                            && a[a.length-1][1])
+                        {
+                                a.splice(a.length - 2, 2,
+                                         [ a[a.length-1][0],
+                                           [ "seq", a[a.length-2][1], a[a.length-1][1] ]]);
+                        }
+                        return a;
+                })([]);
+
+                // this increases jQuery by 1K.  Probably not such a good idea after all..
+                // part of this is done in prepare_ifs anyway.
+                // if (block_type == "lambda") statements = (function(i, a, stat){
+                //         while (i < statements.length) {
+                //                 stat = statements[i++];
+                //                 if (stat[0] == "if" && !stat[3]) {
+                //                         if (stat[2][0] == "return" && stat[2][1] == null) {
+                //                                 a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
+                //                                 break;
+                //                         }
+                //                         var last = last_stat(stat[2]);
+                //                         if (last[0] == "return" && last[1] == null) {
+                //                                 a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
+                //                                 break;
+                //                         }
+                //                 }
+                //                 a.push(stat);
+                //         }
+                //         return a;
+                // })(0, []);
+
+                return statements;
+        };
+
+        function make_if(c, t, e) {
+                return when_constant(c, function(ast, val){
+                        if (val) {
+                                t = walk(t);
+                                warn_unreachable(e);
+                                return t || [ "block" ];
+                        } else {
+                                e = walk(e);
+                                warn_unreachable(t);
+                                return e || [ "block" ];
+                        }
+                }, function() {
+                        return make_real_if(c, t, e);
+                });
+        };
+
+        function abort_else(c, t, e) {
+                var ret = [ [ "if", negate(c), e ] ];
+                if (t[0] == "block") {
+                        if (t[1]) ret = ret.concat(t[1]);
+                } else {
+                        ret.push(t);
+                }
+                return walk([ "block", ret ]);
+        };
+
+        function make_real_if(c, t, e) {
+                c = walk(c);
+                t = walk(t);
+                e = walk(e);
+
+                if (empty(t)) {
+                        c = negate(c);
+                        t = e;
+                        e = null;
+                } else if (empty(e)) {
+                        e = null;
+                } else {
+                        // if we have both else and then, maybe it makes sense to switch them?
+                        (function(){
+                                var a = gen_code(c);
+                                var n = negate(c);
+                                var b = gen_code(n);
+                                if (b.length < a.length) {
+                                        var tmp = t;
+                                        t = e;
+                                        e = tmp;
+                                        c = n;
+                                }
+                        })();
+                }
+                if (empty(e) && empty(t))
+                        return [ "stat", c ];
+                var ret = [ "if", c, t, e ];
+                if (t[0] == "if" && empty(t[3]) && empty(e)) {
+                        ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ]));
+                }
+                else if (t[0] == "stat") {
+                        if (e) {
+                                if (e[0] == "stat")
+                                        ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]);
+                                else if (aborts(e))
+                                        ret = abort_else(c, t, e);
+                        }
+                        else {
+                                ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]);
+                        }
+                }
+                else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw") && t[1] && e[1]) {
+                        ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]);
+                }
+                else if (e && aborts(t)) {
+                        ret = [ [ "if", c, t ] ];
+                        if (e[0] == "block") {
+                                if (e[1]) ret = ret.concat(e[1]);
+                        }
+                        else {
+                                ret.push(e);
+                        }
+                        ret = walk([ "block", ret ]);
+                }
+                else if (t && aborts(e)) {
+                        ret = abort_else(c, t, e);
+                }
+                return ret;
+        };
+
+        function _do_while(cond, body) {
+                return when_constant(cond, function(cond, val){
+                        if (!val) {
+                                warn_unreachable(body);
+                                return [ "block" ];
+                        } else {
+                                return [ "for", null, null, null, walk(body) ];
+                        }
+                });
+        };
+
+        return w.with_walkers({
+                "sub": function(expr, subscript) {
+                        if (subscript[0] == "string") {
+                                var name = subscript[1];
+                                if (is_identifier(name))
+                                        return [ "dot", walk(expr), name ];
+                                else if (/^[1-9][0-9]*$/.test(name) || name === "0")
+                                        return [ "sub", walk(expr), [ "num", parseInt(name, 10) ] ];
+                        }
+                },
+                "if": make_if,
+                "toplevel": function(body) {
+                        return [ "toplevel", tighten(body) ];
+                },
+                "switch": function(expr, body) {
+                        var last = body.length - 1;
+                        return [ "switch", walk(expr), MAP(body, function(branch, i){
+                                var block = tighten(branch[1]);
+                                if (i == last && block.length > 0) {
+                                        var node = block[block.length - 1];
+                                        if (node[0] == "break" && !node[1])
+                                                block.pop();
+                                }
+                                return [ branch[0] ? walk(branch[0]) : null, block ];
+                        }) ];
+                },
+                "function": _lambda,
+                "defun": _lambda,
+                "block": function(body) {
+                        if (body) return rmblock([ "block", tighten(body) ]);
+                },
+                "binary": function(op, left, right) {
+                        return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
+                                return best_of(walk(c), this);
+                        }, function no() {
+                                return function(){
+                                        if(op != "==" && op != "!=") return;
+                                        var l = walk(left), r = walk(right);
+                                        if(l && l[0] == "unary-prefix" && l[1] == "!" && l[2][0] == "num")
+                                                left = ['num', +!l[2][1]];
+                                        else if (r && r[0] == "unary-prefix" && r[1] == "!" && r[2][0] == "num")
+                                                right = ['num', +!r[2][1]];
+                                        return ["binary", op, left, right];
+                                }() || this;
+                        });
+                },
+                "conditional": function(c, t, e) {
+                        return make_conditional(walk(c), walk(t), walk(e));
+                },
+                "try": function(t, c, f) {
+                        return [
+                                "try",
+                                tighten(t),
+                                c != null ? [ c[0], tighten(c[1]) ] : null,
+                                f != null ? tighten(f) : null
+                        ];
+                },
+                "unary-prefix": function(op, expr) {
+                        expr = walk(expr);
+                        var ret = [ "unary-prefix", op, expr ];
+                        if (op == "!")
+                                ret = best_of(ret, negate(expr));
+                        return when_constant(ret, function(ast, val){
+                                return walk(ast); // it's either true or false, so minifies to !0 or !1
+                        }, function() { return ret });
+                },
+                "name": function(name) {
+                        switch (name) {
+                            case "true": return [ "unary-prefix", "!", [ "num", 0 ]];
+                            case "false": return [ "unary-prefix", "!", [ "num", 1 ]];
+                        }
+                },
+                "while": _do_while,
+                "assign": function(op, lvalue, rvalue) {
+                        lvalue = walk(lvalue);
+                        rvalue = walk(rvalue);
+                        var okOps = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
+                        if (op === true && lvalue[0] === "name" && rvalue[0] === "binary" &&
+                            ~okOps.indexOf(rvalue[1]) && rvalue[2][0] === "name" &&
+                            rvalue[2][1] === lvalue[1]) {
+                                return [ this[0], rvalue[1], lvalue, rvalue[3] ]
+                        }
+                        return [ this[0], op, lvalue, rvalue ];
+                }
+        }, function() {
+                for (var i = 0; i < 2; ++i) {
+                        ast = prepare_ifs(ast);
+                        ast = walk(ast);
+                }
+                return ast;
+        });
+};
+
+/* -----[ re-generate code from the AST ]----- */
+
+var DOT_CALL_NO_PARENS = jsp.array_to_hash([
+        "name",
+        "array",
+        "object",
+        "string",
+        "dot",
+        "sub",
+        "call",
+        "regexp",
+        "defun"
+]);
+
+function make_string(str, ascii_only) {
+        var dq = 0, sq = 0;
+        str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
+                switch (s) {
+                    case "\\": return "\\\\";
+                    case "\b": return "\\b";
+                    case "\f": return "\\f";
+                    case "\n": return "\\n";
+                    case "\r": return "\\r";
+                    case "\u2028": return "\\u2028";
+                    case "\u2029": return "\\u2029";
+                    case '"': ++dq; return '"';
+                    case "'": ++sq; return "'";
+                    case "\0": return "\\0";
+                }
+                return s;
+        });
+        if (ascii_only) str = to_ascii(str);
+        if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
+        else return '"' + str.replace(/\x22/g, '\\"') + '"';
+};
+
+function to_ascii(str) {
+        return str.replace(/[\u0080-\uffff]/g, function(ch) {
+                var code = ch.charCodeAt(0).toString(16);
+                while (code.length < 4) code = "0" + code;
+                return "\\u" + code;
+        });
+};
+
+var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]);
+
+function gen_code(ast, options) {
+        options = defaults(options, {
+                indent_start : 0,
+                indent_level : 4,
+                quote_keys   : false,
+                space_colon  : false,
+                beautify     : false,
+                ascii_only   : false,
+                inline_script: false
+        });
+        var beautify = !!options.beautify;
+        var indentation = 0,
+            newline = beautify ? "\n" : "",
+            space = beautify ? " " : "";
+
+        function encode_string(str) {
+                var ret = make_string(str, options.ascii_only);
+                if (options.inline_script)
+                        ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
+                return ret;
+        };
+
+        function make_name(name) {
+                name = name.toString();
+                if (options.ascii_only)
+                        name = to_ascii(name);
+                return name;
+        };
+
+        function indent(line) {
+                if (line == null)
+                        line = "";
+                if (beautify)
+                        line = repeat_string(" ", options.indent_start + indentation * options.indent_level) + line;
+                return line;
+        };
+
+        function with_indent(cont, incr) {
+                if (incr == null) incr = 1;
+                indentation += incr;
+                try { return cont.apply(null, slice(arguments, 1)); }
+                finally { indentation -= incr; }
+        };
+
+        function add_spaces(a) {
+                if (beautify)
+                        return a.join(" ");
+                var b = [];
+                for (var i = 0; i < a.length; ++i) {
+                        var next = a[i + 1];
+                        b.push(a[i]);
+                        if (next &&
+                            ((/[a-z0-9_\x24]$/i.test(a[i].toString()) && /^[a-z0-9_\x24]/i.test(next.toString())) ||
+                             (/[\+\-]$/.test(a[i].toString()) && /^[\+\-]/.test(next.toString())))) {
+                                b.push(" ");
+                        }
+                }
+                return b.join("");
+        };
+
+        function add_commas(a) {
+                return a.join("," + space);
+        };
+
+        function parenthesize(expr) {
+                var gen = make(expr);
+                for (var i = 1; i < arguments.length; ++i) {
+                        var el = arguments[i];
+                        if ((el instanceof Function && el(expr)) || expr[0] == el)
+                                return "(" + gen + ")";
+                }
+                return gen;
+        };
+
+        function best_of(a) {
+                if (a.length == 1) {
+                        return a[0];
+                }
+                if (a.length == 2) {
+                        var b = a[1];
+                        a = a[0];
+                        return a.length <= b.length ? a : b;
+                }
+                return best_of([ a[0], best_of(a.slice(1)) ]);
+        };
+
+        function needs_parens(expr) {
+                if (expr[0] == "function" || expr[0] == "object") {
+                        // dot/call on a literal function requires the
+                        // function literal itself to be parenthesized
+                        // only if it's the first "thing" in a
+                        // statement.  This means that the parent is
+                        // "stat", but it could also be a "seq" and
+                        // we're the first in this "seq" and the
+                        // parent is "stat", and so on.  Messy stuff,
+                        // but it worths the trouble.
+                        var a = slice(w.stack()), self = a.pop(), p = a.pop();
+                        while (p) {
+                                if (p[0] == "stat") return true;
+                                if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) ||
+                                    ((p[0] == "binary" || p[0] == "assign" || p[0] == "unary-postfix") && p[2] === self)) {
+                                        self = p;
+                                        p = a.pop();
+                                } else {
+                                        return false;
+                                }
+                        }
+                }
+                return !HOP(DOT_CALL_NO_PARENS, expr[0]);
+        };
+
+        function make_num(num) {
+                var str = num.toString(10), a = [ str.replace(/^0\./, ".") ], m;
+                if (Math.floor(num) === num) {
+                        if (num >= 0) {
+                                a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
+                                       "0" + num.toString(8)); // same.
+                        } else {
+                                a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
+                                       "-0" + (-num).toString(8)); // same.
+                        }
+                        if ((m = /^(.*?)(0+)$/.exec(num))) {
+                                a.push(m[1] + "e" + m[2].length);
+                        }
+                } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
+                        a.push(m[2] + "e-" + (m[1].length + m[2].length),
+                               str.substr(str.indexOf(".")));
+                }
+                return best_of(a);
+        };
+
+        var w = ast_walker();
+        var make = w.walk;
+        return w.with_walkers({
+                "string": encode_string,
+                "num": make_num,
+                "name": make_name,
+                "debugger": function(){ return "debugger" },
+                "toplevel": function(statements) {
+                        return make_block_statements(statements)
+                                .join(newline + newline);
+                },
+                "splice": function(statements) {
+                        var parent = w.parent();
+                        if (HOP(SPLICE_NEEDS_BRACKETS, parent)) {
+                                // we need block brackets in this case
+                                return make_block.apply(this, arguments);
+                        } else {
+                                return MAP(make_block_statements(statements, true),
+                                           function(line, i) {
+                                                   // the first line is already indented
+                                                   return i > 0 ? indent(line) : line;
+                                           }).join(newline);
+                        }
+                },
+                "block": make_block,
+                "var": function(defs) {
+                        return "var " + add_commas(MAP(defs, make_1vardef)) + ";";
+                },
+                "const": function(defs) {
+                        return "const " + add_commas(MAP(defs, make_1vardef)) + ";";
+                },
+                "try": function(tr, ca, fi) {
+                        var out = [ "try", make_block(tr) ];
+                        if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1]));
+                        if (fi) out.push("finally", make_block(fi));
+                        return add_spaces(out);
+                },
+                "throw": function(expr) {
+                        return add_spaces([ "throw", make(expr) ]) + ";";
+                },
+                "new": function(ctor, args) {
+                        args = args.length > 0 ? "(" + add_commas(MAP(args, function(expr){
+                                return parenthesize(expr, "seq");
+                        })) + ")" : "";
+                        return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){
+                                var w = ast_walker(), has_call = {};
+                                try {
+                                        w.with_walkers({
+                                                "call": function() { throw has_call },
+                                                "function": function() { return this }
+                                        }, function(){
+                                                w.walk(expr);
+                                        });
+                                } catch(ex) {
+                                        if (ex === has_call)
+                                                return true;
+                                        throw ex;
+                                }
+                        }) + args ]);
+                },
+                "switch": function(expr, body) {
+                        return add_spaces([ "switch", "(" + make(expr) + ")", make_switch_block(body) ]);
+                },
+                "break": function(label) {
+                        var out = "break";
+                        if (label != null)
+                                out += " " + make_name(label);
+                        return out + ";";
+                },
+                "continue": function(label) {
+                        var out = "continue";
+                        if (label != null)
+                                out += " " + make_name(label);
+                        return out + ";";
+                },
+                "conditional": function(co, th, el) {
+                        return add_spaces([ parenthesize(co, "assign", "seq", "conditional"), "?",
+                                            parenthesize(th, "seq"), ":",
+                                            parenthesize(el, "seq") ]);
+                },
+                "assign": function(op, lvalue, rvalue) {
+                        if (op && op !== true) op += "=";
+                        else op = "=";
+                        return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]);
+                },
+                "dot": function(expr) {
+                        var out = make(expr), i = 1;
+                        if (expr[0] == "num") {
+                                if (!/\./.test(expr[1]))
+                                        out += ".";
+                        } else if (expr[0] != "function" && needs_parens(expr))
+                                out = "(" + out + ")";
+                        while (i < arguments.length)
+                                out += "." + make_name(arguments[i++]);
+                        return out;
+                },
+                "call": function(func, args) {
+                        var f = make(func);
+                        if (f.charAt(0) != "(" && needs_parens(func))
+                                f = "(" + f + ")";
+                        return f + "(" + add_commas(MAP(args, function(expr){
+                                return parenthesize(expr, "seq");
+                        })) + ")";
+                },
+                "function": make_function,
+                "defun": make_function,
+                "if": function(co, th, el) {
+                        var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ];
+                        if (el) {
+                                out.push("else", make(el));
+                        }
+                        return add_spaces(out);
+                },
+                "for": function(init, cond, step, block) {
+                        var out = [ "for" ];
+                        init = (init != null ? make(init) : "").replace(/;*\s*$/, ";" + space);
+                        cond = (cond != null ? make(cond) : "").replace(/;*\s*$/, ";" + space);
+                        step = (step != null ? make(step) : "").replace(/;*\s*$/, "");
+                        var args = init + cond + step;
+                        if (args == "; ; ") args = ";;";
+                        out.push("(" + args + ")", make(block));
+                        return add_spaces(out);
+                },
+                "for-in": function(vvar, key, hash, block) {
+                        return add_spaces([ "for", "(" +
+                                            (vvar ? make(vvar).replace(/;+$/, "") : make(key)),
+                                            "in",
+                                            make(hash) + ")", make(block) ]);
+                },
+                "while": function(condition, block) {
+                        return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]);
+                },
+                "do": function(condition, block) {
+                        return add_spaces([ "do", make(block), "while", "(" + make(condition) + ")" ]) + ";";
+                },
+                "return": function(expr) {
+                        var out = [ "return" ];
+                        if (expr != null) out.push(make(expr));
+                        return add_spaces(out) + ";";
+                },
+                "binary": function(operator, lvalue, rvalue) {
+                        var left = make(lvalue), right = make(rvalue);
+                        // XXX: I'm pretty sure other cases will bite here.
+                        //      we need to be smarter.
+                        //      adding parens all the time is the safest bet.
+                        if (member(lvalue[0], [ "assign", "conditional", "seq" ]) ||
+                            lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]] ||
+                            lvalue[0] == "function" && needs_parens(this)) {
+                                left = "(" + left + ")";
+                        }
+                        if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
+                            rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] &&
+                            !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
+                                right = "(" + right + ")";
+                        }
+                        else if (!beautify && options.inline_script && (operator == "<" || operator == "<<")
+                                 && rvalue[0] == "regexp" && /^script/i.test(rvalue[1])) {
+                                right = " " + right;
+                        }
+                        return add_spaces([ left, operator, right ]);
+                },
+                "unary-prefix": function(operator, expr) {
+                        var val = make(expr);
+                        if (!(expr[0] == "num" || (expr[0] == "unary-prefix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
+                                val = "(" + val + ")";
+                        return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val;
+                },
+                "unary-postfix": function(operator, expr) {
+                        var val = make(expr);
+                        if (!(expr[0] == "num" || (expr[0] == "unary-postfix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
+                                val = "(" + val + ")";
+                        return val + operator;
+                },
+                "sub": function(expr, subscript) {
+                        var hash = make(expr);
+                        if (needs_parens(expr))
+                                hash = "(" + hash + ")";
+                        return hash + "[" + make(subscript) + "]";
+                },
+                "object": function(props) {
+                        var obj_needs_parens = needs_parens(this);
+                        if (props.length == 0)
+                                return obj_needs_parens ? "({})" : "{}";
+                        var out = "{" + newline + with_indent(function(){
+                                return MAP(props, function(p){
+                                        if (p.length == 3) {
+                                                // getter/setter.  The name is in p[0], the arg.list in p[1][2], the
+                                                // body in p[1][3] and type ("get" / "set") in p[2].
+                                                return indent(make_function(p[0], p[1][2], p[1][3], p[2], true));
+                                        }
+                                        var key = p[0], val = parenthesize(p[1], "seq");
+                                        if (options.quote_keys) {
+                                                key = encode_string(key);
+                                        } else if ((typeof key == "number" || !beautify && +key + "" == key)
+                                                   && parseFloat(key) >= 0) {
+                                                key = make_num(+key);
+                                        } else if (!is_identifier(key)) {
+                                                key = encode_string(key);
+                                        }
+                                        return indent(add_spaces(beautify && options.space_colon
+                                                                 ? [ key, ":", val ]
+                                                                 : [ key + ":", val ]));
+                                }).join("," + newline);
+                        }) + newline + indent("}");
+                        return obj_needs_parens ? "(" + out + ")" : out;
+                },
+                "regexp": function(rx, mods) {
+                        return "/" + rx + "/" + mods;
+                },
+                "array": function(elements) {
+                        if (elements.length == 0) return "[]";
+                        return add_spaces([ "[", add_commas(MAP(elements, function(el, i){
+                                if (!beautify && el[0] == "atom" && el[1] == "undefined") return i === elements.length - 1 ? "," : "";
+                                return parenthesize(el, "seq");
+                        })), "]" ]);
+                },
+                "stat": function(stmt) {
+                        return make(stmt).replace(/;*\s*$/, ";");
+                },
+                "seq": function() {
+                        return add_commas(MAP(slice(arguments), make));
+                },
+                "label": function(name, block) {
+                        return add_spaces([ make_name(name), ":", make(block) ]);
+                },
+                "with": function(expr, block) {
+                        return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]);
+                },
+                "atom": function(name) {
+                        return make_name(name);
+                }
+        }, function(){ return make(ast) });
+
+        // The squeezer replaces "block"-s that contain only a single
+        // statement with the statement itself; technically, the AST
+        // is correct, but this can create problems when we output an
+        // IF having an ELSE clause where the THEN clause ends in an
+        // IF *without* an ELSE block (then the outer ELSE would refer
+        // to the inner IF).  This function checks for this case and
+        // adds the block brackets if needed.
+        function make_then(th) {
+                if (th == null) return ";";
+                if (th[0] == "do") {
+                        // https://github.com/mishoo/UglifyJS/issues/#issue/57
+                        // IE croaks with "syntax error" on code like this:
+                        //     if (foo) do ... while(cond); else ...
+                        // we need block brackets around do/while
+                        return make_block([ th ]);
+                }
+                var b = th;
+                while (true) {
+                        var type = b[0];
+                        if (type == "if") {
+                                if (!b[3])
+                                        // no else, we must add the block
+                                        return make([ "block", [ th ]]);
+                                b = b[3];
+                        }
+                        else if (type == "while" || type == "do") b = b[2];
+                        else if (type == "for" || type == "for-in") b = b[4];
+                        else break;
+                }
+                return make(th);
+        };
+
+        function make_function(name, args, body, keyword, no_parens) {
+                var out = keyword || "function";
+                if (name) {
+                        out += " " + make_name(name);
+                }
+                out += "(" + add_commas(MAP(args, make_name)) + ")";
+                out = add_spaces([ out, make_block(body) ]);
+                return (!no_parens && needs_parens(this)) ? "(" + out + ")" : out;
+        };
+
+        function must_has_semicolon(node) {
+                switch (node[0]) {
+                    case "with":
+                    case "while":
+                        return empty(node[2]); // `with' or `while' with empty body?
+                    case "for":
+                    case "for-in":
+                        return empty(node[4]); // `for' with empty body?
+                    case "if":
+                        if (empty(node[2]) && !node[3]) return true; // `if' with empty `then' and no `else'
+                        if (node[3]) {
+                                if (empty(node[3])) return true; // `else' present but empty
+                                return must_has_semicolon(node[3]); // dive into the `else' branch
+                        }
+                        return must_has_semicolon(node[2]); // dive into the `then' branch
+                }
+        };
+
+        function make_block_statements(statements, noindent) {
+                for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
+                        var stat = statements[i];
+                        var code = make(stat);
+                        if (code != ";") {
+                                if (!beautify && i == last && !must_has_semicolon(stat)) {
+                                        code = code.replace(/;+\s*$/, "");
+                                }
+                                a.push(code);
+                        }
+                }
+                return noindent ? a : MAP(a, indent);
+        };
+
+        function make_switch_block(body) {
+                var n = body.length;
+                if (n == 0) return "{}";
+                return "{" + newline + MAP(body, function(branch, i){
+                        var has_body = branch[1].length > 0, code = with_indent(function(){
+                                return indent(branch[0]
+                                              ? add_spaces([ "case", make(branch[0]) + ":" ])
+                                              : "default:");
+                        }, 0.5) + (has_body ? newline + with_indent(function(){
+                                return make_block_statements(branch[1]).join(newline);
+                        }) : "");
+                        if (!beautify && has_body && i < n - 1)
+                                code += ";";
+                        return code;
+                }).join(newline) + newline + indent("}");
+        };
+
+        function make_block(statements) {
+                if (!statements) return ";";
+                if (statements.length == 0) return "{}";
+                return "{" + newline + with_indent(function(){
+                        return make_block_statements(statements).join(newline);
+                }) + newline + indent("}");
+        };
+
+        function make_1vardef(def) {
+                var name = def[0], val = def[1];
+                if (val != null)
+                        name = add_spaces([ make_name(name), "=", parenthesize(val, "seq") ]);
+                return name;
+        };
+
+};
+
+function split_lines(code, max_line_length) {
+        var splits = [ 0 ];
+        jsp.parse(function(){
+                var next_token = jsp.tokenizer(code);
+                var last_split = 0;
+                var prev_token;
+                function current_length(tok) {
+                        return tok.pos - last_split;
+                };
+                function split_here(tok) {
+                        last_split = tok.pos;
+                        splits.push(last_split);
+                };
+                function custom(){
+                        var tok = next_token.apply(this, arguments);
+                        out: {
+                                if (prev_token) {
+                                        if (prev_token.type == "keyword") break out;
+                                }
+                                if (current_length(tok) > max_line_length) {
+                                        switch (tok.type) {
+                                            case "keyword":
+                                            case "atom":
+                                            case "name":
+                                            case "punc":
+                                                split_here(tok);
+                                                break out;
+                                        }
+                                }
+                        }
+                        prev_token = tok;
+                        return tok;
+                };
+                custom.context = function() {
+                        return next_token.context.apply(this, arguments);
+                };
+                return custom;
+        }());
+        return splits.map(function(pos, i){
+                return code.substring(pos, splits[i + 1] || code.length);
+        }).join("\n");
+};
+
+/* -----[ Utilities ]----- */
+
+function repeat_string(str, i) {
+        if (i <= 0) return "";
+        if (i == 1) return str;
+        var d = repeat_string(str, i >> 1);
+        d += d;
+        if (i & 1) d += str;
+        return d;
+};
+
+function defaults(args, defs) {
+        var ret = {};
+        if (args === true)
+                args = {};
+        for (var i in defs) if (HOP(defs, i)) {
+                ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
+        }
+        return ret;
+};
+
+function is_identifier(name) {
+        return /^[a-z_$][a-z0-9_$]*$/i.test(name)
+                && name != "this"
+                && !HOP(jsp.KEYWORDS_ATOM, name)
+                && !HOP(jsp.RESERVED_WORDS, name)
+                && !HOP(jsp.KEYWORDS, name);
+};
+
+function HOP(obj, prop) {
+        return Object.prototype.hasOwnProperty.call(obj, prop);
+};
+
+// some utilities
+
+var MAP;
+
+(function(){
+        MAP = function(a, f, o) {
+                var ret = [], top = [], i;
+                function doit() {
+                        var val = f.call(o, a[i], i);
+                        if (val instanceof AtTop) {
+                                val = val.v;
+                                if (val instanceof Splice) {
+                                        top.push.apply(top, val.v);
+                                } else {
+                                        top.push(val);
+                                }
+                        }
+                        else if (val != skip) {
+                                if (val instanceof Splice) {
+                                        ret.push.apply(ret, val.v);
+                                } else {
+                                        ret.push(val);
+                                }
+                        }
+                };
+                if (a instanceof Array) for (i = 0; i < a.length; ++i) doit();
+                else for (i in a) if (HOP(a, i)) doit();
+                return top.concat(ret);
+        };
+        MAP.at_top = function(val) { return new AtTop(val) };
+        MAP.splice = function(val) { return new Splice(val) };
+        var skip = MAP.skip = {};
+        function AtTop(val) { this.v = val };
+        function Splice(val) { this.v = val };
+})();
+
+/* -----[ Exports ]----- */
+
+exports.ast_walker = ast_walker;
+exports.ast_mangle = ast_mangle;
+exports.ast_squeeze = ast_squeeze;
+exports.ast_lift_variables = ast_lift_variables;
+exports.gen_code = gen_code;
+exports.ast_add_scope = ast_add_scope;
+exports.set_logger = function(logger) { warn = logger };
+exports.make_string = make_string;
+exports.split_lines = split_lines;
+exports.MAP = MAP;
+
+// keep this last!
+exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;

+ 73 - 0
utils/node_modules/uglify-js/lib/squeeze-more.js

@@ -0,0 +1,73 @@
+var jsp = require("./parse-js"),
+    pro = require("./process"),
+    slice = jsp.slice,
+    member = jsp.member,
+    curry = jsp.curry,
+    MAP = pro.MAP,
+    PRECEDENCE = jsp.PRECEDENCE,
+    OPERATORS = jsp.OPERATORS;
+
+function ast_squeeze_more(ast) {
+        var w = pro.ast_walker(), walk = w.walk, scope;
+        function with_scope(s, cont) {
+                var save = scope, ret;
+                scope = s;
+                ret = cont();
+                scope = save;
+                return ret;
+        };
+        function _lambda(name, args, body) {
+                return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
+        };
+        return w.with_walkers({
+                "toplevel": function(body) {
+                        return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
+                },
+                "function": _lambda,
+                "defun": _lambda,
+                "new": function(ctor, args) {
+                        if (ctor[0] == "name") {
+                                if (ctor[1] == "Array" && !scope.has("Array")) {
+                                        if (args.length != 1) {
+                                                return [ "array", args ];
+                                        } else {
+                                                return walk([ "call", [ "name", "Array" ], args ]);
+                                        }
+                                } else if (ctor[1] == "Object" && !scope.has("Object")) {
+                                        if (!args.length) {
+                                                return [ "object", [] ];
+                                        } else {
+                                                return walk([ "call", [ "name", "Object" ], args ]);
+                                        }
+                                } else if ((ctor[1] == "RegExp" || ctor[1] == "Function" || ctor[1] == "Error") && !scope.has(ctor[1])) {
+                                        return walk([ "call", [ "name", ctor[1] ], args]);
+                                }
+                        }
+                },
+                "call": function(expr, args) {
+                        if (expr[0] == "dot" && expr[1][0] == "string" && args.length == 1
+                            && (args[0][1] > 0 && expr[2] == "substring" || expr[2] == "substr")) {
+                                return [ "call", [ "dot", expr[1], "slice"], args];
+                        }
+                        if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
+                                // foo.toString()  ==>  foo+""
+                                return [ "binary", "+", expr[1], [ "string", "" ]];
+                        }
+                        if (expr[0] == "name") {
+                                if (expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
+                                        return [ "array", args ];
+                                }
+                                if (expr[1] == "Object" && !args.length && !scope.has("Object")) {
+                                        return [ "object", [] ];
+                                }
+                                if (expr[1] == "String" && !scope.has("String")) {
+                                        return [ "binary", "+", args[0], [ "string", "" ]];
+                                }
+                        }
+                }
+        }, function() {
+                return walk(pro.ast_add_scope(ast));
+        });
+};
+
+exports.ast_squeeze_more = ast_squeeze_more;

+ 24 - 0
utils/node_modules/uglify-js/package.json

@@ -0,0 +1,24 @@
+{
+        "name"    : "uglify-js",
+
+        "description" : "JavaScript parser and compressor/beautifier toolkit",
+
+        "author"  : {
+                "name"  : "Mihai Bazon",
+                "email" : "[email protected]",
+                "url"   : "http://mihai.bazon.net/blog"
+        },
+
+        "version" : "1.2.6",
+
+        "main"    : "./uglify-js.js",
+
+        "bin"     : {
+                "uglifyjs" : "./bin/uglifyjs"
+        },
+
+        "repository": {
+                "type": "git",
+                "url": "[email protected]:mishoo/UglifyJS.git"
+        }
+}

+ 24 - 0
utils/node_modules/uglify-js/package.json~

@@ -0,0 +1,24 @@
+{
+        "name"    : "uglify-js",
+
+        "description" : "JavaScript parser and compressor/beautifier toolkit",
+
+        "author"  : {
+                "name"  : "Mihai Bazon",
+                "email" : "[email protected]",
+                "url"   : "http://mihai.bazon.net/blog"
+        },
+
+        "version" : "1.2.3",
+
+        "main"    : "./uglify-js.js",
+
+        "bin"     : {
+                "uglifyjs" : "./bin/uglifyjs"
+        },
+
+        "repository": {
+                "type": "git",
+                "url": "[email protected]:mishoo/UglifyJS.git"
+        }
+}

+ 28 - 0
utils/node_modules/uglify-js/test/beautify.js

@@ -0,0 +1,28 @@
+#! /usr/bin/env node
+
+global.sys = require("sys");
+var fs = require("fs");
+
+var jsp = require("../lib/parse-js");
+var pro = require("../lib/process");
+
+var filename = process.argv[2];
+fs.readFile(filename, "utf8", function(err, text){
+        try {
+                var ast = time_it("parse", function(){ return jsp.parse(text); });
+                ast = time_it("mangle", function(){ return pro.ast_mangle(ast); });
+                ast = time_it("squeeze", function(){ return pro.ast_squeeze(ast); });
+                var gen = time_it("generate", function(){ return pro.gen_code(ast, false); });
+                sys.puts(gen);
+        } catch(ex) {
+                sys.debug(ex.stack);
+                sys.debug(sys.inspect(ex));
+                sys.debug(JSON.stringify(ex));
+        }
+});
+
+function time_it(name, cont) {
+        var t1 = new Date().getTime();
+        try { return cont(); }
+        finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
+};

+ 403 - 0
utils/node_modules/uglify-js/test/testparser.js

@@ -0,0 +1,403 @@
+#! /usr/bin/env node
+
+var parseJS = require("../lib/parse-js");
+var sys = require("sys");
+
+// write debug in a very straightforward manner
+var debug = function(){
+        sys.log(Array.prototype.slice.call(arguments).join(', '));
+};
+
+ParserTestSuite(function(i, input, desc){
+	try {
+		parseJS.parse(input);
+		debug("ok " + i + ": " + desc);
+	} catch(e){
+		debug("FAIL " + i + " " + desc + " (" + e + ")");
+	}
+});
+
+function ParserTestSuite(callback){
+	var inps = [
+		["var abc;", "Regular variable statement w/o assignment"],
+		["var abc = 5;", "Regular variable statement with assignment"],
+		["/* */;", "Multiline comment"],
+		['/** **/;', 'Double star multiline comment'],
+		["var f = function(){;};", "Function expression in var assignment"],
+		['hi; // moo\n;', 'single line comment'],
+		['var varwithfunction;', 'Dont match keywords as substrings'], // difference between `var withsomevar` and `"str"` (local search and lits)
+		['a + b;', 'addition'],
+		["'a';", 'single string literal'],
+		["'a\\n';", 'single string literal with escaped return'],
+		['"a";', 'double string literal'],
+		['"a\\n";', 'double string literal with escaped return'],
+		['"var";', 'string is a keyword'],
+		['"variable";', 'string starts with a keyword'],
+		['"somevariable";', 'string contains a keyword'],
+		['"somevar";', 'string ends with a keyword'],
+		['500;', 'int literal'],
+		['500.;', 'float literal w/o decimals'],
+		['500.432;', 'float literal with decimals'],
+		['.432432;', 'float literal w/o int'],
+		['(a,b,c);', 'parens and comma'],
+		['[1,2,abc];', 'array literal'],
+		['var o = {a:1};', 'object literal unquoted key'],
+		['var o = {"b":2};', 'object literal quoted key'], // opening curly may not be at the start of a statement...
+		['var o = {c:c};', 'object literal keyname is identifier'],
+		['var o = {a:1,"b":2,c:c};', 'object literal combinations'],
+		['var x;\nvar y;', 'two lines'],
+		['var x;\nfunction n(){; }', 'function def'],
+		['var x;\nfunction n(abc){; }', 'function def with arg'],
+		['var x;\nfunction n(abc, def){ ;}', 'function def with args'],
+		['function n(){ "hello"; }', 'function def with body'],
+		['/a/;', 'regex literal'],
+		['/a/b;', 'regex literal with flag'],
+		['/a/ / /b/;', 'regex div regex'],
+		['a/b/c;', 'triple division looks like regex'],
+		['+function(){/regex/;};', 'regex at start of function body'],
+		// http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=86
+		// http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=430
+
+		// first tests for the lexer, should also parse as program (when you append a semi)
+
+		// comments
+		['//foo!@#^&$1234\nbar;', 'single line comment'],
+		['/* abcd!@#@$* { } && null*/;', 'single line multi line comment'],
+		['/*foo\nbar*/;','multi line comment'],
+		['/*x*x*/;','multi line comment with *'],
+		['/**/;','empty comment'],
+		// identifiers
+		["x;",'1 identifier'],
+		["_x;",'2 identifier'],
+		["xyz;",'3 identifier'],
+		["$x;",'4 identifier'],
+		["x$;",'5 identifier'],
+		["_;",'6 identifier'],
+		["x5;",'7 identifier'],
+		["x_y;",'8 identifier'],
+		["x+5;",'9 identifier'],
+		["xyz123;",'10 identifier'],
+		["x1y1z1;",'11 identifier'],
+		["foo\\u00D8bar;",'12 identifier unicode escape'],
+		//["foo�bar;",'13 identifier unicode embedded (might fail)'],
+		// numbers
+		["5;", '1 number'],
+		["5.5;", '2 number'],
+		["0;", '3 number'],
+		["0.0;", '4 number'],
+		["0.001;", '5 number'],
+		["1.e2;", '6 number'],
+		["1.e-2;", '7 number'],
+		["1.E2;", '8 number'],
+		["1.E-2;", '9 number'],
+		[".5;", '10 number'],
+		[".5e3;", '11 number'],
+		[".5e-3;", '12 number'],
+		["0.5e3;", '13 number'],
+		["55;", '14 number'],
+		["123;", '15 number'],
+		["55.55;", '16 number'],
+		["55.55e10;", '17 number'],
+		["123.456;", '18 number'],
+		["1+e;", '20 number'],
+		["0x01;", '22 number'],
+		["0XCAFE;", '23 number'],
+		["0x12345678;", '24 number'],
+		["0x1234ABCD;", '25 number'],
+		["0x0001;", '26 number'],
+		// strings
+		["\"foo\";", '1 string'],
+		["\'foo\';", '2 string'],
+		["\"x\";", '3 string'],
+		["\'\';", '4 string'],
+		["\"foo\\tbar\";", '5 string'],
+		["\"!@#$%^&*()_+{}[]\";", '6 string'],
+		["\"/*test*/\";", '7 string'],
+		["\"//test\";", '8 string'],
+		["\"\\\\\";", '9 string'],
+		["\"\\u0001\";", '10 string'],
+		["\"\\uFEFF\";", '11 string'],
+		["\"\\u10002\";", '12 string'],
+		["\"\\x55\";", '13 string'],
+		["\"\\x55a\";", '14 string'],
+		["\"a\\\\nb\";", '15 string'],
+		['";"', '16 string: semi in a string'],
+		['"a\\\nb";', '17 string: line terminator escape'],
+		// literals
+		["null;", "null"],
+		["true;", "true"],
+		["false;", "false"],
+		// regex
+		["/a/;", "1 regex"],
+		["/abc/;", "2 regex"],
+		["/abc[a-z]*def/g;", "3 regex"],
+		["/\\b/;", "4 regex"],
+		["/[a-zA-Z]/;", "5 regex"],
+
+		// program tests (for as far as they havent been covered above)
+
+		// regexp
+		["/foo(.*)/g;", "another regexp"],
+		// arrays
+		["[];", "1 array"],
+		["[   ];", "2 array"],
+		["[1];", "3 array"],
+		["[1,2];", "4 array"],
+		["[1,2,,];", "5 array"],
+		["[1,2,3];", "6 array"],
+		["[1,2,3,,,];", "7 array"],
+		// objects
+		["{};", "1 object"],
+		["({x:5});", "2 object"],
+		["({x:5,y:6});", "3 object"],
+		["({x:5,});", "4 object"],
+		["({if:5});", "5 object"],
+		["({ get x() {42;} });", "6 object"],
+		["({ set y(a) {1;} });", "7 object"],
+		// member expression
+		["o.m;", "1 member expression"],
+		["o['m'];", "2 member expression"],
+		["o['n']['m'];", "3 member expression"],
+		["o.n.m;", "4 member expression"],
+		["o.if;", "5 member expression"],
+		// call and invoke expressions
+		["f();", "1 call/invoke expression"],
+		["f(x);", "2 call/invoke expression"],
+		["f(x,y);", "3 call/invoke expression"],
+		["o.m();", "4 call/invoke expression"],
+		["o['m'];", "5 call/invoke expression"],
+		["o.m(x);", "6 call/invoke expression"],
+		["o['m'](x);", "7 call/invoke expression"],
+		["o.m(x,y);", "8 call/invoke expression"],
+		["o['m'](x,y);", "9 call/invoke expression"],
+		["f(x)(y);", "10 call/invoke expression"],
+		["f().x;", "11 call/invoke expression"],
+
+		// eval
+		["eval('x');", "1 eval"],
+		["(eval)('x');", "2 eval"],
+		["(1,eval)('x');", "3 eval"],
+		["eval(x,y);", "4 eval"],
+		// new expression
+		["new f();", "1 new expression"],
+		["new o;", "2 new expression"],
+		["new o.m;", "3 new expression"],
+		["new o.m(x);", "4 new expression"],
+		["new o.m(x,y);", "5 new expression"],
+		// prefix/postfix
+		["++x;", "1 pre/postfix"],
+		["x++;", "2 pre/postfix"],
+		["--x;", "3 pre/postfix"],
+		["x--;", "4 pre/postfix"],
+		["x ++;", "5 pre/postfix"],
+		["x /* comment */ ++;", "6 pre/postfix"],
+		["++ /* comment */ x;", "7 pre/postfix"],
+		// unary operators
+		["delete x;", "1 unary operator"],
+		["void x;", "2 unary operator"],
+		["+ x;", "3 unary operator"],
+		["-x;", "4 unary operator"],
+		["~x;", "5 unary operator"],
+		["!x;", "6 unary operator"],
+		// meh
+		["new Date++;", "new date ++"],
+		["+x++;", " + x ++"],
+		// expression expressions
+		["1 * 2;", "1 expression expressions"],
+		["1 / 2;", "2 expression expressions"],
+		["1 % 2;", "3 expression expressions"],
+		["1 + 2;", "4 expression expressions"],
+		["1 - 2;", "5 expression expressions"],
+		["1 << 2;", "6 expression expressions"],
+		["1 >>> 2;", "7 expression expressions"],
+		["1 >> 2;", "8 expression expressions"],
+		["1 * 2 + 3;", "9 expression expressions"],
+		["(1+2)*3;", "10 expression expressions"],
+		["1*(2+3);", "11 expression expressions"],
+		["x<y;", "12 expression expressions"],
+		["x>y;", "13 expression expressions"],
+		["x<=y;", "14 expression expressions"],
+		["x>=y;", "15 expression expressions"],
+		["x instanceof y;", "16 expression expressions"],
+		["x in y;", "17 expression expressions"],
+		["x&y;", "18 expression expressions"],
+		["x^y;", "19 expression expressions"],
+		["x|y;", "20 expression expressions"],
+		["x+y<z;", "21 expression expressions"],
+		["x<y+z;", "22 expression expressions"],
+		["x+y+z;", "23 expression expressions"],
+		["x+y<z;", "24 expression expressions"],
+		["x<y+z;", "25 expression expressions"],
+		["x&y|z;", "26 expression expressions"],
+		["x&&y;", "27 expression expressions"],
+		["x||y;", "28 expression expressions"],
+		["x&&y||z;", "29 expression expressions"],
+		["x||y&&z;", "30 expression expressions"],
+		["x<y?z:w;", "31 expression expressions"],
+		// assignment
+		["x >>>= y;", "1 assignment"],
+		["x <<= y;", "2 assignment"],
+		["x = y;", "3 assignment"],
+		["x += y;", "4 assignment"],
+		["x /= y;", "5 assignment"],
+		// comma
+		["x, y;", "comma"],
+		// block
+		["{};", "1 block"],
+		["{x;};", "2 block"],
+		["{x;y;};", "3 block"],
+		// vars
+		["var x;", "1 var"],
+		["var x,y;", "2 var"],
+		["var x=1,y=2;", "3 var"],
+		["var x,y=2;", "4 var"],
+		// empty
+		[";", "1 empty"],
+		["\n;", "2 empty"],
+		// expression statement
+		["x;", "1 expression statement"],
+		["5;", "2 expression statement"],
+		["1+2;", "3 expression statement"],
+		// if
+		["if (c) x; else y;", "1 if statement"],
+		["if (c) x;", "2 if statement"],
+		["if (c) {} else {};", "3 if statement"],
+		["if (c1) if (c2) s1; else s2;", "4 if statement"],
+		// while
+		["do s; while (e);", "1 while statement"],
+		["do { s; } while (e);", "2 while statement"],
+		["while (e) s;", "3 while statement"],
+		["while (e) { s; };", "4 while statement"],
+		// for
+		["for (;;) ;", "1 for statement"],
+		["for (;c;x++) x;", "2 for statement"],
+		["for (i;i<len;++i){};", "3 for statement"],
+		["for (var i=0;i<len;++i) {};", "4 for statement"],
+		["for (var i=0,j=0;;){};", "5 for statement"],
+		//["for (x in b; c; u) {};", "6 for statement"],
+		["for ((x in b); c; u) {};", "7 for statement"],
+		["for (x in a);", "8 for statement"],
+		["for (var x in a){};", "9 for statement"],
+		["for (var x=5 in a) {};", "10 for statement"],
+		["for (var x = a in b in c) {};", "11 for statement"],
+		["for (var x=function(){a+b;}; a<b; ++i) some;", "11 for statement, testing for parsingForHeader reset with the function"],
+		["for (var x=function(){for (x=0; x<15; ++x) alert(foo); }; a<b; ++i) some;", "11 for statement, testing for parsingForHeader reset with the function"],
+		// flow statements
+		["while(1){ continue; }", "1 flow statement"],
+		["label: while(1){ continue label; }", "2 flow statement"],
+		["while(1){ break; }", "3 flow statement"],
+		["somewhere: while(1){ break somewhere; }", "4 flow statement"],
+		["while(1){ continue /* comment */ ; }", "5 flow statement"],
+		["while(1){ continue \n; }", "6 flow statement"],
+		["(function(){ return; })()", "7 flow statement"],
+		["(function(){ return 0; })()", "8 flow statement"],
+		["(function(){ return 0 + \n 1; })()", "9 flow statement"],
+		// with
+		["with (e) s;", "with statement"],
+		// switch
+		["switch (e) { case x: s; };", "1 switch statement"],
+		["switch (e) { case x: s1;s2; default: s3; case y: s4; };", "2 switch statement"],
+		["switch (e) { default: s1; case x: s2; case y: s3; };", "3 switch statement"],
+		["switch (e) { default: s; };", "4 switch statement"],
+		["switch (e) { case x: s1; case y: s2; };", "5 switch statement"],
+		// labels
+		["foo : x;", " flow statement"],
+		// throw
+		["throw x;", "1 throw statement"],
+		["throw x\n;", "2 throw statement"],
+		// try catch finally
+		["try { s1; } catch (e) { s2; };", "1 trycatchfinally statement"],
+		["try { s1; } finally { s2; };", "2 trycatchfinally statement"],
+		["try { s1; } catch (e) { s2; } finally { s3; };", "3 trycatchfinally statement"],
+		// debugger
+		["debugger;", "debuger statement"],
+		// function decl
+		["function f(x) { e; return x; };", "1 function declaration"],
+		["function f() { x; y; };", "2 function declaration"],
+		["function f(x,y) { var z; return x; };", "3 function declaration"],
+		// function exp
+		["(function f(x) { return x; });", "1 function expression"],
+		["(function empty() {;});", "2 function expression"],
+		["(function empty() {;});", "3 function expression"],
+		["(function (x) {; });", "4 function expression"],
+		// program
+		["var x; function f(){;}; null;", "1 program"],
+		[";;", "2 program"],
+		["{ x; y; z; }", "3 program"],
+		["function f(){ function g(){;}};", "4 program"],
+		["x;\n/*foo*/\n	;", "5 program"],
+
+		// asi
+		["foo: while(1){ continue \n foo; }", "1 asi"],
+		["foo: while(1){ break \n foo; }", "2 asi"],
+		["(function(){ return\nfoo; })()", "3 asi"],
+		["var x; { 1 \n 2 } 3", "4 asi"],
+		["ab 	 /* hi */\ncd", "5 asi"],
+		["ab/*\n*/cd", "6 asi (multi line multilinecomment counts as eol)"],
+		["foo: while(1){ continue /* wtf \n busta */ foo; }", "7 asi illegal with multi line comment"],
+		["function f() { s }", "8 asi"],
+		["function f() { return }", "9 asi"],
+
+		// use strict
+                // XXX: some of these should actually fail?
+                //      no support for "use strict" yet...
+		['"use strict"; \'bla\'\n; foo;', "1 directive"],
+		['(function() { "use strict"; \'bla\';\n foo; });', "2 directive"],
+		['"use\\n strict";', "3 directive"],
+		['foo; "use strict";', "4 directive"],
+
+		// tests from http://es5conform.codeplex.com/
+
+		['"use strict"; var o = { eval: 42};', "8.7.2-3-1-s: the use of eval as property name is allowed"],
+		['({foo:0,foo:1});', 'Duplicate property name allowed in not strict mode'],
+		['function foo(a,a){}', 'Duplicate parameter name allowed in not strict mode'],
+		['(function foo(eval){})', 'Eval allowed as parameter name in non strict mode'],
+		['(function foo(arguments){})', 'Arguments allowed as parameter name in non strict mode'],
+
+		// empty programs
+
+		['', '1 Empty program'],
+		['// test', '2 Empty program'],
+		['//test\n', '3 Empty program'],
+		['\n// test', '4 Empty program'],
+		['\n// test\n', '5 Empty program'],
+		['/* */', '6 Empty program'],
+		['/*\ns,fd\n*/', '7 Empty program'],
+		['/*\ns,fd\n*/\n', '8 Empty program'],
+		['  	', '9 Empty program'],
+		['  /*\nsmeh*/	\n   ', '10 Empty program'],
+
+		// trailing whitespace
+
+		['a  ', '1 Trailing whitespace'],
+		['a /* something */', '2 Trailing whitespace'],
+		['a\n	// hah', '3 Trailing whitespace'],
+		['/abc/de//f', '4 Trailing whitespace'],
+		['/abc/de/*f*/\n	', '5 Trailing whitespace'],
+
+		// things the parser tripped over at one point or the other (prevents regression bugs)
+		['for (x;function(){ a\nb };z) x;', 'for header with function body forcing ASI'],
+		['c=function(){return;return};', 'resetting noAsi after literal'],
+		['d\nd()', 'asi exception causing token overflow'],
+		['for(;;){x=function(){}}', 'function expression in a for header'],
+		['for(var k;;){}', 'parser failing due to ASI accepting the incorrect "for" rule'],
+		['({get foo(){ }})', 'getter with empty function body'],
+		['\nreturnr', 'eol causes return statement to ignore local search requirement'],
+		[' / /', '1 whitespace before regex causes regex to fail?'],
+		['/ // / /', '2 whitespace before regex causes regex to fail?'],
+		['/ / / / /', '3 whitespace before regex causes regex to fail?'],
+
+		['\n\t// Used for trimming whitespace\n\ttrimLeft = /^\\s+/;\n\ttrimRight = /\\s+$/;\t\n','turned out this didnt crash (the test below did), but whatever.'],
+		['/[\\/]/;', 'escaped forward slash inside class group (would choke on fwd slash)'],
+		['/[/]/;', 'also broke but is valid in es5 (not es3)'],
+		['({get:5});','get property name thats not a getter'],
+		['({set:5});','set property name thats not a setter'],
+		['l !== "px" && (d.style(h, c, (k || 1) + l), j = (k || 1) / f.cur() * j, d.style(h, c, j + l)), i[1] && (k = (i[1] === "-=" ? -1 : 1) * k + j), f.custom(j, k, l)', 'this choked regex/div at some point'],
+		['(/\'/g, \'\\\\\\\'\') + "\'";', 'the sequence of escaped characters confused the tokenizer'],
+                ['if (true) /=a/.test("a");', 'regexp starting with "=" in not obvious context (not implied by preceding token)']
+	];
+
+	for (var i=0; i<inps.length; ++i) {
+		callback(i, inps[i][0], inps[i][1]);
+	};
+};

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/array1.js

@@ -0,0 +1 @@
+[],Array(1),[1,2,3]

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/array2.js

@@ -0,0 +1 @@
+(function(){var a=function(){};return new a(1,2,3,4)})()

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/array3.js

@@ -0,0 +1 @@
+(function(){function a(){}return new a(1,2,3,4)})()

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/array4.js

@@ -0,0 +1 @@
+(function(){function a(){}(function(){return new a(1,2,3)})()})()

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/assignment.js

@@ -0,0 +1 @@
+a=1,b=a,c=1,d=b,e=d,longname=2;if(longname+1){x=3;if(x)var z=7}z=1,y=1,x=1,g+=1,h=g,++i,j=i,i++,j=i+17

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/concatstring.js

@@ -0,0 +1 @@
+var a=a+"a"+"b"+1+c,b=a+"c"+"ds"+123+c,c=a+"c"+123+d+"ds"+c

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/const.js

@@ -0,0 +1 @@
+var a=13,b=1/3

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/empty-blocks.js

@@ -0,0 +1 @@
+function bar(){return--x}function foo(){while(bar());}function mak(){for(;;);}var x=5

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/forstatement.js

@@ -0,0 +1 @@
+a=func(),b=z;for(a++;i<10;i++)alert(i);var z=1;g=2;for(;i<10;i++)alert(i);var a=2;for(var i=1;i<10;i++)alert(i)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/if.js

@@ -0,0 +1 @@
+var a=1;a==1?a=2:a=17

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/ifreturn.js

@@ -0,0 +1 @@
+function a(a){return a==1?2:17}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/ifreturn2.js

@@ -0,0 +1 @@
+function x(a){return typeof a=="object"?a:a===42?0:a*2}function y(a){return typeof a=="object"?a:null}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue10.js

@@ -0,0 +1 @@
+function f(){var a;return(a="a")?a:a}f()

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue11.js

@@ -0,0 +1 @@
+new(A,B),new(A||B),new(X?A:B)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue13.js

@@ -0,0 +1 @@
+var a=/^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#])(?::(\d))?)?(..?$|(?:[^?#\/]\/))([^?#]*)(?:\?([^#]))?(?:#(.))?/

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue14.js

@@ -0,0 +1 @@
+var a={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue16.js

@@ -0,0 +1 @@
+var a=3250441966

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue17.js

@@ -0,0 +1 @@
+var a=function(b){b(),a()}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue20.js

@@ -0,0 +1 @@
+1

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue21.js

@@ -0,0 +1 @@
+var a=0;switch(a){case 0:a++}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue25.js

@@ -0,0 +1 @@
+a:break a;console.log(1)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue27.js

@@ -0,0 +1 @@
+(a?b:c)?d:e

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue278.js

@@ -0,0 +1 @@
+if(!x)debugger

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue28.js

@@ -0,0 +1 @@
+o={".5":.5},o={.5:.5},o={.5:.5}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue29.js

@@ -0,0 +1 @@
+result=function(){return 1}()

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue30.js

@@ -0,0 +1 @@
+var a=8,b=4,c=4

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue34.js

@@ -0,0 +1 @@
+var a={};a["this"]=1,a.that=2

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue4.js

@@ -0,0 +1 @@
+var a=2e3,b=.002,c=2e-5

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue48.js

@@ -0,0 +1 @@
+var s,i;s="",i=0

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue50.js

@@ -0,0 +1 @@
+function bar(a){try{foo()}catch(b){alert("Exception caught (foo not defined)")}alert(a)}bar(10)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue53.js

@@ -0,0 +1 @@
+x=(y,z)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue54.1.js

@@ -0,0 +1 @@
+foo+"",a.toString(16),b.toString.call(c)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue68.js

@@ -0,0 +1 @@
+function f(){function b(){}if(a)return;b()}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue69.js

@@ -0,0 +1 @@
+[(a,b)]

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/issue9.js

@@ -0,0 +1 @@
+var a={a:1,b:2}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/mangle.js

@@ -0,0 +1 @@
+(function(){var a=function b(a,b,c){return b}})()

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/null_string.js

@@ -0,0 +1 @@
+var nullString="\0"

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/strict-equals.js

@@ -0,0 +1 @@
+typeof a=="string",b+""!=c+"",d<e==f<g

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/var.js

@@ -0,0 +1 @@
+var a=1,b=2

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/whitespace.js

@@ -0,0 +1 @@
+function id(a){return a}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/expected/with.js

@@ -0,0 +1 @@
+with({});

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/array1.js

@@ -0,0 +1,3 @@
+new Array();
+new Array(1);
+new Array(1, 2, 3);

+ 4 - 0
utils/node_modules/uglify-js/test/unit/compress/test/array2.js

@@ -0,0 +1,4 @@
+(function(){
+        var Array = function(){};
+        return new Array(1, 2, 3, 4);
+})();

+ 4 - 0
utils/node_modules/uglify-js/test/unit/compress/test/array3.js

@@ -0,0 +1,4 @@
+(function(){
+        return new Array(1, 2, 3, 4);
+        function Array() {};
+})();

+ 6 - 0
utils/node_modules/uglify-js/test/unit/compress/test/array4.js

@@ -0,0 +1,6 @@
+(function(){
+        (function(){
+                return new Array(1, 2, 3);
+        })();
+        function Array(){};
+})();

+ 20 - 0
utils/node_modules/uglify-js/test/unit/compress/test/assignment.js

@@ -0,0 +1,20 @@
+a=1;
+b=a;
+c=1;
+d=b;
+e=d;
+longname=2;
+if (longname+1) {
+    x=3;
+    if (x) var z = 7;
+}
+z=1,y=1,x=1
+
+g+=1;
+h=g;
+
+++i;
+j=i;
+
+i++;
+j=i+17;

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/concatstring.js

@@ -0,0 +1,3 @@
+var a = a + "a" + "b" + 1 + c;
+var b = a + "c" + "ds" + 123 + c;
+var c = a + "c" + 123 + d + "ds" + c;

+ 5 - 0
utils/node_modules/uglify-js/test/unit/compress/test/const.js

@@ -0,0 +1,5 @@
+// test that the calculation is fold to 13
+var a = 1 + 2 * 6;
+
+// test that it isn't replaced with 0.3333 because that is more characters
+var b = 1/3;

+ 4 - 0
utils/node_modules/uglify-js/test/unit/compress/test/empty-blocks.js

@@ -0,0 +1,4 @@
+var x = 5;
+function bar() { return --x; }
+function foo() { while (bar()); }
+function mak() { for(;;); }

+ 10 - 0
utils/node_modules/uglify-js/test/unit/compress/test/forstatement.js

@@ -0,0 +1,10 @@
+a=func();
+b=z;
+for (a++; i < 10; i++) { alert(i); }
+
+var z=1;
+g=2;
+for (; i < 10; i++) { alert(i); }
+
+var a = 2;
+for (var i = 1; i < 10; i++) { alert(i); }

+ 6 - 0
utils/node_modules/uglify-js/test/unit/compress/test/if.js

@@ -0,0 +1,6 @@
+var a = 1;
+if (a == 1) {
+	a = 2;
+} else {
+	a = 17;
+}

+ 9 - 0
utils/node_modules/uglify-js/test/unit/compress/test/ifreturn.js

@@ -0,0 +1,9 @@
+function a(b) {
+	if (b == 1) {
+		return 2;
+	} else {
+		return 17;
+	}
+
+	return 3;
+}

+ 16 - 0
utils/node_modules/uglify-js/test/unit/compress/test/ifreturn2.js

@@ -0,0 +1,16 @@
+function x(a) {
+    if (typeof a === 'object')
+        return a;
+
+    if (a === 42)
+        return 0;
+
+    return a * 2;
+}
+
+function y(a) {
+    if (typeof a === 'object')
+        return a;
+
+    return null;
+};

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue10.js

@@ -0,0 +1 @@
+function f() { var a; if (a = 'a') { return a; } else { return a; } }; f();

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue11.js

@@ -0,0 +1,3 @@
+new (A, B)
+new (A || B)
+new (X ? A : B)

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue13.js

@@ -0,0 +1 @@
+var a = /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#])(?::(\d))?)?(..?$|(?:[^?#\/]\/))([^?#]*)(?:\?([^#]))?(?:#(.))?/;

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue14.js

@@ -0,0 +1 @@
+var a = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue16.js

@@ -0,0 +1 @@
+var a = 0xC1BDCEEE;

+ 4 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue17.js

@@ -0,0 +1,4 @@
+var a = function(b) {
+    b();
+    a()
+}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue20.js

@@ -0,0 +1 @@
+{a: 1}

+ 6 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue21.js

@@ -0,0 +1,6 @@
+var a = 0;
+switch(a) {
+    case 0:
+        a++;
+        break;
+}

+ 7 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue25.js

@@ -0,0 +1,7 @@
+label1 : {
+    label2 : {
+        break label2;
+        console.log(2);
+    }
+    console.log(1);
+}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue27.js

@@ -0,0 +1 @@
+(a ? b : c) ? d : e

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue278.js

@@ -0,0 +1 @@
+if (!x) debugger;

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue28.js

@@ -0,0 +1,3 @@
+o = {'.5':.5}
+o = {'0.5':.5}
+o = {0.5:.5}

+ 1 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue29.js

@@ -0,0 +1 @@
+result=(function(){ return 1;})()

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue30.js

@@ -0,0 +1,3 @@
+var a = 1 << 3;
+var b = 8 >> 1;
+var c = 8 >>> 1;

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue34.js

@@ -0,0 +1,3 @@
+var a = {};
+a["this"] = 1;
+a["that"] = 2;

+ 3 - 0
utils/node_modules/uglify-js/test/unit/compress/test/issue4.js

@@ -0,0 +1,3 @@
+var a = 2e3;
+var b = 2e-3;
+var c = 2e-5;

部分文件因为文件数量过多而无法显示