jazz-jss 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (215) hide show
  1. data/dist/handlebars/handlebars.js +1493 -0
  2. data/dist/hashchange/{jquery.ba-hashchange.js → hashchange.js} +0 -0
  3. data/dist/jazz/lib/controller.js +1 -1
  4. data/dist/jazz/lib/core.js +3 -0
  5. data/dist/jazz/lib/db.js +1 -1
  6. data/dist/jazz/lib/helper.js +2 -1
  7. data/dist/jazz/lib/model.js +11 -4
  8. data/dist/jazz/lib/view.js +1 -1
  9. data/dist/jazz/module.js +15 -0
  10. data/dist/jquery/jquery.js +0 -0
  11. data/dist/require/require.js +0 -0
  12. data/dist/require/template.js +98 -0
  13. data/dist/underscore/underscore.js +0 -0
  14. data/lib/jazz/app_detector.rb +6 -3
  15. data/lib/jazz/app_generator.rb +12 -8
  16. data/lib/jazz/helper_generator.rb +30 -0
  17. data/lib/jazz/scaffold_generator.rb +4 -0
  18. data/templates/app_root/Rakefile +11 -0
  19. data/templates/app_root/{app → development/app}/assets/javascripts/application.js +0 -0
  20. data/templates/app_root/{app → development/app}/assets/stylesheets/application.css +0 -0
  21. data/templates/app_root/development/app/controllers/application_controller.js +11 -0
  22. data/templates/app_root/development/app/helpers/application_helper.js +7 -0
  23. data/templates/app_root/development/config/glue.js +9 -0
  24. data/templates/app_root/development/config/routes.js +5 -0
  25. data/templates/app_root/scripts/build.js +30 -0
  26. data/templates/app_root/scripts/r.js/LICENSE +58 -0
  27. data/templates/app_root/scripts/r.js/README.md +177 -0
  28. data/templates/app_root/scripts/r.js/build/build.js +26 -0
  29. data/templates/app_root/scripts/r.js/build/buildebug.sh +4 -0
  30. data/templates/app_root/scripts/r.js/build/example.build.js +296 -0
  31. data/templates/app_root/scripts/r.js/build/jslib/blank.js +4 -0
  32. data/templates/app_root/scripts/r.js/build/jslib/build.js +952 -0
  33. data/templates/app_root/scripts/r.js/build/jslib/commandLine.js +23 -0
  34. data/templates/app_root/scripts/r.js/build/jslib/commonJs.js +152 -0
  35. data/templates/app_root/scripts/r.js/build/jslib/env.js +47 -0
  36. data/templates/app_root/scripts/r.js/build/jslib/lang.js +49 -0
  37. data/templates/app_root/scripts/r.js/build/jslib/logger.js +58 -0
  38. data/templates/app_root/scripts/r.js/build/jslib/node.js +106 -0
  39. data/templates/app_root/scripts/r.js/build/jslib/node/args.js +20 -0
  40. data/templates/app_root/scripts/r.js/build/jslib/node/file.js +263 -0
  41. data/templates/app_root/scripts/r.js/build/jslib/node/load.js +17 -0
  42. data/templates/app_root/scripts/r.js/build/jslib/node/optimize.js +10 -0
  43. data/templates/app_root/scripts/r.js/build/jslib/node/print.js +16 -0
  44. data/templates/app_root/scripts/r.js/build/jslib/optimize.js +279 -0
  45. data/templates/app_root/scripts/r.js/build/jslib/opto.build.js +11 -0
  46. data/templates/app_root/scripts/r.js/build/jslib/parse.js +590 -0
  47. data/templates/app_root/scripts/r.js/build/jslib/pragma.js +251 -0
  48. data/templates/app_root/scripts/r.js/build/jslib/requirePatch.js +286 -0
  49. data/templates/app_root/scripts/r.js/build/jslib/rhino.js +22 -0
  50. data/templates/app_root/scripts/r.js/build/jslib/rhino/args.js +21 -0
  51. data/templates/app_root/scripts/r.js/build/jslib/rhino/file.js +244 -0
  52. data/templates/app_root/scripts/r.js/build/jslib/rhino/load.js +12 -0
  53. data/templates/app_root/scripts/r.js/build/jslib/rhino/optimize.js +100 -0
  54. data/templates/app_root/scripts/r.js/build/jslib/rhino/print.js +12 -0
  55. data/templates/app_root/scripts/r.js/build/jslib/uglifyjs/README.md +16 -0
  56. data/templates/app_root/scripts/r.js/build/jslib/uglifyjs/index.js +21 -0
  57. data/templates/app_root/scripts/r.js/build/jslib/uglifyjs/parse-js.js +1342 -0
  58. data/templates/app_root/scripts/r.js/build/jslib/uglifyjs/process.js +2005 -0
  59. data/templates/app_root/scripts/r.js/build/jslib/uglifyjs/squeeze-more.js +55 -0
  60. data/templates/app_root/scripts/r.js/build/jslib/x.js +243 -0
  61. data/templates/app_root/scripts/r.js/build/tests/all.js +47 -0
  62. data/templates/app_root/scripts/r.js/build/tests/allj.sh +3 -0
  63. data/templates/app_root/scripts/r.js/build/tests/alln.sh +18 -0
  64. data/templates/app_root/scripts/r.js/build/tests/anonSimple.build.js +7 -0
  65. data/templates/app_root/scripts/r.js/build/tests/buildUtils.js +19 -0
  66. data/templates/app_root/scripts/r.js/build/tests/builds.js +489 -0
  67. data/templates/app_root/scripts/r.js/build/tests/circular.build.js +18 -0
  68. data/templates/app_root/scripts/r.js/build/tests/convert.js +18 -0
  69. data/templates/app_root/scripts/r.js/build/tests/css.build.js +6 -0
  70. data/templates/app_root/scripts/r.js/build/tests/css/common/common.css +4 -0
  71. data/templates/app_root/scripts/r.js/build/tests/css/master.css +10 -0
  72. data/templates/app_root/scripts/r.js/build/tests/css/sub/sub1.css +7 -0
  73. data/templates/app_root/scripts/r.js/build/tests/cssTestCompare.css +13 -0
  74. data/templates/app_root/scripts/r.js/build/tests/end.frag +3 -0
  75. data/templates/app_root/scripts/r.js/build/tests/expected/unoExcludeShallow.js +23 -0
  76. data/templates/app_root/scripts/r.js/build/tests/exports.build.js +12 -0
  77. data/templates/app_root/scripts/r.js/build/tests/hasTestModule.build.js +10 -0
  78. data/templates/app_root/scripts/r.js/build/tests/hasTestModule.js +30 -0
  79. data/templates/app_root/scripts/r.js/build/tests/http/httpBuild.js +70 -0
  80. data/templates/app_root/scripts/r.js/build/tests/http/main.html +28 -0
  81. data/templates/app_root/scripts/r.js/build/tests/http/scripts/main.js +18 -0
  82. data/templates/app_root/scripts/r.js/build/tests/http/scripts/one.js +3 -0
  83. data/templates/app_root/scripts/r.js/build/tests/http/scripts/three.js +5 -0
  84. data/templates/app_root/scripts/r.js/build/tests/http/scripts/two.js +7 -0
  85. data/templates/app_root/scripts/r.js/build/tests/i18n.build.js +17 -0
  86. data/templates/app_root/scripts/r.js/build/tests/indexBuilder.build.js +8 -0
  87. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/a.js +17 -0
  88. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/b.js +8 -0
  89. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/build.js +6 -0
  90. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/c.js +7 -0
  91. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/d.js +5 -0
  92. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/expected.js +36 -0
  93. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/node_modules/amdefine/LICENSE +58 -0
  94. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/node_modules/amdefine/amdefine.js +188 -0
  95. data/templates/app_root/scripts/r.js/build/tests/lib/amdefine/node_modules/amdefine/package.json +21 -0
  96. data/templates/app_root/scripts/r.js/build/tests/lib/comments/bang.js +10 -0
  97. data/templates/app_root/scripts/r.js/build/tests/lib/comments/build.js +6 -0
  98. data/templates/app_root/scripts/r.js/build/tests/lib/comments/expected.js +9 -0
  99. data/templates/app_root/scripts/r.js/build/tests/lib/comments/license.js +13 -0
  100. data/templates/app_root/scripts/r.js/build/tests/lib/dotpackage/scripts/app.build.js +15 -0
  101. data/templates/app_root/scripts/r.js/build/tests/lib/dotpackage/scripts/main-expected.js +13 -0
  102. data/templates/app_root/scripts/r.js/build/tests/lib/dotpackage/scripts/main.js +7 -0
  103. data/templates/app_root/scripts/r.js/build/tests/lib/empty/build.js +13 -0
  104. data/templates/app_root/scripts/r.js/build/tests/lib/empty/expected.js +8 -0
  105. data/templates/app_root/scripts/r.js/build/tests/lib/empty/main.js +1 -0
  106. data/templates/app_root/scripts/r.js/build/tests/lib/empty/sub1.js +1 -0
  107. data/templates/app_root/scripts/r.js/build/tests/lib/empty/sub2.js +1 -0
  108. data/templates/app_root/scripts/r.js/build/tests/lib/nameInsertion/build.js +6 -0
  109. data/templates/app_root/scripts/r.js/build/tests/lib/nameInsertion/expected.js +12 -0
  110. data/templates/app_root/scripts/r.js/build/tests/lib/nameInsertion/main.js +9 -0
  111. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/build.js +8 -0
  112. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/expected.js +47 -0
  113. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/main.js +6 -0
  114. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/modules/four.js +7 -0
  115. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/modules/one.js +9 -0
  116. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/modules/three.js +14 -0
  117. data/templates/app_root/scripts/r.js/build/tests/lib/namespace/modules/two.js +5 -0
  118. data/templates/app_root/scripts/r.js/build/tests/lib/nested/a.js +12 -0
  119. data/templates/app_root/scripts/r.js/build/tests/lib/nested/b.js +3 -0
  120. data/templates/app_root/scripts/r.js/build/tests/lib/nested/build.js +6 -0
  121. data/templates/app_root/scripts/r.js/build/tests/lib/nested/buildWithCE.js +7 -0
  122. data/templates/app_root/scripts/r.js/build/tests/lib/nested/c.js +3 -0
  123. data/templates/app_root/scripts/r.js/build/tests/lib/nested/d.js +3 -0
  124. data/templates/app_root/scripts/r.js/build/tests/lib/nested/e.js +3 -0
  125. data/templates/app_root/scripts/r.js/build/tests/lib/nested/expected-built.js +36 -0
  126. data/templates/app_root/scripts/r.js/build/tests/lib/nested/expected-builtWithCE.js +42 -0
  127. data/templates/app_root/scripts/r.js/build/tests/lib/nested/main.js +3 -0
  128. data/templates/app_root/scripts/r.js/build/tests/lib/nested/top.js +10 -0
  129. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/a.js +3 -0
  130. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/b.js +3 -0
  131. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/build.js +6 -0
  132. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/buildNeedAll.js +9 -0
  133. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/buildNeedB.js +11 -0
  134. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/buildNeedC.js +11 -0
  135. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/buildNeedD.js +11 -0
  136. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/buildNested.js +7 -0
  137. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/c.js +3 -0
  138. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/d.js +3 -0
  139. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/expected-built.js +21 -0
  140. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/expected-builtNeedAll.js +31 -0
  141. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/expected-builtNeedB.js +24 -0
  142. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/expected-builtNeedC.js +24 -0
  143. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/expected-builtNeedD.js +25 -0
  144. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/expected-builtNested.js +31 -0
  145. data/templates/app_root/scripts/r.js/build/tests/lib/nestedHas/main.js +17 -0
  146. data/templates/app_root/scripts/r.js/build/tests/lib/plugins/build.js +6 -0
  147. data/templates/app_root/scripts/r.js/build/tests/lib/plugins/converter.js +11 -0
  148. data/templates/app_root/scripts/r.js/build/tests/lib/plugins/main.js +6 -0
  149. data/templates/app_root/scripts/r.js/build/tests/lib/plugins/plug.js +29 -0
  150. data/templates/app_root/scripts/r.js/build/tests/lib/plugins/util.js +7 -0
  151. data/templates/app_root/scripts/r.js/build/tests/nameOnly.build.js +10 -0
  152. data/templates/app_root/scripts/r.js/build/tests/nodeAll.js +3 -0
  153. data/templates/app_root/scripts/r.js/build/tests/nodeOptimize.js +12 -0
  154. data/templates/app_root/scripts/r.js/build/tests/nodeOptimizeNoCallback.js +10 -0
  155. data/templates/app_root/scripts/r.js/build/tests/order.build.js +16 -0
  156. data/templates/app_root/scripts/r.js/build/tests/override/override.js +36 -0
  157. data/templates/app_root/scripts/r.js/build/tests/packages.build.js +59 -0
  158. data/templates/app_root/scripts/r.js/build/tests/parse.js +103 -0
  159. data/templates/app_root/scripts/r.js/build/tests/simple.build.js +13 -0
  160. data/templates/app_root/scripts/r.js/build/tests/simpleNamespace.build.js +17 -0
  161. data/templates/app_root/scripts/r.js/build/tests/start.frag +4 -0
  162. data/templates/app_root/scripts/r.js/build/tests/text.build.js +16 -0
  163. data/templates/app_root/scripts/r.js/build/tests/textExclude.build.js +16 -0
  164. data/templates/app_root/scripts/r.js/dist.js +95 -0
  165. data/templates/app_root/scripts/r.js/dist/README.md +6 -0
  166. data/templates/app_root/scripts/r.js/dist/r-1.0.0.js +9042 -0
  167. data/templates/app_root/scripts/r.js/dist/r-edge.js +9191 -0
  168. data/templates/app_root/scripts/r.js/dist/r.js +9099 -0
  169. data/templates/app_root/scripts/r.js/lib/closure/COPYING +202 -0
  170. data/templates/app_root/scripts/r.js/lib/closure/README +292 -0
  171. data/templates/app_root/scripts/r.js/lib/closure/compiler.jar +0 -0
  172. data/templates/app_root/scripts/r.js/lib/rhino/LICENSE +4 -0
  173. data/templates/app_root/scripts/r.js/lib/rhino/js.jar +0 -0
  174. data/templates/app_root/scripts/r.js/require.js +1952 -0
  175. data/templates/app_root/scripts/r.js/tasks.txt +2 -0
  176. data/templates/app_root/scripts/r.js/tests/all.js +49 -0
  177. data/templates/app_root/scripts/r.js/tests/allNode.js +3 -0
  178. data/templates/app_root/scripts/r.js/tests/allj.sh +1 -0
  179. data/templates/app_root/scripts/r.js/tests/alln.sh +2 -0
  180. data/templates/app_root/scripts/r.js/tests/alpha.js +3 -0
  181. data/templates/app_root/scripts/r.js/tests/beta.js +6 -0
  182. data/templates/app_root/scripts/r.js/tests/doh/LICENSE +195 -0
  183. data/templates/app_root/scripts/r.js/tests/doh/README +12 -0
  184. data/templates/app_root/scripts/r.js/tests/doh/_browserRunner.js +855 -0
  185. data/templates/app_root/scripts/r.js/tests/doh/_nodeRunner.js +20 -0
  186. data/templates/app_root/scripts/r.js/tests/doh/_rhinoRunner.js +17 -0
  187. data/templates/app_root/scripts/r.js/tests/doh/_sounds/LICENSE +10 -0
  188. data/templates/app_root/scripts/r.js/tests/doh/_sounds/doh.wav +0 -0
  189. data/templates/app_root/scripts/r.js/tests/doh/_sounds/dohaaa.wav +0 -0
  190. data/templates/app_root/scripts/r.js/tests/doh/_sounds/woohoo.wav +0 -0
  191. data/templates/app_root/scripts/r.js/tests/doh/runner.html +316 -0
  192. data/templates/app_root/scripts/r.js/tests/doh/runner.js +1499 -0
  193. data/templates/app_root/scripts/r.js/tests/doh/runner.sh +3 -0
  194. data/templates/app_root/scripts/r.js/tests/doh/small_logo.png +0 -0
  195. data/templates/app_root/scripts/r.js/tests/node/canvasTest.js +39 -0
  196. data/templates/app_root/scripts/r.js/tests/node/embedded/README.md +15 -0
  197. data/templates/app_root/scripts/r.js/tests/node/embedded/coffee/foo.coffee +1 -0
  198. data/templates/app_root/scripts/r.js/tests/node/embedded/main.js +13 -0
  199. data/templates/app_root/scripts/r.js/tests/node/embedded/scripts/bar.js +13 -0
  200. data/templates/app_root/scripts/r.js/tests/node/index.js +11 -0
  201. data/templates/app_root/scripts/r.js/tests/node/tests/alpha/foo.js +6 -0
  202. data/templates/app_root/scripts/r.js/tests/node/tests/alpha/hello.html +9 -0
  203. data/templates/app_root/scripts/r.js/tests/node/tests/foo.js +3 -0
  204. data/templates/app_root/scripts/r.js/tests/node/tests/server.js +11 -0
  205. data/templates/app_root/scripts/r.js/tests/relative.js +10 -0
  206. data/templates/app_root/scripts/r.js/tests/sub/betaSub.js +3 -0
  207. data/templates/application.js +6 -5
  208. data/templates/boot.js +42 -0
  209. data/templates/helper.js +9 -0
  210. data/templates/index.html +1 -1
  211. metadata +201 -11
  212. data/dist/jazz/main.js +0 -65
  213. data/dist/mustache/mustache.js +0 -324
  214. data/templates/app_root/config/routes.js +0 -4
  215. data/templates/glue.js +0 -4
@@ -0,0 +1,2005 @@
1
+ define(["require", "exports", "module", "./parse-js", "./squeeze-more"], function(require, exports, module) {
2
+
3
+ /***********************************************************************
4
+
5
+ A JavaScript tokenizer / parser / beautifier / compressor.
6
+
7
+ This version is suitable for Node.js. With minimal changes (the
8
+ exports stuff) it should work on any JS platform.
9
+
10
+ This file implements some AST processors. They work on data built
11
+ by parse-js.
12
+
13
+ Exported functions:
14
+
15
+ - ast_mangle(ast, options) -- mangles the variable/function names
16
+ in the AST. Returns an AST.
17
+
18
+ - ast_squeeze(ast) -- employs various optimizations to make the
19
+ final generated code even smaller. Returns an AST.
20
+
21
+ - gen_code(ast, options) -- generates JS code from the AST. Pass
22
+ true (or an object, see the code for some options) as second
23
+ argument to get "pretty" (indented) code.
24
+
25
+ -------------------------------- (C) ---------------------------------
26
+
27
+ Author: Mihai Bazon
28
+ <mihai.bazon@gmail.com>
29
+ http://mihai.bazon.net/blog
30
+
31
+ Distributed under the BSD license:
32
+
33
+ Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
34
+
35
+ Redistribution and use in source and binary forms, with or without
36
+ modification, are permitted provided that the following conditions
37
+ are met:
38
+
39
+ * Redistributions of source code must retain the above
40
+ copyright notice, this list of conditions and the following
41
+ disclaimer.
42
+
43
+ * Redistributions in binary form must reproduce the above
44
+ copyright notice, this list of conditions and the following
45
+ disclaimer in the documentation and/or other materials
46
+ provided with the distribution.
47
+
48
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
49
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
52
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
53
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
54
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
55
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
57
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
58
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59
+ SUCH DAMAGE.
60
+
61
+ ***********************************************************************/
62
+
63
+ var jsp = require("./parse-js"),
64
+ slice = jsp.slice,
65
+ member = jsp.member,
66
+ PRECEDENCE = jsp.PRECEDENCE,
67
+ OPERATORS = jsp.OPERATORS;
68
+
69
+ /* -----[ helper for AST traversal ]----- */
70
+
71
+ function ast_walker() {
72
+ function _vardefs(defs) {
73
+ return [ this[0], MAP(defs, function(def){
74
+ var a = [ def[0] ];
75
+ if (def.length > 1)
76
+ a[1] = walk(def[1]);
77
+ return a;
78
+ }) ];
79
+ };
80
+ function _block(statements) {
81
+ var out = [ this[0] ];
82
+ if (statements != null)
83
+ out.push(MAP(statements, walk));
84
+ return out;
85
+ };
86
+ var walkers = {
87
+ "string": function(str) {
88
+ return [ this[0], str ];
89
+ },
90
+ "num": function(num) {
91
+ return [ this[0], num ];
92
+ },
93
+ "name": function(name) {
94
+ return [ this[0], name ];
95
+ },
96
+ "toplevel": function(statements) {
97
+ return [ this[0], MAP(statements, walk) ];
98
+ },
99
+ "block": _block,
100
+ "splice": _block,
101
+ "var": _vardefs,
102
+ "const": _vardefs,
103
+ "try": function(t, c, f) {
104
+ return [
105
+ this[0],
106
+ MAP(t, walk),
107
+ c != null ? [ c[0], MAP(c[1], walk) ] : null,
108
+ f != null ? MAP(f, walk) : null
109
+ ];
110
+ },
111
+ "throw": function(expr) {
112
+ return [ this[0], walk(expr) ];
113
+ },
114
+ "new": function(ctor, args) {
115
+ return [ this[0], walk(ctor), MAP(args, walk) ];
116
+ },
117
+ "switch": function(expr, body) {
118
+ return [ this[0], walk(expr), MAP(body, function(branch){
119
+ return [ branch[0] ? walk(branch[0]) : null,
120
+ MAP(branch[1], walk) ];
121
+ }) ];
122
+ },
123
+ "break": function(label) {
124
+ return [ this[0], label ];
125
+ },
126
+ "continue": function(label) {
127
+ return [ this[0], label ];
128
+ },
129
+ "conditional": function(cond, t, e) {
130
+ return [ this[0], walk(cond), walk(t), walk(e) ];
131
+ },
132
+ "assign": function(op, lvalue, rvalue) {
133
+ return [ this[0], op, walk(lvalue), walk(rvalue) ];
134
+ },
135
+ "dot": function(expr) {
136
+ return [ this[0], walk(expr) ].concat(slice(arguments, 1));
137
+ },
138
+ "call": function(expr, args) {
139
+ return [ this[0], walk(expr), MAP(args, walk) ];
140
+ },
141
+ "function": function(name, args, body) {
142
+ return [ this[0], name, args.slice(), MAP(body, walk) ];
143
+ },
144
+ "defun": function(name, args, body) {
145
+ return [ this[0], name, args.slice(), MAP(body, walk) ];
146
+ },
147
+ "if": function(conditional, t, e) {
148
+ return [ this[0], walk(conditional), walk(t), walk(e) ];
149
+ },
150
+ "for": function(init, cond, step, block) {
151
+ return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
152
+ },
153
+ "for-in": function(vvar, key, hash, block) {
154
+ return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
155
+ },
156
+ "while": function(cond, block) {
157
+ return [ this[0], walk(cond), walk(block) ];
158
+ },
159
+ "do": function(cond, block) {
160
+ return [ this[0], walk(cond), walk(block) ];
161
+ },
162
+ "return": function(expr) {
163
+ return [ this[0], walk(expr) ];
164
+ },
165
+ "binary": function(op, left, right) {
166
+ return [ this[0], op, walk(left), walk(right) ];
167
+ },
168
+ "unary-prefix": function(op, expr) {
169
+ return [ this[0], op, walk(expr) ];
170
+ },
171
+ "unary-postfix": function(op, expr) {
172
+ return [ this[0], op, walk(expr) ];
173
+ },
174
+ "sub": function(expr, subscript) {
175
+ return [ this[0], walk(expr), walk(subscript) ];
176
+ },
177
+ "object": function(props) {
178
+ return [ this[0], MAP(props, function(p){
179
+ return p.length == 2
180
+ ? [ p[0], walk(p[1]) ]
181
+ : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
182
+ }) ];
183
+ },
184
+ "regexp": function(rx, mods) {
185
+ return [ this[0], rx, mods ];
186
+ },
187
+ "array": function(elements) {
188
+ return [ this[0], MAP(elements, walk) ];
189
+ },
190
+ "stat": function(stat) {
191
+ return [ this[0], walk(stat) ];
192
+ },
193
+ "seq": function() {
194
+ return [ this[0] ].concat(MAP(slice(arguments), walk));
195
+ },
196
+ "label": function(name, block) {
197
+ return [ this[0], name, walk(block) ];
198
+ },
199
+ "with": function(expr, block) {
200
+ return [ this[0], walk(expr), walk(block) ];
201
+ },
202
+ "atom": function(name) {
203
+ return [ this[0], name ];
204
+ }
205
+ };
206
+
207
+ var user = {};
208
+ var stack = [];
209
+ function walk(ast) {
210
+ if (ast == null)
211
+ return null;
212
+ try {
213
+ stack.push(ast);
214
+ var type = ast[0];
215
+ var gen = user[type];
216
+ if (gen) {
217
+ var ret = gen.apply(ast, ast.slice(1));
218
+ if (ret != null)
219
+ return ret;
220
+ }
221
+ gen = walkers[type];
222
+ return gen.apply(ast, ast.slice(1));
223
+ } finally {
224
+ stack.pop();
225
+ }
226
+ };
227
+
228
+ function dive(ast) {
229
+ if (ast == null)
230
+ return null;
231
+ try {
232
+ stack.push(ast);
233
+ return walkers[ast[0]].apply(ast, ast.slice(1));
234
+ } finally {
235
+ stack.pop();
236
+ }
237
+ };
238
+
239
+ function with_walkers(walkers, cont){
240
+ var save = {}, i;
241
+ for (i in walkers) if (HOP(walkers, i)) {
242
+ save[i] = user[i];
243
+ user[i] = walkers[i];
244
+ }
245
+ var ret = cont();
246
+ for (i in save) if (HOP(save, i)) {
247
+ if (!save[i]) delete user[i];
248
+ else user[i] = save[i];
249
+ }
250
+ return ret;
251
+ };
252
+
253
+ return {
254
+ walk: walk,
255
+ dive: dive,
256
+ with_walkers: with_walkers,
257
+ parent: function() {
258
+ return stack[stack.length - 2]; // last one is current node
259
+ },
260
+ stack: function() {
261
+ return stack;
262
+ }
263
+ };
264
+ };
265
+
266
+ /* -----[ Scope and mangling ]----- */
267
+
268
+ function Scope(parent) {
269
+ this.names = {}; // names defined in this scope
270
+ this.mangled = {}; // mangled names (orig.name => mangled)
271
+ this.rev_mangled = {}; // reverse lookup (mangled => orig.name)
272
+ this.cname = -1; // current mangled name
273
+ this.refs = {}; // names referenced from this scope
274
+ this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes
275
+ this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes
276
+ this.parent = parent; // parent scope
277
+ this.children = []; // sub-scopes
278
+ if (parent) {
279
+ this.level = parent.level + 1;
280
+ parent.children.push(this);
281
+ } else {
282
+ this.level = 0;
283
+ }
284
+ };
285
+
286
+ var base54 = (function(){
287
+ var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
288
+ return function(num) {
289
+ var ret = "";
290
+ do {
291
+ ret = DIGITS.charAt(num % 54) + ret;
292
+ num = Math.floor(num / 54);
293
+ } while (num > 0);
294
+ return ret;
295
+ };
296
+ })();
297
+
298
+ Scope.prototype = {
299
+ has: function(name) {
300
+ for (var s = this; s; s = s.parent)
301
+ if (HOP(s.names, name))
302
+ return s;
303
+ },
304
+ has_mangled: function(mname) {
305
+ for (var s = this; s; s = s.parent)
306
+ if (HOP(s.rev_mangled, mname))
307
+ return s;
308
+ },
309
+ toJSON: function() {
310
+ return {
311
+ names: this.names,
312
+ uses_eval: this.uses_eval,
313
+ uses_with: this.uses_with
314
+ };
315
+ },
316
+
317
+ next_mangled: function() {
318
+ // we must be careful that the new mangled name:
319
+ //
320
+ // 1. doesn't shadow a mangled name from a parent
321
+ // scope, unless we don't reference the original
322
+ // name from this scope OR from any sub-scopes!
323
+ // This will get slow.
324
+ //
325
+ // 2. doesn't shadow an original name from a parent
326
+ // scope, in the event that the name is not mangled
327
+ // in the parent scope and we reference that name
328
+ // here OR IN ANY SUBSCOPES!
329
+ //
330
+ // 3. doesn't shadow a name that is referenced but not
331
+ // defined (possibly global defined elsewhere).
332
+ for (;;) {
333
+ var m = base54(++this.cname), prior;
334
+
335
+ // case 1.
336
+ prior = this.has_mangled(m);
337
+ if (prior && this.refs[prior.rev_mangled[m]] === prior)
338
+ continue;
339
+
340
+ // case 2.
341
+ prior = this.has(m);
342
+ if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m))
343
+ continue;
344
+
345
+ // case 3.
346
+ if (HOP(this.refs, m) && this.refs[m] == null)
347
+ continue;
348
+
349
+ // I got "do" once. :-/
350
+ if (!is_identifier(m))
351
+ continue;
352
+
353
+ return m;
354
+ }
355
+ },
356
+ set_mangle: function(name, m) {
357
+ this.rev_mangled[m] = name;
358
+ return this.mangled[name] = m;
359
+ },
360
+ get_mangled: function(name, newMangle) {
361
+ if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use
362
+ var s = this.has(name);
363
+ if (!s) return name; // not in visible scope, no mangle
364
+ if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope
365
+ if (!newMangle) return name; // not found and no mangling requested
366
+ return s.set_mangle(name, s.next_mangled());
367
+ },
368
+ references: function(name) {
369
+ return name && !this.parent || this.uses_with || this.uses_eval || this.refs[name];
370
+ },
371
+ define: function(name, type) {
372
+ if (name != null) {
373
+ if (type == "var" || !HOP(this.names, name))
374
+ this.names[name] = type || "var";
375
+ return name;
376
+ }
377
+ }
378
+ };
379
+
380
+ function ast_add_scope(ast) {
381
+
382
+ var current_scope = null;
383
+ var w = ast_walker(), walk = w.walk;
384
+ var having_eval = [];
385
+
386
+ function with_new_scope(cont) {
387
+ current_scope = new Scope(current_scope);
388
+ var ret = current_scope.body = cont();
389
+ ret.scope = current_scope;
390
+ current_scope = current_scope.parent;
391
+ return ret;
392
+ };
393
+
394
+ function define(name, type) {
395
+ return current_scope.define(name, type);
396
+ };
397
+
398
+ function reference(name) {
399
+ current_scope.refs[name] = true;
400
+ };
401
+
402
+ function _lambda(name, args, body) {
403
+ var is_defun = this[0] == "defun";
404
+ return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){
405
+ if (!is_defun) define(name, "lambda");
406
+ MAP(args, function(name){ define(name, "arg") });
407
+ return MAP(body, walk);
408
+ })];
409
+ };
410
+
411
+ function _vardefs(type) {
412
+ return function(defs) {
413
+ MAP(defs, function(d){
414
+ define(d[0], type);
415
+ if (d[1]) reference(d[0]);
416
+ });
417
+ };
418
+ };
419
+
420
+ return with_new_scope(function(){
421
+ // process AST
422
+ var ret = w.with_walkers({
423
+ "function": _lambda,
424
+ "defun": _lambda,
425
+ "label": function(name, stat) { define(name, "label") },
426
+ "break": function(label) { if (label) reference(label) },
427
+ "continue": function(label) { if (label) reference(label) },
428
+ "with": function(expr, block) {
429
+ for (var s = current_scope; s; s = s.parent)
430
+ s.uses_with = true;
431
+ },
432
+ "var": _vardefs("var"),
433
+ "const": _vardefs("const"),
434
+ "try": function(t, c, f) {
435
+ if (c != null) return [
436
+ this[0],
437
+ MAP(t, walk),
438
+ [ define(c[0], "catch"), MAP(c[1], walk) ],
439
+ f != null ? MAP(f, walk) : null
440
+ ];
441
+ },
442
+ "name": function(name) {
443
+ if (name == "eval")
444
+ having_eval.push(current_scope);
445
+ reference(name);
446
+ }
447
+ }, function(){
448
+ return walk(ast);
449
+ });
450
+
451
+ // the reason why we need an additional pass here is
452
+ // that names can be used prior to their definition.
453
+
454
+ // scopes where eval was detected and their parents
455
+ // are marked with uses_eval, unless they define the
456
+ // "eval" name.
457
+ MAP(having_eval, function(scope){
458
+ if (!scope.has("eval")) while (scope) {
459
+ scope.uses_eval = true;
460
+ scope = scope.parent;
461
+ }
462
+ });
463
+
464
+ // for referenced names it might be useful to know
465
+ // their origin scope. current_scope here is the
466
+ // toplevel one.
467
+ function fixrefs(scope, i) {
468
+ // do children first; order shouldn't matter
469
+ for (i = scope.children.length; --i >= 0;)
470
+ fixrefs(scope.children[i]);
471
+ for (i in scope.refs) if (HOP(scope.refs, i)) {
472
+ // find origin scope and propagate the reference to origin
473
+ for (var origin = scope.has(i), s = scope; s; s = s.parent) {
474
+ s.refs[i] = origin;
475
+ if (s === origin) break;
476
+ }
477
+ }
478
+ };
479
+ fixrefs(current_scope);
480
+
481
+ return ret;
482
+ });
483
+
484
+ };
485
+
486
+ /* -----[ mangle names ]----- */
487
+
488
+ function ast_mangle(ast, options) {
489
+ var w = ast_walker(), walk = w.walk, scope;
490
+ options = options || {};
491
+
492
+ function get_mangled(name, newMangle) {
493
+ if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel
494
+ if (options.except && member(name, options.except))
495
+ return name;
496
+ return scope.get_mangled(name, newMangle);
497
+ };
498
+
499
+ function get_define(name) {
500
+ if (options.defines) {
501
+ // we always lookup a defined symbol for the current scope FIRST, so declared
502
+ // vars trump a DEFINE symbol, but if no such var is found, then match a DEFINE value
503
+ if (!scope.has(name)) {
504
+ if (HOP(options.defines, name)) {
505
+ return options.defines[name];
506
+ }
507
+ }
508
+ return null;
509
+ }
510
+ };
511
+
512
+ function _lambda(name, args, body) {
513
+ var is_defun = this[0] == "defun", extra;
514
+ if (name) {
515
+ if (is_defun) name = get_mangled(name);
516
+ else {
517
+ extra = {};
518
+ if (!(scope.uses_eval || scope.uses_with))
519
+ name = extra[name] = scope.next_mangled();
520
+ else
521
+ extra[name] = name;
522
+ }
523
+ }
524
+ body = with_scope(body.scope, function(){
525
+ args = MAP(args, function(name){ return get_mangled(name) });
526
+ return MAP(body, walk);
527
+ }, extra);
528
+ return [ this[0], name, args, body ];
529
+ };
530
+
531
+ function with_scope(s, cont, extra) {
532
+ var _scope = scope;
533
+ scope = s;
534
+ if (extra) for (var i in extra) if (HOP(extra, i)) {
535
+ s.set_mangle(i, extra[i]);
536
+ }
537
+ for (var i in s.names) if (HOP(s.names, i)) {
538
+ get_mangled(i, true);
539
+ }
540
+ var ret = cont();
541
+ ret.scope = s;
542
+ scope = _scope;
543
+ return ret;
544
+ };
545
+
546
+ function _vardefs(defs) {
547
+ return [ this[0], MAP(defs, function(d){
548
+ return [ get_mangled(d[0]), walk(d[1]) ];
549
+ }) ];
550
+ };
551
+
552
+ return w.with_walkers({
553
+ "function": _lambda,
554
+ "defun": function() {
555
+ // move function declarations to the top when
556
+ // they are not in some block.
557
+ var ast = _lambda.apply(this, arguments);
558
+ switch (w.parent()[0]) {
559
+ case "toplevel":
560
+ case "function":
561
+ case "defun":
562
+ return MAP.at_top(ast);
563
+ }
564
+ return ast;
565
+ },
566
+ "label": function(label, stat) { return [ this[0], get_mangled(label), walk(stat) ] },
567
+ "break": function(label) { if (label) return [ this[0], get_mangled(label) ] },
568
+ "continue": function(label) { if (label) return [ this[0], get_mangled(label) ] },
569
+ "var": _vardefs,
570
+ "const": _vardefs,
571
+ "name": function(name) {
572
+ return get_define(name) || [ this[0], get_mangled(name) ];
573
+ },
574
+ "try": function(t, c, f) {
575
+ return [ this[0],
576
+ MAP(t, walk),
577
+ c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
578
+ f != null ? MAP(f, walk) : null ];
579
+ },
580
+ "toplevel": function(body) {
581
+ var self = this;
582
+ return with_scope(self.scope, function(){
583
+ return [ self[0], MAP(body, walk) ];
584
+ });
585
+ }
586
+ }, function() {
587
+ return walk(ast_add_scope(ast));
588
+ });
589
+ };
590
+
591
+ /* -----[
592
+ - compress foo["bar"] into foo.bar,
593
+ - remove block brackets {} where possible
594
+ - join consecutive var declarations
595
+ - various optimizations for IFs:
596
+ - if (cond) foo(); else bar(); ==> cond?foo():bar();
597
+ - if (cond) foo(); ==> cond&&foo();
598
+ - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw
599
+ - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
600
+ ]----- */
601
+
602
+ var warn = function(){};
603
+
604
+ function best_of(ast1, ast2) {
605
+ return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
606
+ };
607
+
608
+ function last_stat(b) {
609
+ if (b[0] == "block" && b[1] && b[1].length > 0)
610
+ return b[1][b[1].length - 1];
611
+ return b;
612
+ }
613
+
614
+ function aborts(t) {
615
+ if (t) switch (last_stat(t)[0]) {
616
+ case "return":
617
+ case "break":
618
+ case "continue":
619
+ case "throw":
620
+ return true;
621
+ }
622
+ };
623
+
624
+ function boolean_expr(expr) {
625
+ return ( (expr[0] == "unary-prefix"
626
+ && member(expr[1], [ "!", "delete" ])) ||
627
+
628
+ (expr[0] == "binary"
629
+ && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
630
+
631
+ (expr[0] == "binary"
632
+ && member(expr[1], [ "&&", "||" ])
633
+ && boolean_expr(expr[2])
634
+ && boolean_expr(expr[3])) ||
635
+
636
+ (expr[0] == "conditional"
637
+ && boolean_expr(expr[2])
638
+ && boolean_expr(expr[3])) ||
639
+
640
+ (expr[0] == "assign"
641
+ && expr[1] === true
642
+ && boolean_expr(expr[3])) ||
643
+
644
+ (expr[0] == "seq"
645
+ && boolean_expr(expr[expr.length - 1]))
646
+ );
647
+ };
648
+
649
+ function empty(b) {
650
+ return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
651
+ };
652
+
653
+ function is_string(node) {
654
+ return (node[0] == "string" ||
655
+ node[0] == "unary-prefix" && node[1] == "typeof" ||
656
+ node[0] == "binary" && node[1] == "+" &&
657
+ (is_string(node[2]) || is_string(node[3])));
658
+ };
659
+
660
+ var when_constant = (function(){
661
+
662
+ var $NOT_CONSTANT = {};
663
+
664
+ // this can only evaluate constant expressions. If it finds anything
665
+ // not constant, it throws $NOT_CONSTANT.
666
+ function evaluate(expr) {
667
+ switch (expr[0]) {
668
+ case "string":
669
+ case "num":
670
+ return expr[1];
671
+ case "name":
672
+ case "atom":
673
+ switch (expr[1]) {
674
+ case "true": return true;
675
+ case "false": return false;
676
+ case "null": return null;
677
+ }
678
+ break;
679
+ case "unary-prefix":
680
+ switch (expr[1]) {
681
+ case "!": return !evaluate(expr[2]);
682
+ case "typeof": return typeof evaluate(expr[2]);
683
+ case "~": return ~evaluate(expr[2]);
684
+ case "-": return -evaluate(expr[2]);
685
+ case "+": return +evaluate(expr[2]);
686
+ }
687
+ break;
688
+ case "binary":
689
+ var left = expr[2], right = expr[3];
690
+ switch (expr[1]) {
691
+ case "&&" : return evaluate(left) && evaluate(right);
692
+ case "||" : return evaluate(left) || evaluate(right);
693
+ case "|" : return evaluate(left) | evaluate(right);
694
+ case "&" : return evaluate(left) & evaluate(right);
695
+ case "^" : return evaluate(left) ^ evaluate(right);
696
+ case "+" : return evaluate(left) + evaluate(right);
697
+ case "*" : return evaluate(left) * evaluate(right);
698
+ case "/" : return evaluate(left) / evaluate(right);
699
+ case "%" : return evaluate(left) % evaluate(right);
700
+ case "-" : return evaluate(left) - evaluate(right);
701
+ case "<<" : return evaluate(left) << evaluate(right);
702
+ case ">>" : return evaluate(left) >> evaluate(right);
703
+ case ">>>" : return evaluate(left) >>> evaluate(right);
704
+ case "==" : return evaluate(left) == evaluate(right);
705
+ case "===" : return evaluate(left) === evaluate(right);
706
+ case "!=" : return evaluate(left) != evaluate(right);
707
+ case "!==" : return evaluate(left) !== evaluate(right);
708
+ case "<" : return evaluate(left) < evaluate(right);
709
+ case "<=" : return evaluate(left) <= evaluate(right);
710
+ case ">" : return evaluate(left) > evaluate(right);
711
+ case ">=" : return evaluate(left) >= evaluate(right);
712
+ case "in" : return evaluate(left) in evaluate(right);
713
+ case "instanceof" : return evaluate(left) instanceof evaluate(right);
714
+ }
715
+ }
716
+ throw $NOT_CONSTANT;
717
+ };
718
+
719
+ return function(expr, yes, no) {
720
+ try {
721
+ var val = evaluate(expr), ast;
722
+ switch (typeof val) {
723
+ case "string": ast = [ "string", val ]; break;
724
+ case "number": ast = [ "num", val ]; break;
725
+ case "boolean": ast = [ "name", String(val) ]; break;
726
+ default: throw new Error("Can't handle constant of type: " + (typeof val));
727
+ }
728
+ return yes.call(expr, ast, val);
729
+ } catch(ex) {
730
+ if (ex === $NOT_CONSTANT) {
731
+ if (expr[0] == "binary"
732
+ && (expr[1] == "===" || expr[1] == "!==")
733
+ && ((is_string(expr[2]) && is_string(expr[3]))
734
+ || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) {
735
+ expr[1] = expr[1].substr(0, 2);
736
+ }
737
+ else if (no && expr[0] == "binary"
738
+ && (expr[1] == "||" || expr[1] == "&&")) {
739
+ // the whole expression is not constant but the lval may be...
740
+ try {
741
+ var lval = evaluate(expr[2]);
742
+ expr = ((expr[1] == "&&" && (lval ? expr[3] : lval)) ||
743
+ (expr[1] == "||" && (lval ? lval : expr[3])) ||
744
+ expr);
745
+ } catch(ex2) {
746
+ // IGNORE... lval is not constant
747
+ }
748
+ }
749
+ return no ? no.call(expr, expr) : null;
750
+ }
751
+ else throw ex;
752
+ }
753
+ };
754
+
755
+ })();
756
+
757
+ function warn_unreachable(ast) {
758
+ if (!empty(ast))
759
+ warn("Dropping unreachable code: " + gen_code(ast, true));
760
+ };
761
+
762
+ function prepare_ifs(ast) {
763
+ var w = ast_walker(), walk = w.walk;
764
+ // In this first pass, we rewrite ifs which abort with no else with an
765
+ // if-else. For example:
766
+ //
767
+ // if (x) {
768
+ // blah();
769
+ // return y;
770
+ // }
771
+ // foobar();
772
+ //
773
+ // is rewritten into:
774
+ //
775
+ // if (x) {
776
+ // blah();
777
+ // return y;
778
+ // } else {
779
+ // foobar();
780
+ // }
781
+ function redo_if(statements) {
782
+ statements = MAP(statements, walk);
783
+
784
+ for (var i = 0; i < statements.length; ++i) {
785
+ var fi = statements[i];
786
+ if (fi[0] != "if") continue;
787
+
788
+ if (fi[3] && walk(fi[3])) continue;
789
+
790
+ var t = walk(fi[2]);
791
+ if (!aborts(t)) continue;
792
+
793
+ var conditional = walk(fi[1]);
794
+
795
+ var e_body = statements.slice(i + 1);
796
+ var e = e_body.length == 1 ? e_body[0] : [ "block", e_body ];
797
+
798
+ var ret = statements.slice(0, i).concat([ [
799
+ fi[0], // "if"
800
+ conditional, // conditional
801
+ t, // then
802
+ e // else
803
+ ] ]);
804
+
805
+ return redo_if(ret);
806
+ }
807
+
808
+ return statements;
809
+ };
810
+
811
+ function redo_if_lambda(name, args, body) {
812
+ body = redo_if(body);
813
+ return [ this[0], name, args, body ];
814
+ };
815
+
816
+ function redo_if_block(statements) {
817
+ return [ this[0], statements != null ? redo_if(statements) : null ];
818
+ };
819
+
820
+ return w.with_walkers({
821
+ "defun": redo_if_lambda,
822
+ "function": redo_if_lambda,
823
+ "block": redo_if_block,
824
+ "splice": redo_if_block,
825
+ "toplevel": function(statements) {
826
+ return [ this[0], redo_if(statements) ];
827
+ },
828
+ "try": function(t, c, f) {
829
+ return [
830
+ this[0],
831
+ redo_if(t),
832
+ c != null ? [ c[0], redo_if(c[1]) ] : null,
833
+ f != null ? redo_if(f) : null
834
+ ];
835
+ }
836
+ }, function() {
837
+ return walk(ast);
838
+ });
839
+ };
840
+
841
+ function for_side_effects(ast, handler) {
842
+ var w = ast_walker(), walk = w.walk;
843
+ var $stop = {}, $restart = {};
844
+ function stop() { throw $stop };
845
+ function restart() { throw $restart };
846
+ function found(){ return handler.call(this, this, w, stop, restart) };
847
+ function unary(op) {
848
+ if (op == "++" || op == "--")
849
+ return found.apply(this, arguments);
850
+ };
851
+ return w.with_walkers({
852
+ "try": found,
853
+ "throw": found,
854
+ "return": found,
855
+ "new": found,
856
+ "switch": found,
857
+ "break": found,
858
+ "continue": found,
859
+ "assign": found,
860
+ "call": found,
861
+ "if": found,
862
+ "for": found,
863
+ "for-in": found,
864
+ "while": found,
865
+ "do": found,
866
+ "return": found,
867
+ "unary-prefix": unary,
868
+ "unary-postfix": unary,
869
+ "defun": found
870
+ }, function(){
871
+ while (true) try {
872
+ walk(ast);
873
+ break;
874
+ } catch(ex) {
875
+ if (ex === $stop) break;
876
+ if (ex === $restart) continue;
877
+ throw ex;
878
+ }
879
+ });
880
+ };
881
+
882
+ function ast_lift_variables(ast) {
883
+ var w = ast_walker(), walk = w.walk, scope;
884
+ function do_body(body, env) {
885
+ var _scope = scope;
886
+ scope = env;
887
+ body = MAP(body, walk);
888
+ var hash = {}, names = MAP(env.names, function(type, name){
889
+ if (type != "var") return MAP.skip;
890
+ if (!env.references(name)) return MAP.skip;
891
+ hash[name] = true;
892
+ return [ name ];
893
+ });
894
+ if (names.length > 0) {
895
+ // looking for assignments to any of these variables.
896
+ // we can save considerable space by moving the definitions
897
+ // in the var declaration.
898
+ for_side_effects([ "block", body ], function(ast, walker, stop, restart) {
899
+ if (ast[0] == "assign"
900
+ && ast[1] === true
901
+ && ast[2][0] == "name"
902
+ && HOP(hash, ast[2][1])) {
903
+ // insert the definition into the var declaration
904
+ for (var i = names.length; --i >= 0;) {
905
+ if (names[i][0] == ast[2][1]) {
906
+ if (names[i][1]) // this name already defined, we must stop
907
+ stop();
908
+ names[i][1] = ast[3]; // definition
909
+ names.push(names.splice(i, 1)[0]);
910
+ break;
911
+ }
912
+ }
913
+ // remove this assignment from the AST.
914
+ var p = walker.parent();
915
+ if (p[0] == "seq") {
916
+ var a = p[2];
917
+ a.unshift(0, p.length);
918
+ p.splice.apply(p, a);
919
+ }
920
+ else if (p[0] == "stat") {
921
+ p.splice(0, p.length, "block"); // empty statement
922
+ }
923
+ else {
924
+ stop();
925
+ }
926
+ restart();
927
+ }
928
+ stop();
929
+ });
930
+ body.unshift([ "var", names ]);
931
+ }
932
+ scope = _scope;
933
+ return body;
934
+ };
935
+ function _vardefs(defs) {
936
+ var ret = null;
937
+ for (var i = defs.length; --i >= 0;) {
938
+ var d = defs[i];
939
+ if (!d[1]) continue;
940
+ d = [ "assign", true, [ "name", d[0] ], d[1] ];
941
+ if (ret == null) ret = d;
942
+ else ret = [ "seq", d, ret ];
943
+ }
944
+ if (ret == null) {
945
+ if (w.parent()[0] == "for-in")
946
+ return [ "name", defs[0][0] ];
947
+ return MAP.skip;
948
+ }
949
+ return [ "stat", ret ];
950
+ };
951
+ function _toplevel(body) {
952
+ return [ this[0], do_body(body, this.scope) ];
953
+ };
954
+ return w.with_walkers({
955
+ "function": function(name, args, body){
956
+ for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
957
+ args.pop();
958
+ if (!body.scope.references(name)) name = null;
959
+ return [ this[0], name, args, do_body(body, body.scope) ];
960
+ },
961
+ "defun": function(name, args, body){
962
+ if (!scope.references(name)) return MAP.skip;
963
+ for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
964
+ args.pop();
965
+ return [ this[0], name, args, do_body(body, body.scope) ];
966
+ },
967
+ "var": _vardefs,
968
+ "toplevel": _toplevel
969
+ }, function(){
970
+ return walk(ast_add_scope(ast));
971
+ });
972
+ };
973
+
974
+ function ast_squeeze(ast, options) {
975
+ options = defaults(options, {
976
+ make_seqs : true,
977
+ dead_code : true,
978
+ no_warnings : false,
979
+ keep_comps : true
980
+ });
981
+
982
+ var w = ast_walker(), walk = w.walk, scope;
983
+
984
+ function negate(c) {
985
+ var not_c = [ "unary-prefix", "!", c ];
986
+ switch (c[0]) {
987
+ case "unary-prefix":
988
+ return c[1] == "!" && boolean_expr(c[2]) ? c[2] : not_c;
989
+ case "seq":
990
+ c = slice(c);
991
+ c[c.length - 1] = negate(c[c.length - 1]);
992
+ return c;
993
+ case "conditional":
994
+ return best_of(not_c, [ "conditional", c[1], negate(c[2]), negate(c[3]) ]);
995
+ case "binary":
996
+ var op = c[1], left = c[2], right = c[3];
997
+ if (!options.keep_comps) switch (op) {
998
+ case "<=" : return [ "binary", ">", left, right ];
999
+ case "<" : return [ "binary", ">=", left, right ];
1000
+ case ">=" : return [ "binary", "<", left, right ];
1001
+ case ">" : return [ "binary", "<=", left, right ];
1002
+ }
1003
+ switch (op) {
1004
+ case "==" : return [ "binary", "!=", left, right ];
1005
+ case "!=" : return [ "binary", "==", left, right ];
1006
+ case "===" : return [ "binary", "!==", left, right ];
1007
+ case "!==" : return [ "binary", "===", left, right ];
1008
+ case "&&" : return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
1009
+ case "||" : return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
1010
+ }
1011
+ break;
1012
+ }
1013
+ return not_c;
1014
+ };
1015
+
1016
+ function make_conditional(c, t, e) {
1017
+ var make_real_conditional = function() {
1018
+ if (c[0] == "unary-prefix" && c[1] == "!") {
1019
+ return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
1020
+ } else {
1021
+ return e ? best_of(
1022
+ [ "conditional", c, t, e ],
1023
+ [ "conditional", negate(c), e, t ]
1024
+ ) : [ "binary", "&&", c, t ];
1025
+ }
1026
+ };
1027
+ // shortcut the conditional if the expression has a constant value
1028
+ return when_constant(c, function(ast, val){
1029
+ warn_unreachable(val ? e : t);
1030
+ return (val ? t : e);
1031
+ }, make_real_conditional);
1032
+ };
1033
+
1034
+ function with_scope(s, cont) {
1035
+ var _scope = scope;
1036
+ scope = s;
1037
+ var ret = cont();
1038
+ ret.scope = s;
1039
+ scope = _scope;
1040
+ return ret;
1041
+ };
1042
+
1043
+ function rmblock(block) {
1044
+ if (block != null && block[0] == "block" && block[1]) {
1045
+ if (block[1].length == 1)
1046
+ block = block[1][0];
1047
+ else if (block[1].length == 0)
1048
+ block = [ "block" ];
1049
+ }
1050
+ return block;
1051
+ };
1052
+
1053
+ function _lambda(name, args, body) {
1054
+ var is_defun = this[0] == "defun";
1055
+ body = with_scope(body.scope, function(){
1056
+ var ret = tighten(body, "lambda");
1057
+ if (!is_defun && name && !scope.references(name))
1058
+ name = null;
1059
+ return ret;
1060
+ });
1061
+ return [ this[0], name, args, body ];
1062
+ };
1063
+
1064
+ // this function does a few things:
1065
+ // 1. discard useless blocks
1066
+ // 2. join consecutive var declarations
1067
+ // 3. remove obviously dead code
1068
+ // 4. transform consecutive statements using the comma operator
1069
+ // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... }
1070
+ function tighten(statements, block_type) {
1071
+ statements = MAP(statements, walk);
1072
+
1073
+ statements = statements.reduce(function(a, stat){
1074
+ if (stat[0] == "block") {
1075
+ if (stat[1]) {
1076
+ a.push.apply(a, stat[1]);
1077
+ }
1078
+ } else {
1079
+ a.push(stat);
1080
+ }
1081
+ return a;
1082
+ }, []);
1083
+
1084
+ statements = (function(a, prev){
1085
+ statements.forEach(function(cur){
1086
+ if (prev && ((cur[0] == "var" && prev[0] == "var") ||
1087
+ (cur[0] == "const" && prev[0] == "const"))) {
1088
+ prev[1] = prev[1].concat(cur[1]);
1089
+ } else {
1090
+ a.push(cur);
1091
+ prev = cur;
1092
+ }
1093
+ });
1094
+ return a;
1095
+ })([]);
1096
+
1097
+ if (options.dead_code) statements = (function(a, has_quit){
1098
+ statements.forEach(function(st){
1099
+ if (has_quit) {
1100
+ if (st[0] == "function" || st[0] == "defun") {
1101
+ a.push(st);
1102
+ }
1103
+ else if (st[0] == "var" || st[0] == "const") {
1104
+ if (!options.no_warnings)
1105
+ warn("Variables declared in unreachable code");
1106
+ st[1] = MAP(st[1], function(def){
1107
+ if (def[1] && !options.no_warnings)
1108
+ warn_unreachable([ "assign", true, [ "name", def[0] ], def[1] ]);
1109
+ return [ def[0] ];
1110
+ });
1111
+ a.push(st);
1112
+ }
1113
+ else if (!options.no_warnings)
1114
+ warn_unreachable(st);
1115
+ }
1116
+ else {
1117
+ a.push(st);
1118
+ if (member(st[0], [ "return", "throw", "break", "continue" ]))
1119
+ has_quit = true;
1120
+ }
1121
+ });
1122
+ return a;
1123
+ })([]);
1124
+
1125
+ if (options.make_seqs) statements = (function(a, prev) {
1126
+ statements.forEach(function(cur){
1127
+ if (prev && prev[0] == "stat" && cur[0] == "stat") {
1128
+ prev[1] = [ "seq", prev[1], cur[1] ];
1129
+ } else {
1130
+ a.push(cur);
1131
+ prev = cur;
1132
+ }
1133
+ });
1134
+ if (a.length >= 2
1135
+ && a[a.length-2][0] == "stat"
1136
+ && (a[a.length-1][0] == "return" || a[a.length-1][0] == "throw")
1137
+ && a[a.length-1][1])
1138
+ {
1139
+ a.splice(a.length - 2, 2,
1140
+ [ a[a.length-1][0],
1141
+ [ "seq", a[a.length-2][1], a[a.length-1][1] ]]);
1142
+ }
1143
+ return a;
1144
+ })([]);
1145
+
1146
+ // this increases jQuery by 1K. Probably not such a good idea after all..
1147
+ // part of this is done in prepare_ifs anyway.
1148
+ // if (block_type == "lambda") statements = (function(i, a, stat){
1149
+ // while (i < statements.length) {
1150
+ // stat = statements[i++];
1151
+ // if (stat[0] == "if" && !stat[3]) {
1152
+ // if (stat[2][0] == "return" && stat[2][1] == null) {
1153
+ // a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
1154
+ // break;
1155
+ // }
1156
+ // var last = last_stat(stat[2]);
1157
+ // if (last[0] == "return" && last[1] == null) {
1158
+ // a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
1159
+ // break;
1160
+ // }
1161
+ // }
1162
+ // a.push(stat);
1163
+ // }
1164
+ // return a;
1165
+ // })(0, []);
1166
+
1167
+ return statements;
1168
+ };
1169
+
1170
+ function make_if(c, t, e) {
1171
+ return when_constant(c, function(ast, val){
1172
+ if (val) {
1173
+ t = walk(t);
1174
+ warn_unreachable(e);
1175
+ return t || [ "block" ];
1176
+ } else {
1177
+ e = walk(e);
1178
+ warn_unreachable(t);
1179
+ return e || [ "block" ];
1180
+ }
1181
+ }, function() {
1182
+ return make_real_if(c, t, e);
1183
+ });
1184
+ };
1185
+
1186
+ function make_real_if(c, t, e) {
1187
+ c = walk(c);
1188
+ t = walk(t);
1189
+ e = walk(e);
1190
+
1191
+ if (empty(t)) {
1192
+ c = negate(c);
1193
+ t = e;
1194
+ e = null;
1195
+ } else if (empty(e)) {
1196
+ e = null;
1197
+ } else {
1198
+ // if we have both else and then, maybe it makes sense to switch them?
1199
+ (function(){
1200
+ var a = gen_code(c);
1201
+ var n = negate(c);
1202
+ var b = gen_code(n);
1203
+ if (b.length < a.length) {
1204
+ var tmp = t;
1205
+ t = e;
1206
+ e = tmp;
1207
+ c = n;
1208
+ }
1209
+ })();
1210
+ }
1211
+ if (empty(e) && empty(t))
1212
+ return [ "stat", c ];
1213
+ var ret = [ "if", c, t, e ];
1214
+ if (t[0] == "if" && empty(t[3]) && empty(e)) {
1215
+ ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ]));
1216
+ }
1217
+ else if (t[0] == "stat") {
1218
+ if (e) {
1219
+ if (e[0] == "stat") {
1220
+ ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]);
1221
+ }
1222
+ }
1223
+ else {
1224
+ ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]);
1225
+ }
1226
+ }
1227
+ else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw") && t[1] && e[1]) {
1228
+ ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]);
1229
+ }
1230
+ else if (e && aborts(t)) {
1231
+ ret = [ [ "if", c, t ] ];
1232
+ if (e[0] == "block") {
1233
+ if (e[1]) ret = ret.concat(e[1]);
1234
+ }
1235
+ else {
1236
+ ret.push(e);
1237
+ }
1238
+ ret = walk([ "block", ret ]);
1239
+ }
1240
+ else if (t && aborts(e)) {
1241
+ ret = [ [ "if", negate(c), e ] ];
1242
+ if (t[0] == "block") {
1243
+ if (t[1]) ret = ret.concat(t[1]);
1244
+ } else {
1245
+ ret.push(t);
1246
+ }
1247
+ ret = walk([ "block", ret ]);
1248
+ }
1249
+ return ret;
1250
+ };
1251
+
1252
+ function _do_while(cond, body) {
1253
+ return when_constant(cond, function(cond, val){
1254
+ if (!val) {
1255
+ warn_unreachable(body);
1256
+ return [ "block" ];
1257
+ } else {
1258
+ return [ "for", null, null, null, walk(body) ];
1259
+ }
1260
+ });
1261
+ };
1262
+
1263
+ return w.with_walkers({
1264
+ "sub": function(expr, subscript) {
1265
+ if (subscript[0] == "string") {
1266
+ var name = subscript[1];
1267
+ if (is_identifier(name))
1268
+ return [ "dot", walk(expr), name ];
1269
+ else if (/^[1-9][0-9]*$/.test(name) || name === "0")
1270
+ return [ "sub", walk(expr), [ "num", parseInt(name, 10) ] ];
1271
+ }
1272
+ },
1273
+ "if": make_if,
1274
+ "toplevel": function(body) {
1275
+ return [ "toplevel", with_scope(this.scope, function(){
1276
+ return tighten(body);
1277
+ }) ];
1278
+ },
1279
+ "switch": function(expr, body) {
1280
+ var last = body.length - 1;
1281
+ return [ "switch", walk(expr), MAP(body, function(branch, i){
1282
+ var block = tighten(branch[1]);
1283
+ if (i == last && block.length > 0) {
1284
+ var node = block[block.length - 1];
1285
+ if (node[0] == "break" && !node[1])
1286
+ block.pop();
1287
+ }
1288
+ return [ branch[0] ? walk(branch[0]) : null, block ];
1289
+ }) ];
1290
+ },
1291
+ "function": _lambda,
1292
+ "defun": _lambda,
1293
+ "block": function(body) {
1294
+ if (body) return rmblock([ "block", tighten(body) ]);
1295
+ },
1296
+ "binary": function(op, left, right) {
1297
+ return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
1298
+ return best_of(walk(c), this);
1299
+ }, function no() {
1300
+ return function(){
1301
+ if(op != "==" && op != "!=") return;
1302
+ var l = walk(left), r = walk(right);
1303
+ if(l && l[0] == "unary-prefix" && l[1] == "!" && l[2][0] == "num")
1304
+ left = ['num', +!l[2][1]];
1305
+ else if (r && r[0] == "unary-prefix" && r[1] == "!" && r[2][0] == "num")
1306
+ right = ['num', +!r[2][1]];
1307
+ return ["binary", op, left, right];
1308
+ }() || this;
1309
+ });
1310
+ },
1311
+ "conditional": function(c, t, e) {
1312
+ return make_conditional(walk(c), walk(t), walk(e));
1313
+ },
1314
+ "try": function(t, c, f) {
1315
+ return [
1316
+ "try",
1317
+ tighten(t),
1318
+ c != null ? [ c[0], tighten(c[1]) ] : null,
1319
+ f != null ? tighten(f) : null
1320
+ ];
1321
+ },
1322
+ "unary-prefix": function(op, expr) {
1323
+ expr = walk(expr);
1324
+ var ret = [ "unary-prefix", op, expr ];
1325
+ if (op == "!")
1326
+ ret = best_of(ret, negate(expr));
1327
+ return when_constant(ret, function(ast, val){
1328
+ return walk(ast); // it's either true or false, so minifies to !0 or !1
1329
+ }, function() { return ret });
1330
+ },
1331
+ "name": function(name) {
1332
+ switch (name) {
1333
+ case "true": return [ "unary-prefix", "!", [ "num", 0 ]];
1334
+ case "false": return [ "unary-prefix", "!", [ "num", 1 ]];
1335
+ }
1336
+ },
1337
+ "while": _do_while,
1338
+ "assign": function(op, lvalue, rvalue) {
1339
+ lvalue = walk(lvalue);
1340
+ rvalue = walk(rvalue);
1341
+ var okOps = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
1342
+ if (op === true && lvalue[0] === "name" && rvalue[0] === "binary" &&
1343
+ ~okOps.indexOf(rvalue[1]) && rvalue[2][0] === "name" &&
1344
+ rvalue[2][1] === lvalue[1]) {
1345
+ return [ this[0], rvalue[1], lvalue, rvalue[3] ]
1346
+ }
1347
+ return [ this[0], op, lvalue, rvalue ];
1348
+ }
1349
+ }, function() {
1350
+ for (var i = 0; i < 2; ++i) {
1351
+ ast = prepare_ifs(ast);
1352
+ ast = ast_add_scope(ast);
1353
+ ast = walk(ast);
1354
+ }
1355
+ return ast;
1356
+ });
1357
+ };
1358
+
1359
+ /* -----[ re-generate code from the AST ]----- */
1360
+
1361
+ var DOT_CALL_NO_PARENS = jsp.array_to_hash([
1362
+ "name",
1363
+ "array",
1364
+ "object",
1365
+ "string",
1366
+ "dot",
1367
+ "sub",
1368
+ "call",
1369
+ "regexp",
1370
+ "defun"
1371
+ ]);
1372
+
1373
+ function make_string(str, ascii_only) {
1374
+ var dq = 0, sq = 0;
1375
+ str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
1376
+ switch (s) {
1377
+ case "\\": return "\\\\";
1378
+ case "\b": return "\\b";
1379
+ case "\f": return "\\f";
1380
+ case "\n": return "\\n";
1381
+ case "\r": return "\\r";
1382
+ case "\t": return "\\t";
1383
+ case "\u2028": return "\\u2028";
1384
+ case "\u2029": return "\\u2029";
1385
+ case '"': ++dq; return '"';
1386
+ case "'": ++sq; return "'";
1387
+ case "\0": return "\\0";
1388
+ }
1389
+ return s;
1390
+ });
1391
+ if (ascii_only) str = to_ascii(str);
1392
+ if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
1393
+ else return '"' + str.replace(/\x22/g, '\\"') + '"';
1394
+ };
1395
+
1396
+ function to_ascii(str) {
1397
+ return str.replace(/[\u0080-\uffff]/g, function(ch) {
1398
+ var code = ch.charCodeAt(0).toString(16);
1399
+ while (code.length < 4) code = "0" + code;
1400
+ return "\\u" + code;
1401
+ });
1402
+ };
1403
+
1404
+ var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]);
1405
+
1406
+ function gen_code(ast, options) {
1407
+ options = defaults(options, {
1408
+ indent_start : 0,
1409
+ indent_level : 4,
1410
+ quote_keys : false,
1411
+ space_colon : false,
1412
+ beautify : false,
1413
+ ascii_only : false,
1414
+ inline_script: false
1415
+ });
1416
+ var beautify = !!options.beautify;
1417
+ var indentation = 0,
1418
+ newline = beautify ? "\n" : "",
1419
+ space = beautify ? " " : "";
1420
+
1421
+ function encode_string(str) {
1422
+ var ret = make_string(str, options.ascii_only);
1423
+ if (options.inline_script)
1424
+ ret = ret.replace(/<\x2fscript([>/\t\n\f\r ])/gi, "<\\/script$1");
1425
+ return ret;
1426
+ };
1427
+
1428
+ function make_name(name) {
1429
+ name = name.toString();
1430
+ if (options.ascii_only)
1431
+ name = to_ascii(name);
1432
+ return name;
1433
+ };
1434
+
1435
+ function indent(line) {
1436
+ if (line == null)
1437
+ line = "";
1438
+ if (beautify)
1439
+ line = repeat_string(" ", options.indent_start + indentation * options.indent_level) + line;
1440
+ return line;
1441
+ };
1442
+
1443
+ function with_indent(cont, incr) {
1444
+ if (incr == null) incr = 1;
1445
+ indentation += incr;
1446
+ try { return cont.apply(null, slice(arguments, 1)); }
1447
+ finally { indentation -= incr; }
1448
+ };
1449
+
1450
+ function add_spaces(a) {
1451
+ if (beautify)
1452
+ return a.join(" ");
1453
+ var b = [];
1454
+ for (var i = 0; i < a.length; ++i) {
1455
+ var next = a[i + 1];
1456
+ b.push(a[i]);
1457
+ if (next &&
1458
+ ((/[a-z0-9_\x24]$/i.test(a[i].toString()) && /^[a-z0-9_\x24]/i.test(next.toString())) ||
1459
+ (/[\+\-]$/.test(a[i].toString()) && /^[\+\-]/.test(next.toString())))) {
1460
+ b.push(" ");
1461
+ }
1462
+ }
1463
+ return b.join("");
1464
+ };
1465
+
1466
+ function add_commas(a) {
1467
+ return a.join("," + space);
1468
+ };
1469
+
1470
+ function parenthesize(expr) {
1471
+ var gen = make(expr);
1472
+ for (var i = 1; i < arguments.length; ++i) {
1473
+ var el = arguments[i];
1474
+ if ((el instanceof Function && el(expr)) || expr[0] == el)
1475
+ return "(" + gen + ")";
1476
+ }
1477
+ return gen;
1478
+ };
1479
+
1480
+ function best_of(a) {
1481
+ if (a.length == 1) {
1482
+ return a[0];
1483
+ }
1484
+ if (a.length == 2) {
1485
+ var b = a[1];
1486
+ a = a[0];
1487
+ return a.length <= b.length ? a : b;
1488
+ }
1489
+ return best_of([ a[0], best_of(a.slice(1)) ]);
1490
+ };
1491
+
1492
+ function needs_parens(expr) {
1493
+ if (expr[0] == "function" || expr[0] == "object") {
1494
+ // dot/call on a literal function requires the
1495
+ // function literal itself to be parenthesized
1496
+ // only if it's the first "thing" in a
1497
+ // statement. This means that the parent is
1498
+ // "stat", but it could also be a "seq" and
1499
+ // we're the first in this "seq" and the
1500
+ // parent is "stat", and so on. Messy stuff,
1501
+ // but it worths the trouble.
1502
+ var a = slice(w.stack()), self = a.pop(), p = a.pop();
1503
+ while (p) {
1504
+ if (p[0] == "stat") return true;
1505
+ if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) ||
1506
+ ((p[0] == "binary" || p[0] == "assign" || p[0] == "unary-postfix") && p[2] === self)) {
1507
+ self = p;
1508
+ p = a.pop();
1509
+ } else {
1510
+ return false;
1511
+ }
1512
+ }
1513
+ }
1514
+ return !HOP(DOT_CALL_NO_PARENS, expr[0]);
1515
+ };
1516
+
1517
+ function make_num(num) {
1518
+ var str = num.toString(10), a = [ str.replace(/^0\./, ".") ], m;
1519
+ if (Math.floor(num) === num) {
1520
+ if (num >= 0) {
1521
+ a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
1522
+ "0" + num.toString(8)); // same.
1523
+ } else {
1524
+ a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
1525
+ "-0" + (-num).toString(8)); // same.
1526
+ }
1527
+ if ((m = /^(.*?)(0+)$/.exec(num))) {
1528
+ a.push(m[1] + "e" + m[2].length);
1529
+ }
1530
+ } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
1531
+ a.push(m[2] + "e-" + (m[1].length + m[2].length),
1532
+ str.substr(str.indexOf(".")));
1533
+ }
1534
+ return best_of(a);
1535
+ };
1536
+
1537
+ var w = ast_walker();
1538
+ var make = w.walk;
1539
+ return w.with_walkers({
1540
+ "string": encode_string,
1541
+ "num": make_num,
1542
+ "name": make_name,
1543
+ "toplevel": function(statements) {
1544
+ return make_block_statements(statements)
1545
+ .join(newline + newline);
1546
+ },
1547
+ "splice": function(statements) {
1548
+ var parent = w.parent();
1549
+ if (HOP(SPLICE_NEEDS_BRACKETS, parent)) {
1550
+ // we need block brackets in this case
1551
+ return make_block.apply(this, arguments);
1552
+ } else {
1553
+ return MAP(make_block_statements(statements, true),
1554
+ function(line, i) {
1555
+ // the first line is already indented
1556
+ return i > 0 ? indent(line) : line;
1557
+ }).join(newline);
1558
+ }
1559
+ },
1560
+ "block": make_block,
1561
+ "var": function(defs) {
1562
+ return "var " + add_commas(MAP(defs, make_1vardef)) + ";";
1563
+ },
1564
+ "const": function(defs) {
1565
+ return "const " + add_commas(MAP(defs, make_1vardef)) + ";";
1566
+ },
1567
+ "try": function(tr, ca, fi) {
1568
+ var out = [ "try", make_block(tr) ];
1569
+ if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1]));
1570
+ if (fi) out.push("finally", make_block(fi));
1571
+ return add_spaces(out);
1572
+ },
1573
+ "throw": function(expr) {
1574
+ return add_spaces([ "throw", make(expr) ]) + ";";
1575
+ },
1576
+ "new": function(ctor, args) {
1577
+ args = args.length > 0 ? "(" + add_commas(MAP(args, function(expr){
1578
+ return parenthesize(expr, "seq");
1579
+ })) + ")" : "";
1580
+ return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){
1581
+ var w = ast_walker(), has_call = {};
1582
+ try {
1583
+ w.with_walkers({
1584
+ "call": function() { throw has_call },
1585
+ "function": function() { return this }
1586
+ }, function(){
1587
+ w.walk(expr);
1588
+ });
1589
+ } catch(ex) {
1590
+ if (ex === has_call)
1591
+ return true;
1592
+ throw ex;
1593
+ }
1594
+ }) + args ]);
1595
+ },
1596
+ "switch": function(expr, body) {
1597
+ return add_spaces([ "switch", "(" + make(expr) + ")", make_switch_block(body) ]);
1598
+ },
1599
+ "break": function(label) {
1600
+ var out = "break";
1601
+ if (label != null)
1602
+ out += " " + make_name(label);
1603
+ return out + ";";
1604
+ },
1605
+ "continue": function(label) {
1606
+ var out = "continue";
1607
+ if (label != null)
1608
+ out += " " + make_name(label);
1609
+ return out + ";";
1610
+ },
1611
+ "conditional": function(co, th, el) {
1612
+ return add_spaces([ parenthesize(co, "assign", "seq", "conditional"), "?",
1613
+ parenthesize(th, "seq"), ":",
1614
+ parenthesize(el, "seq") ]);
1615
+ },
1616
+ "assign": function(op, lvalue, rvalue) {
1617
+ if (op && op !== true) op += "=";
1618
+ else op = "=";
1619
+ return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]);
1620
+ },
1621
+ "dot": function(expr) {
1622
+ var out = make(expr), i = 1;
1623
+ if (expr[0] == "num") {
1624
+ if (!/\./.test(expr[1]))
1625
+ out += ".";
1626
+ } else if (needs_parens(expr))
1627
+ out = "(" + out + ")";
1628
+ while (i < arguments.length)
1629
+ out += "." + make_name(arguments[i++]);
1630
+ return out;
1631
+ },
1632
+ "call": function(func, args) {
1633
+ var f = make(func);
1634
+ if (f.charAt(0) != "(" && needs_parens(func))
1635
+ f = "(" + f + ")";
1636
+ return f + "(" + add_commas(MAP(args, function(expr){
1637
+ return parenthesize(expr, "seq");
1638
+ })) + ")";
1639
+ },
1640
+ "function": make_function,
1641
+ "defun": make_function,
1642
+ "if": function(co, th, el) {
1643
+ var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ];
1644
+ if (el) {
1645
+ out.push("else", make(el));
1646
+ }
1647
+ return add_spaces(out);
1648
+ },
1649
+ "for": function(init, cond, step, block) {
1650
+ var out = [ "for" ];
1651
+ init = (init != null ? make(init) : "").replace(/;*\s*$/, ";" + space);
1652
+ cond = (cond != null ? make(cond) : "").replace(/;*\s*$/, ";" + space);
1653
+ step = (step != null ? make(step) : "").replace(/;*\s*$/, "");
1654
+ var args = init + cond + step;
1655
+ if (args == "; ; ") args = ";;";
1656
+ out.push("(" + args + ")", make(block));
1657
+ return add_spaces(out);
1658
+ },
1659
+ "for-in": function(vvar, key, hash, block) {
1660
+ return add_spaces([ "for", "(" +
1661
+ (vvar ? make(vvar).replace(/;+$/, "") : make(key)),
1662
+ "in",
1663
+ make(hash) + ")", make(block) ]);
1664
+ },
1665
+ "while": function(condition, block) {
1666
+ return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]);
1667
+ },
1668
+ "do": function(condition, block) {
1669
+ return add_spaces([ "do", make(block), "while", "(" + make(condition) + ")" ]) + ";";
1670
+ },
1671
+ "return": function(expr) {
1672
+ var out = [ "return" ];
1673
+ if (expr != null) out.push(make(expr));
1674
+ return add_spaces(out) + ";";
1675
+ },
1676
+ "binary": function(operator, lvalue, rvalue) {
1677
+ var left = make(lvalue), right = make(rvalue);
1678
+ // XXX: I'm pretty sure other cases will bite here.
1679
+ // we need to be smarter.
1680
+ // adding parens all the time is the safest bet.
1681
+ if (member(lvalue[0], [ "assign", "conditional", "seq" ]) ||
1682
+ lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]] ||
1683
+ lvalue[0] == "function" && needs_parens(this)) {
1684
+ left = "(" + left + ")";
1685
+ }
1686
+ if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
1687
+ rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] &&
1688
+ !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
1689
+ right = "(" + right + ")";
1690
+ }
1691
+ else if (!beautify && options.inline_script && (operator == "<" || operator == "<<")
1692
+ && rvalue[0] == "regexp" && /^script/i.test(rvalue[1])) {
1693
+ right = " " + right;
1694
+ }
1695
+ return add_spaces([ left, operator, right ]);
1696
+ },
1697
+ "unary-prefix": function(operator, expr) {
1698
+ var val = make(expr);
1699
+ if (!(expr[0] == "num" || (expr[0] == "unary-prefix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
1700
+ val = "(" + val + ")";
1701
+ return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val;
1702
+ },
1703
+ "unary-postfix": function(operator, expr) {
1704
+ var val = make(expr);
1705
+ if (!(expr[0] == "num" || (expr[0] == "unary-postfix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
1706
+ val = "(" + val + ")";
1707
+ return val + operator;
1708
+ },
1709
+ "sub": function(expr, subscript) {
1710
+ var hash = make(expr);
1711
+ if (needs_parens(expr))
1712
+ hash = "(" + hash + ")";
1713
+ return hash + "[" + make(subscript) + "]";
1714
+ },
1715
+ "object": function(props) {
1716
+ var obj_needs_parens = needs_parens(this);
1717
+ if (props.length == 0)
1718
+ return obj_needs_parens ? "({})" : "{}";
1719
+ var out = "{" + newline + with_indent(function(){
1720
+ return MAP(props, function(p){
1721
+ if (p.length == 3) {
1722
+ // getter/setter. The name is in p[0], the arg.list in p[1][2], the
1723
+ // body in p[1][3] and type ("get" / "set") in p[2].
1724
+ return indent(make_function(p[0], p[1][2], p[1][3], p[2]));
1725
+ }
1726
+ var key = p[0], val = parenthesize(p[1], "seq");
1727
+ if (options.quote_keys) {
1728
+ key = encode_string(key);
1729
+ } else if ((typeof key == "number" || !beautify && +key + "" == key)
1730
+ && parseFloat(key) >= 0) {
1731
+ key = make_num(+key);
1732
+ } else if (!is_identifier(key)) {
1733
+ key = encode_string(key);
1734
+ }
1735
+ return indent(add_spaces(beautify && options.space_colon
1736
+ ? [ key, ":", val ]
1737
+ : [ key + ":", val ]));
1738
+ }).join("," + newline);
1739
+ }) + newline + indent("}");
1740
+ return obj_needs_parens ? "(" + out + ")" : out;
1741
+ },
1742
+ "regexp": function(rx, mods) {
1743
+ return "/" + rx + "/" + mods;
1744
+ },
1745
+ "array": function(elements) {
1746
+ if (elements.length == 0) return "[]";
1747
+ return add_spaces([ "[", add_commas(MAP(elements, function(el, i){
1748
+ if (!beautify && el[0] == "atom" && el[1] == "undefined") return i === elements.length - 1 ? "," : "";
1749
+ return parenthesize(el, "seq");
1750
+ })), "]" ]);
1751
+ },
1752
+ "stat": function(stmt) {
1753
+ return make(stmt).replace(/;*\s*$/, ";");
1754
+ },
1755
+ "seq": function() {
1756
+ return add_commas(MAP(slice(arguments), make));
1757
+ },
1758
+ "label": function(name, block) {
1759
+ return add_spaces([ make_name(name), ":", make(block) ]);
1760
+ },
1761
+ "with": function(expr, block) {
1762
+ return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]);
1763
+ },
1764
+ "atom": function(name) {
1765
+ return make_name(name);
1766
+ }
1767
+ }, function(){ return make(ast) });
1768
+
1769
+ // The squeezer replaces "block"-s that contain only a single
1770
+ // statement with the statement itself; technically, the AST
1771
+ // is correct, but this can create problems when we output an
1772
+ // IF having an ELSE clause where the THEN clause ends in an
1773
+ // IF *without* an ELSE block (then the outer ELSE would refer
1774
+ // to the inner IF). This function checks for this case and
1775
+ // adds the block brackets if needed.
1776
+ function make_then(th) {
1777
+ if (th == null) return ";";
1778
+ if (th[0] == "do") {
1779
+ // https://github.com/mishoo/UglifyJS/issues/#issue/57
1780
+ // IE croaks with "syntax error" on code like this:
1781
+ // if (foo) do ... while(cond); else ...
1782
+ // we need block brackets around do/while
1783
+ return make_block([ th ]);
1784
+ }
1785
+ var b = th;
1786
+ while (true) {
1787
+ var type = b[0];
1788
+ if (type == "if") {
1789
+ if (!b[3])
1790
+ // no else, we must add the block
1791
+ return make([ "block", [ th ]]);
1792
+ b = b[3];
1793
+ }
1794
+ else if (type == "while" || type == "do") b = b[2];
1795
+ else if (type == "for" || type == "for-in") b = b[4];
1796
+ else break;
1797
+ }
1798
+ return make(th);
1799
+ };
1800
+
1801
+ function make_function(name, args, body, keyword) {
1802
+ var out = keyword || "function";
1803
+ if (name) {
1804
+ out += " " + make_name(name);
1805
+ }
1806
+ out += "(" + add_commas(MAP(args, make_name)) + ")";
1807
+ out = add_spaces([ out, make_block(body) ]);
1808
+ return needs_parens(this) ? "(" + out + ")" : out;
1809
+ };
1810
+
1811
+ function must_has_semicolon(node) {
1812
+ switch (node[0]) {
1813
+ case "with":
1814
+ case "while":
1815
+ return empty(node[2]); // `with' or `while' with empty body?
1816
+ case "for":
1817
+ case "for-in":
1818
+ return empty(node[4]); // `for' with empty body?
1819
+ case "if":
1820
+ if (empty(node[2]) && !node[3]) return true; // `if' with empty `then' and no `else'
1821
+ if (node[3]) {
1822
+ if (empty(node[3])) return true; // `else' present but empty
1823
+ return must_has_semicolon(node[3]); // dive into the `else' branch
1824
+ }
1825
+ return must_has_semicolon(node[2]); // dive into the `then' branch
1826
+ }
1827
+ };
1828
+
1829
+ function make_block_statements(statements, noindent) {
1830
+ for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
1831
+ var stat = statements[i];
1832
+ var code = make(stat);
1833
+ if (code != ";") {
1834
+ if (!beautify && i == last && !must_has_semicolon(stat)) {
1835
+ code = code.replace(/;+\s*$/, "");
1836
+ }
1837
+ a.push(code);
1838
+ }
1839
+ }
1840
+ return noindent ? a : MAP(a, indent);
1841
+ };
1842
+
1843
+ function make_switch_block(body) {
1844
+ var n = body.length;
1845
+ if (n == 0) return "{}";
1846
+ return "{" + newline + MAP(body, function(branch, i){
1847
+ var has_body = branch[1].length > 0, code = with_indent(function(){
1848
+ return indent(branch[0]
1849
+ ? add_spaces([ "case", make(branch[0]) + ":" ])
1850
+ : "default:");
1851
+ }, 0.5) + (has_body ? newline + with_indent(function(){
1852
+ return make_block_statements(branch[1]).join(newline);
1853
+ }) : "");
1854
+ if (!beautify && has_body && i < n - 1)
1855
+ code += ";";
1856
+ return code;
1857
+ }).join(newline) + newline + indent("}");
1858
+ };
1859
+
1860
+ function make_block(statements) {
1861
+ if (!statements) return ";";
1862
+ if (statements.length == 0) return "{}";
1863
+ return "{" + newline + with_indent(function(){
1864
+ return make_block_statements(statements).join(newline);
1865
+ }) + newline + indent("}");
1866
+ };
1867
+
1868
+ function make_1vardef(def) {
1869
+ var name = def[0], val = def[1];
1870
+ if (val != null)
1871
+ name = add_spaces([ make_name(name), "=", parenthesize(val, "seq") ]);
1872
+ return name;
1873
+ };
1874
+
1875
+ };
1876
+
1877
+ function split_lines(code, max_line_length) {
1878
+ var splits = [ 0 ];
1879
+ jsp.parse(function(){
1880
+ var next_token = jsp.tokenizer(code);
1881
+ var last_split = 0;
1882
+ var prev_token;
1883
+ function current_length(tok) {
1884
+ return tok.pos - last_split;
1885
+ };
1886
+ function split_here(tok) {
1887
+ last_split = tok.pos;
1888
+ splits.push(last_split);
1889
+ };
1890
+ function custom(){
1891
+ var tok = next_token.apply(this, arguments);
1892
+ out: {
1893
+ if (prev_token) {
1894
+ if (prev_token.type == "keyword") break out;
1895
+ }
1896
+ if (current_length(tok) > max_line_length) {
1897
+ switch (tok.type) {
1898
+ case "keyword":
1899
+ case "atom":
1900
+ case "name":
1901
+ case "punc":
1902
+ split_here(tok);
1903
+ break out;
1904
+ }
1905
+ }
1906
+ }
1907
+ prev_token = tok;
1908
+ return tok;
1909
+ };
1910
+ custom.context = function() {
1911
+ return next_token.context.apply(this, arguments);
1912
+ };
1913
+ return custom;
1914
+ }());
1915
+ return splits.map(function(pos, i){
1916
+ return code.substring(pos, splits[i + 1] || code.length);
1917
+ }).join("\n");
1918
+ };
1919
+
1920
+ /* -----[ Utilities ]----- */
1921
+
1922
+ function repeat_string(str, i) {
1923
+ if (i <= 0) return "";
1924
+ if (i == 1) return str;
1925
+ var d = repeat_string(str, i >> 1);
1926
+ d += d;
1927
+ if (i & 1) d += str;
1928
+ return d;
1929
+ };
1930
+
1931
+ function defaults(args, defs) {
1932
+ var ret = {};
1933
+ if (args === true)
1934
+ args = {};
1935
+ for (var i in defs) if (HOP(defs, i)) {
1936
+ ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
1937
+ }
1938
+ return ret;
1939
+ };
1940
+
1941
+ function is_identifier(name) {
1942
+ return /^[a-z_$][a-z0-9_$]*$/i.test(name)
1943
+ && name != "this"
1944
+ && !HOP(jsp.KEYWORDS_ATOM, name)
1945
+ && !HOP(jsp.RESERVED_WORDS, name)
1946
+ && !HOP(jsp.KEYWORDS, name);
1947
+ };
1948
+
1949
+ function HOP(obj, prop) {
1950
+ return Object.prototype.hasOwnProperty.call(obj, prop);
1951
+ };
1952
+
1953
+ // some utilities
1954
+
1955
+ var MAP;
1956
+
1957
+ (function(){
1958
+ MAP = function(a, f, o) {
1959
+ var ret = [], top = [], i;
1960
+ function doit() {
1961
+ var val = f.call(o, a[i], i);
1962
+ if (val instanceof AtTop) {
1963
+ val = val.v;
1964
+ if (val instanceof Splice) {
1965
+ top.push.apply(top, val.v);
1966
+ } else {
1967
+ top.push(val);
1968
+ }
1969
+ }
1970
+ else if (val != skip) {
1971
+ if (val instanceof Splice) {
1972
+ ret.push.apply(ret, val.v);
1973
+ } else {
1974
+ ret.push(val);
1975
+ }
1976
+ }
1977
+ };
1978
+ if (a instanceof Array) for (i = 0; i < a.length; ++i) doit();
1979
+ else for (i in a) if (HOP(a, i)) doit();
1980
+ return top.concat(ret);
1981
+ };
1982
+ MAP.at_top = function(val) { return new AtTop(val) };
1983
+ MAP.splice = function(val) { return new Splice(val) };
1984
+ var skip = MAP.skip = {};
1985
+ function AtTop(val) { this.v = val };
1986
+ function Splice(val) { this.v = val };
1987
+ })();
1988
+
1989
+ /* -----[ Exports ]----- */
1990
+
1991
+ exports.ast_walker = ast_walker;
1992
+ exports.ast_mangle = ast_mangle;
1993
+ exports.ast_squeeze = ast_squeeze;
1994
+ exports.ast_lift_variables = ast_lift_variables;
1995
+ exports.gen_code = gen_code;
1996
+ exports.ast_add_scope = ast_add_scope;
1997
+ exports.set_logger = function(logger) { warn = logger };
1998
+ exports.make_string = make_string;
1999
+ exports.split_lines = split_lines;
2000
+ exports.MAP = MAP;
2001
+
2002
+ // keep this last!
2003
+ exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
2004
+
2005
+ });