From affc8e245ae691acb26b26116604fe4aecdbe523 Mon Sep 17 00:00:00 2001 From: Rob Linton Date: Fri, 17 Sep 2010 14:27:50 -0500 Subject: [PATCH] initial import --- Buildfile | 7 + LICENSE | 15 + README.md | 55 ++ apps/demo_app/Buildfile | 7 + apps/demo_app/controllers/demo.js | 13 + apps/demo_app/core.js | 15 + apps/demo_app/fixtures/person.js | 25 + apps/demo_app/main.js | 21 + apps/demo_app/models/person.js | 15 + apps/demo_app/resources/MainPage.xib | 874 ++++++++++++++++++ apps/demo_app/resources/loading.rhtml | 9 + bin/scxib | 53 ++ core.js | 97 ++ lib/beautify._js | 4 + lib/parse-js._js | 1195 ++++++++++++++++++++++++ lib/process._js | 1201 +++++++++++++++++++++++++ resources/SCXIB.xslt | 883 ++++++++++++++++++ 17 files changed, 4489 insertions(+) create mode 100644 Buildfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 apps/demo_app/Buildfile create mode 100644 apps/demo_app/controllers/demo.js create mode 100644 apps/demo_app/core.js create mode 100644 apps/demo_app/fixtures/person.js create mode 100644 apps/demo_app/main.js create mode 100644 apps/demo_app/models/person.js create mode 100644 apps/demo_app/resources/MainPage.xib create mode 100644 apps/demo_app/resources/loading.rhtml create mode 100755 bin/scxib create mode 100644 core.js create mode 100644 lib/beautify._js create mode 100644 lib/parse-js._js create mode 100644 lib/process._js create mode 100644 resources/SCXIB.xslt diff --git a/Buildfile b/Buildfile new file mode 100644 index 0000000..0e2f373 --- /dev/null +++ b/Buildfile @@ -0,0 +1,7 @@ +# =========================================================================== +# Project: SCXIB +# Copyright: ©2010 Robert Linton +# Contributors: Devin Torres, Kurt Williams +# =========================================================================== + +config :scxib, :required => :sproutcore diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e260b28 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +SCXIB - Interface Builder for the Web +Copyright (c) 2010 Robert Linton +Copyright (c) 2010 Devin Torres + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9de5c08 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +SCXIB - Interface Builder for the Web +===================================== + +SCXIB (pronounced ska-zib) grew out of the desire to use Interface Builder +as a design tool for SproutCore applications. + +[View the Demo Video] + +## How to use SCXIB + +### On-the-fly Application loading + +Transform XIB files for your SproutCore application to load and use during development: + + SCXIB.loadXibWithOptions(sc_static('MainPage.xib'), { + namespace: DemoApp.NAMESPACE, + pageName: 'mainPage', + callback: function () { + DemoApp.getPath('mainPage.mainPane').append(); + } + }); + +### XIB to JavaScript + +Transform a XIB file into a JavaScript file for your SproutCore application +using a command line tool: + `./bin/scxib -namespace DemoApp -page mainPage apps/demo_app/resources/MainPage.xib` + +## Requirements + + - Interface Builder for XCode 3.2.x + - SproutCore + +## Current Class Mappings + + - NSWindow -> SC.Page + - NSPanel -> SC.Panel + - NSView -> SC.View + - NSCustomView -> your app's custom view name + - NSLabel -> SC.LabelView + - NSTextField -> SC.TextFieldView + - NSSplitView -> SC.SplitView + - IKImageView -> SC.ImageView + - NSCheckBox -> SC.CheckBoxView + - NSButton -> SC.ButtonView + - NSPopUpButton -> SC.SelectFieldView + - NSSegmentedControl -> SC.SegmentedView + - NSCollectionView -> SC.SourceListView + - NSScrollView -> SC.ScrollView + - NSWebView -> SC.WebView + - NSMatrix -> SC.RadioView + - NSTabView -> SC.TabView + - NSBox Horizontal/Vertical -> SC.SeparatorView:layoutDirection SC.LAYOUT\_HORIZONTAL/SC.LAYOUT\_VERTICAL + +[View the Demo Video]: http://vimeo.com/ diff --git a/apps/demo_app/Buildfile b/apps/demo_app/Buildfile new file mode 100644 index 0000000..77737c0 --- /dev/null +++ b/apps/demo_app/Buildfile @@ -0,0 +1,7 @@ +# =========================================================================== +# Project: SCXIB +# Copyright: ©2010 Robert Linton +# Contributors: Devin Torres, Kurt Williams +# =========================================================================== + +config :demo_app, :required => :scxib diff --git a/apps/demo_app/controllers/demo.js b/apps/demo_app/controllers/demo.js new file mode 100644 index 0000000..63e175f --- /dev/null +++ b/apps/demo_app/controllers/demo.js @@ -0,0 +1,13 @@ +// ========================================================================== +// Project: DemoApp +// Copyright: ©2010 Robert Linton +// Contributors: Devin Torres, Kurt Williams +// ========================================================================== +/*globals DemoApp */ + +DemoApp.demoController = SC.Object.create({ + + people: [], + message: 'foobar' + +}); diff --git a/apps/demo_app/core.js b/apps/demo_app/core.js new file mode 100644 index 0000000..48d20de --- /dev/null +++ b/apps/demo_app/core.js @@ -0,0 +1,15 @@ +// ========================================================================== +// Project: DemoApp +// Copyright: ©2010 Robert Linton +// Contributors: Devin Torres, Kurt Williams +// ========================================================================== +/*globals DemoApp */ + +DemoApp = SC.Application.create({ + + NAMESPACE: 'DemoApp', + VERSION: '0.1.0', + + store: SC.Store.create().from(SC.Record.fixtures) + +}); diff --git a/apps/demo_app/fixtures/person.js b/apps/demo_app/fixtures/person.js new file mode 100644 index 0000000..1cc8897 --- /dev/null +++ b/apps/demo_app/fixtures/person.js @@ -0,0 +1,25 @@ +sc_require('models/person'); + +DemoApp.Person.FIXTURES = [ + + { guid: 1, + firstName: "Michael", + lastName: "Scott" }, + + { guid: 2, + firstName: "Dwight", + lastName: "Schrute" }, + + { guid: 3, + firstName: "Jim", + lastName: "Halpert" }, + + { guid: 4, + firstName: "Pam", + lastName: "Beesly" }, + + { guid: 5, + firstName: "Ryan", + lastName: "Howard" } + +]; diff --git a/apps/demo_app/main.js b/apps/demo_app/main.js new file mode 100644 index 0000000..57cb60a --- /dev/null +++ b/apps/demo_app/main.js @@ -0,0 +1,21 @@ +// ========================================================================== +// Project: DemoApp +// Copyright: ©2010 Robert Linton +// Contributors: Devin Torres, Kurt Williams +// ========================================================================== +/*globals DemoApp */ + +DemoApp.main = function main() { + + SCXIB.loadXibWithOptions(sc_static('MainPage.xib'), { + namespace: DemoApp.NAMESPACE, + callback: function () { + var people = DemoApp.store.find(DemoApp.Person); + DemoApp.demoController.set('people', people); + DemoApp.getPath('mainPage.mainPane').append(); + } + }); + +}; + +function main() { DemoApp.main(); } diff --git a/apps/demo_app/models/person.js b/apps/demo_app/models/person.js new file mode 100644 index 0000000..eacea0b --- /dev/null +++ b/apps/demo_app/models/person.js @@ -0,0 +1,15 @@ +// ========================================================================== +// Project: DemoApp.Person +// Copyright: ©2010 Robert Linton +// Contributors: Devin Torres, Kurt Williams +// ========================================================================== +/*globals DemoApp */ + +DemoApp.Person = SC.Record.extend({ + + // TODO: Add your own code here. + name: function(){ + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName').cacheable(), + +}) ; diff --git a/apps/demo_app/resources/MainPage.xib b/apps/demo_app/resources/MainPage.xib new file mode 100644 index 0000000..28ee4f5 --- /dev/null +++ b/apps/demo_app/resources/MainPage.xib @@ -0,0 +1,874 @@ + + + + 1060 + 10F569 + 804 + 1038.29 + 461.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 804 + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + NSObject + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{196, -47}, {945, 557}} + 544735232 + Window + NSWindow + + {1.79769e+308, 1.79769e+308} + + + 256 + + + + 268 + + + + 274 + + + + 274 + + + + 2304 + + + + 274 + {198, 555} + + {0, 0} + {0, 0} + 0 + 0 + + + 6 + System + controlBackgroundColor + + 3 + MC42NjY2NjY2NjY3AA + + + + -1 + 0 + + + {{1, 1}, {198, 555}} + + + + + 6 + System + controlColor + + + 4 + + + + -2147483392 + {{234, 1}, {15, 143}} + + + _doScroller: + 1 + 0.89655172824859619 + + + + -2147483392 + {{1, 144}, {233, 15}} + + 1 + + _doScroller: + 0.63157892227172852 + + + {200, 557} + + + 562 + + + + + + {200, 557} + + NSView + + + + 274 + + + + 18 + {{13, 10}, {709, 533}} + + + + 1 + + + 256 + + + + 268 + {{14, 438}, {38, 17}} + + YES + + 68288064 + 272630784 + Label + + LucidaGrande + 13 + 1044 + + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + + 268 + {{17, 395}, {96, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + + + + + {{10, 33}, {689, 487}} + + Tab + + + + + 2 + + + 256 + + + + 268 + {{46, 424}, {131, 17}} + + YES + + 68288064 + 272630784 + too much awesome! + + + + + + + + + 268 + {{49, 310}, {61, 38}} + + YES + 2 + 1 + + + -2080244224 + 0 + Radio + + + 1 + 1211912703 + 0 + + NSRadioButton + + + + 200 + 25 + + + 67239424 + 0 + Radio + + + 1211912703 + 0 + + 549453824 + {18, 18} + + + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAwYAAAF1gAAAAAACAAIAAgACAABAAEAAQABAAAMGGFw +cGwCAAAAbW50clJHQiBYWVogB9YABAADABMALAASYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAPbWAAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA +AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAMSbmRpbgAA +BOwAAAY+ZGVzYwAACywAAABkZHNjbQAAC5AAAAAubW1vZAAAC8AAAAAoY3BydAAAC+gAAAAtWFlaIAAA +AAAAAF1KAAA0kQAACCVYWVogAAAAAAAAdCAAALRgAAAjPVhZWiAAAAAAAAAlbAAAFyoAAKfDWFlaIAAA +AAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbGN1 +cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAAD +AQAAAQACBAUGBwkKCw0ODxASExQWFxgaGxweHyAiIyQmJygpKywtLzAxMjM1Njc4OTs8PT5AQUJDREZH +SElKS0xOT1BRUlNUVVZXWFlaW1xdXl9hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SF +hoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnZ6foKGio6SlpqanqKmqq6ytra6vsLGysrO0tba3uLi5uru8 +vL2+v8DBwcLDxMXGxsfIycrKy8zNzs7P0NHS0tPU1dbW19jZ2drb3Nzd3t/g4eLi4+Tl5ufo6enq6+zt +7u/w8fHy8/T19vf4+fr7/P3+/v8AAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR8gISIjJCUnKCkq +Ky0uLzAxMzQ1Njc4OTo7PD0/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaWltcXV5fYGFiY2RlZmdo +aWprbG1ub3BxcnN0dXZ3d3h5ent8fH1+f4CBgoKDhIWGh4iIiYqLjI2Oj5CRkpOUlJWWl5iZmpucnZ2e +n6ChoqOkpaamp6ipqqusra6vsLCxsrO0tba3uLm5uru8vb6/wMHCw8TFx8jJysvMzc7P0NDR0tPU1dbX +2Nna29ze3+Dh4uPk5ebn6err7O3u7/Hy8/T19vf5+vv8/f7/AAIDAwQFBgcICQoKCwwNDg8QERITFBUW +FxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODg5Ojs8PT4+P0BBQkNDREVGR0hJSUpLTE1O +Tk9QUVJSU1RVVVZXWFhZWltbXF1eXl9gYWFiY2RkZWZnZ2hpaWprbGxtbm5vcHFxcnNzdHV1dnd4eHl6 +ent8fH1+fn+AgYGCg4SEhYaHiImJiouMjY6Oj5CRkpOTlJWWl5iZmZqbnJ2en6ChoqOkpaanqKmqq6yt +rq+xsrO0tba3uLq7vL2+wMHDxMbHycrMzs/R0tTW19nb3d7g4uTm6Ors7vDy9Pb4+vz+/wAAbmRpbgAA +AAAAAAY2AACXGgAAVjoAAFPKAACJ3gAAJ8IAABaoAABQDQAAVDkAAiuFAAIZmQABeFEAAwEAAAIAAAAA +AAEABgANABcAIwAxAEAAUgBlAHsAkwCrAMUA4gD/AR8BPwFhAYUBqgHQAfgCIAJLAncCpQLSAwIDMwNl +A5gDzgQFBD0EdQSvBOsFKQVnBacF6AYqBm4GtQb8B0UHkgfkCDkIkAjnCT4JmAn0ClAKrQsLC2sLygwq +DIwM8Q1XDcAOKA6SDv4PbA/bEE0QxBE7EbQSMRKwEzITuRREFNAVYBXxFocXHhfAGGIZBBmsGlQa+RuU +HC4czh1yHhQeux9jIA0gvCFoIhkizyOJJEEk+SW6JnknOygFKMspkypiKzIsASzXLawuhy9gMD4xGzH8 +MtszvzSgNYY2cjdcOEw5OTorOxs8CD0EPfU+6z/nQOFB2ELUQ9VE00XcRttH5EjxSgBLCUwdTTFOUE9v +UI9Rt1LdVAVVNlZsV6VY4FohW21ct135X09goGH0Y0tkqGYFZ19oxGova5ptCG54b/BxbnLsdG119Xd/ +eQh6knwqfcV/W4D4gpSEO4Xih4CJKorYjIqOOY/jkZuTWJUOlsyYiZpSnB6d4Z+soX+jWqUvpxOo+6rj +rMuuwLC4sra0rra0uL+60LzfvwDBHcLdxLXGhchYyi7MCs3lz7rRmtOA1WPXR9kq2xPc/97s4M/iveSn +5o3obupT7ELuLPAM8fLz0PW396H5f/tZ/T3//wAAAAEAAwALABYAJQA3AE0AZQCBAJ8AwQDlAQsBNQFh +AZABwQH1AisCZAKfAtwDHANfA6MD6gQ0BH8EzQT1BR0FcAXEBhsGdAbPBy0HXAeMB+4IUgi4CSAJVAmK +CfYKZArVC0cLgQu8DDIMqw0mDaIOIQ6hDyQPqRAvELgQ/RFDEc8SXRLuE4AUFRSrFUMV3RZ5FxcXthhY +GPwZoRpIGvEbnBxJHPgdqB5bHw8fxSB9ITch8iKwJDAk8yW3Jn4nRigQKNwpqSp5K0osHCzxLccuoC95 +MFUxMzISMvMz1TS5NaA2hzdxOFw5STo4Oyg8Gj4DPvs/9EDuQepD6ETpRexG8Uf3SP9LFEwhTTBOQE9S +UGZSklOrVMVV4Vb/WB5ZP1phW4Vcq13SXvthUmJ/Y69k4GYSZ0dofGm0au1tZG6ib+FxInJlc6l073Y2 +d396FXtjfLJ+A39VgKmB/4NWhK+GCYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qoOGiWqPV +pVGmz6eOqE6pzqtRrNSuWq/gsWmy8rR+tgu5Kbq6vE294b93wQ7Cp8RBxd3He8kZyrrLisxbzf/Po9FK +0vHUm9ZF1/HZn9tO3Cbc/96x4GTiGePQ5YjnQegf6Pzquex27jbv9/G583z0X/VC9wj40Pqa/GX+Mf// +AAAAAQADAAsAJQA3AE0AZQCBAJ8AwQELATUBYQGQAcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVw +BcQGGwZ0Bs8HLQeMB+4IUgi4CSAJign2CmQK1QtHC7wMMgyrDSYNog4hDqEPJA+pEC8QuBFDEl0S7hOA +FBUUqxVDFnkXFxe2GFgY/BpIGvEbnBxJHPgdqB8PH8UgfSE3IfIjbyQwJPMltydGKBAo3Cp5K0osHC3H +LqAveTEzMhIy8zS5NaA2hzhcOUk6ODwaPQ4+Az/0QO5C6EPoROlG8Uf3SglLFEwhTkBPUlF7UpJUxVXh +Vv9ZP1phXKtd0mAlYVJjr2TgZhJofGm0au1tZG6ib+FxInJldO92Nnd/eMl6FXyyfgN/VYCpgf+Er4YJ +h2WIwoohi4GOR4+skRKSe5PklVCWvJgrmZubDJx/nfSfaqDholqj1aVRps+oTqnOq1Gs1K2Xrlqv4LFp +svK0frYLt5m5Kbnxurq8Tb3hv3fBDsHawqfEQcUPxd3He8hKyRnKusuKzFvN/87Rz6PQdtFK0vHTxtSb +1kXXG9fx2MjZn9tO3Cbc/93Y3rHfiuBk4hni9ePQ5KzliOZk50HoH+j86drqueuX7HbtVu427xbv9/DX +8bnymvN89F/1QvYl9wj37PjQ+bX6mvt//GX9S/4x//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAABIAAAAcAEMAbwBsAG8AcgAgAEwAQwBE +AABtbW9kAAAAAAAABhAAAJxOAAAAAL5zkQAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQg +QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA + + + + + + 3 + MCAwAA + + + + 400 + 75 + + + {61, 18} + {4, 2} + 1151868928 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 549453824 + {18, 18} + + + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFugEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + + + + + + + 400 + 75 + + + + + + + + + 268 + {{54, 64}, {346, 180}} + + YES + + -1805517311 + 272629760 + this is a big text box + + + YES + + + + + + + 268 + {{217, 380}, {38, 17}} + + YES + + 68288064 + 272630784 + Label + + + + + + + + + 268 + {{214, 339}, {96, 22}} + + YES + + -1804468671 + 272630784 + + + + YES + + + + + + {{10, 33}, {689, 487}} + + + Tab2 + + + + + + + 0 + YES + YES + + + + + + {{210, 0}, {735, 557}} + + NSView + + + {945, 557} + + YES + 3 + + + {945, 557} + + + {{0, 0}, {1680, 1028}} + {1.79769e+308, 1.79769e+308} + + + + + + + + itemPrototype + + + + 12 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + + + + + + 2 + + + + + + + + 3 + + + + + + + + + 4 + + + + + + + + 5 + + + + + + + + 6 + + + + + + + + + + 7 + + + + + 8 + + + + + 9 + + + + + 10 + + + + + 16 + + + + + + + + + 17 + + + + + + + + 18 + + + + + + + + 19 + + + + + + + + + + + + 20 + + + + + + + + + 43 + + + + + + + + 44 + + + + + 45 + + + + + + + + 46 + + + + + 47 + + + + + + + + 48 + + + + + 51 + + + + + + + + + + 52 + + + + + 53 + + + + + 54 + + + + + 55 + + + + + + + + 56 + + + + + 57 + + + + + + + + 58 + + + + + 59 + + + + + + + + 60 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{21, 157}, {945, 557}} + com.apple.InterfaceBuilder.CocoaPlugin + {{21, 157}, {945, 557}} + + {196, 240} + {{357, 418}, {480, 270}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + IBUserDefinedRuntimeAttributesPlaceholderName + + IBUserDefinedRuntimeAttributesPlaceholderName + + + + com.apple.InterfaceBuilder.userDefinedRuntimeAttributeType.number + defaultThickness + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + IBUserDefinedRuntimeAttributesPlaceholderName + + IBUserDefinedRuntimeAttributesPlaceholderName + + + + com.apple.InterfaceBuilder.userDefinedRuntimeAttributeType.string + valueBinding + DemoApp.demoController.message + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAAAAAAAAxArAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + IBUserDefinedRuntimeAttributesPlaceholderName + + IBUserDefinedRuntimeAttributesPlaceholderName + + + + com.apple.InterfaceBuilder.userDefinedRuntimeAttributeType.string + contentValueKey + firstName + + + com.apple.InterfaceBuilder.userDefinedRuntimeAttributeType.string + contentBinding + DemoApp.demoController.people + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 60 + + + 0 + IBCocoaFramework + YES + + 3 + + diff --git a/apps/demo_app/resources/loading.rhtml b/apps/demo_app/resources/loading.rhtml new file mode 100644 index 0000000..e5fd662 --- /dev/null +++ b/apps/demo_app/resources/loading.rhtml @@ -0,0 +1,9 @@ +<% content_for :loading do %> +<% # Any HTML in this file will be visible on screen while your page loads + # its application JavaScript. SproutCore applications are optimized for + # caching and startup very fast, so your users will often only see this + # content for a brief moment on their first app load, if at all. +%> +

Loading...

+ +<% end %> diff --git a/bin/scxib b/bin/scxib new file mode 100755 index 0000000..abed192 --- /dev/null +++ b/bin/scxib @@ -0,0 +1,53 @@ +#!/bin/sh +JSC=/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc + +show_help() { + echo "Usage: $0 [options] file [file ...]" + echo "Options:" + echo " -namespace SC.Application namespace" + echo " -page SC.Page name" + echo " -panel SC.Panel name" +} + +while true; do + case "$1" in + -h|--help|-\?) show_help; exit 0;; + -namespace) if [ $# -gt 1 ]; then + namespace=$2; shift 2 + else + echo "-namespace requires an argument" 1>&2 + exit 1 + fi ;; + -page) if [ $# -gt 1 ]; then + page=$2; shift 2 + else + echo "-page requires an argument" 1>&2 + exit 1 + fi ;; + -panel) if [ $# -gt 1 ]; then + panel=$2; shift 2 + else + echo "-panel requires an argument" 1>&2 + exit 1 + fi ;; + --) shift; break;; + -*) echo "invalid option: $1" 1>&2; show_help; exit 1;; + *) break;; + esac +done + +if [[ -z $namespace ]]; then + echo "$0: no namespace"; exit 1 +fi + +basedir=`dirname $0`/.. + +if [[ -n $page ]]; then + src=`xsltproc --param namespace "'$namespace'" --param pageName "'$page'" $basedir/resources/SCXIB.xslt $@` +elif [[ -n $panel ]]; then + src=`xsltproc --param namespace "'$namespace'" --param panelName "'$panel'" $basedir/resources/SCXIB.xslt $@` +else + echo "$0: no page or panel"; exit 1 +fi + +exec $JSC $basedir/lib/parse-js._js $basedir/lib/process._js $basedir/lib/beautify._js -- "$src" diff --git a/core.js b/core.js new file mode 100644 index 0000000..d222e8a --- /dev/null +++ b/core.js @@ -0,0 +1,97 @@ +// ========================================================================== +// Project: SCXIB +// Copyright: ©2010 Robert Linton +// Contributors: Devin Torres, Kurt Williams +// ========================================================================== +/*globals SCXIB DOMParser XSLTProcessor */ +/*jslint evil: true */ + +/** @namespace + + SCXIB allows you to load Interface Builder XIBs as SproutCore SC.Pages or + SC.Panels. + + Example: + + {{{ + SCXIB.loadXibWithOptions(sc_static('MainPage.xib'), { + namespace: MyApp.NAMESPACE, + pageName: 'mainPage', + callback: function () { + MyApp.getPath('mainPage.mainPane').append(); + } + }); + }}} + + @extends SC.Object +*/ +SCXIB = SC.Object.create( + /** @scope SCXIB.prototype */ { + + NAMESPACE: 'SCXIB', + VERSION: '0.1.0', + + /** + Fetches a document using SC.Request and parses it as XML. + + @param url {String} Location of the XML document. + @returns {Document} + */ + loadXmlDoc: function (url) { + var parser = new DOMParser(), doc; + SC.Request.getUrl(url).async(NO).notify(this, function (resp) { + if (SC.$ok(resp)) { + doc = parser.parseFromString(resp.rawRequest.responseText, 'text/xml'); + } else { + SC.Logger.error('Could not fetch XML file "%@"'.fmt(url)); + } + }).send(); + return doc; + }, + + /** + Fetches a string for eval from an XSLT transformation of a XIB file. + + @param xibOptions {Array} An array of XIB file paths and opts. + @param opts {Hash} A hash of opts for use during XSLT transformation. + @returns {void} + */ + loadXibsWithOptions: function (xibOptions) { + var self = this, + xslDoc = this.loadXmlDoc(sc_static('SCXIB.xslt')), + xibDoc, xsltProc, resDoc, callback; + + xibOptions.forEach(function (opts) { + xibDoc = self.loadXmlDoc(opts.url); + + xsltProc = new XSLTProcessor(); + xsltProc.importStylesheet(xslDoc); + + for (var p in opts) { + if (opts.hasOwnProperty(p)) { + xsltProc.setParameter(null, p, opts[p]); + } + } + + resDoc = xsltProc.transformToFragment(xibDoc, document); + + if (resDoc && resDoc.textContent) { + try { + eval(resDoc.textContent); + callback = opts.callback; + if (callback) callback.call(callback); + } catch (e) { + SC.Logger.error("Exception while evaluating XIB transform: %@, line %@".fmt(e, e.line)); + } + } else { + SC.Logger.error('Could not parse XIB file "%@"'.fmt(opts.url)); + } + }); + }, + + loadXibWithOptions: function (url, options) { + var defaults = { url: url, pageName: 'mainPage' }; + this.loadXibsWithOptions([SC.mixin(defaults, options)]); + } + +}); diff --git a/lib/beautify._js b/lib/beautify._js new file mode 100644 index 0000000..c9ec524 --- /dev/null +++ b/lib/beautify._js @@ -0,0 +1,4 @@ +(function (src) { + var ast = jsp.parse(src); + print(uglify.gen_code(ast, { indent_level: 2 })); +})(arguments[0]); diff --git a/lib/parse-js._js b/lib/parse-js._js new file mode 100644 index 0000000..f9b3ed7 --- /dev/null +++ b/lib/parse-js._js @@ -0,0 +1,1195 @@ +/*********************************************************************** + + 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 contains the tokenizer/parser. It is a port to JavaScript + of parse-js [1], a JavaScript parser library written in Common Lisp + by Marijn Haverbeke. Thank you Marijn! + + [1] http://marijn.haverbeke.nl/parse-js/ + + Exported functions: + + - tokenizer(code) -- returns a function. Call the returned + function to fetch the next token. + + - parse(code) -- returns an AST of the given JavaScript code. + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the same terms as the original code (ZLIB license): + + Copyright 2010 (c) Mihai Bazon + Based on parse-js (http://marijn.haverbeke.nl/parse-js/). + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + ***********************************************************************/ + +var jsp = {}; + +/* -----[ Tokenizer (constants) ]----- */ + +var KEYWORDS = array_to_hash([ + "break", + "case", + "catch", + "const", + "continue", + "default", + "delete", + "do", + "else", + "finally", + "for", + "function", + "if", + "in", + "instanceof", + "new", + "return", + "switch", + "throw", + "try", + "typeof", + "var", + "void", + "while", + "with", + "NaN" +]); + +var RESERVED_WORDS = array_to_hash([ + "abstract", + "boolean", + "byte", + "char", + "class", + "debugger", + "double", + "enum", + "export", + "extends", + "final", + "float", + "goto", + "implements", + "import", + "int", + "interface", + "long", + "native", + "package", + "private", + "protected", + "public", + "short", + "static", + "super", + "synchronized", + "throws", + "transient", + "volatile" +]); + +var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([ + "return", + "new", + "delete", + "throw" +]); + +var KEYWORDS_ATOM = array_to_hash([ + "false", + "null", + "true", + "undefined", + "NaN" +]); + +var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^")); + +var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; +var RE_OCT_NUMBER = /^0[0-7]+$/; +var RE_DEC_NUMBER = /^\d*\.?\d*(?:e-?\d*(?:\d\.?|\.?\d)\d*)?$/i; + +var OPERATORS = array_to_hash([ + "in", + "instanceof", + "typeof", + "new", + "void", + "delete", + "++", + "--", + "+", + "-", + "!", + "~", + "&", + "|", + "^", + "*", + "/", + "%", + ">>", + "<<", + ">>>", + "<", + ">", + "<=", + ">=", + "==", + "===", + "!=", + "!==", + "?", + "=", + "+=", + "-=", + "/=", + "*=", + "%=", + ">>=", + "<<=", + ">>>=", + "~=", + "%=", + "|=", + "^=", + "&&", + "||" +]); + +var WHITESPACE_CHARS = array_to_hash(characters(" \n\r\t")); + +var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:")); + +var PUNC_CHARS = array_to_hash(characters("[]{}(),;:")); + +var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy")); + +/* -----[ Tokenizer ]----- */ + +function is_alphanumeric_char(ch) { + ch = ch.charCodeAt(0); + return (ch >= 48 && ch <= 57) || + (ch >= 65 && ch <= 90) || + (ch >= 97 && ch <= 122); +}; + +function is_identifier_char(ch) { + return is_alphanumeric_char(ch) || ch == "$" || ch == "_"; +}; + +function is_digit(ch) { + ch = ch.charCodeAt(0); + return ch >= 48 && ch <= 57; +}; + +function parse_js_number(num) { + if (RE_HEX_NUMBER.test(num)) { + return parseInt(num.substr(2), 16); + } else if (RE_OCT_NUMBER.test(num)) { + return parseInt(num.substr(1), 8); + } else if (RE_DEC_NUMBER.test(num)) { + return parseFloat(num); + } +}; + +function JS_Parse_Error(message, line, col, pos) { + this.message = message; + this.line = line; + this.col = col; + this.pos = pos; + try { + ({})(); + } catch(ex) { + this.stack = ex.stack; + }; +}; + +JS_Parse_Error.prototype.toString = function() { + return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; +}; + +function js_error(message, line, col, pos) { + throw new JS_Parse_Error(message, line, col, pos); +}; + +function is_token(token, type, val) { + return token.type == type && (val == null || token.value == val); +}; + +var EX_EOF = {}; + +function tokenizer($TEXT, skip_comments) { + + var S = { + text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n"), + pos : 0, + tokpos : 0, + line : 0, + tokline : 0, + col : 0, + tokcol : 0, + newline_before : false, + regex_allowed : false + }; + + function peek() { return S.text.charAt(S.pos); }; + + function next(signal_eof) { + var ch = S.text.charAt(S.pos++); + if (signal_eof && !ch) + throw EX_EOF; + if (ch == "\n") { + S.newline_before = true; + ++S.line; + S.col = 0; + } else { + ++S.col; + } + return ch; + }; + + function eof() { + return !S.peek(); + }; + + function find(what, signal_eof) { + var pos = S.text.indexOf(what, S.pos); + if (signal_eof && pos == -1) throw EX_EOF; + return pos; + }; + + function start_token() { + S.tokline = S.line; + S.tokcol = S.col; + S.tokpos = S.pos; + }; + + function token(type, value) { + S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) || + (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) || + (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value))); + var ret = { + type : type, + value : value, + line : S.tokline, + col : S.tokcol, + pos : S.tokpos, + nlb : S.newline_before + }; + S.newline_before = false; + return ret; + }; + + function skip_whitespace() { + while (HOP(WHITESPACE_CHARS, peek())) + next(); + }; + + function read_while(pred) { + var ret = "", ch = peek(), i = 0; + while (ch && pred(ch, i++)) { + ret += next(); + ch = peek(); + } + return ret; + }; + + function parse_error(err) { + js_error(err, S.tokline, S.tokcol, S.tokpos); + }; + + function read_num(prefix) { + var has_e = false, after_e = false, has_x = false; + var num = read_while(function(ch, i){ + if (ch == "x" || ch == "X") { + if (has_x) return false; + return has_x = true; + } + if (!has_x && (ch == "E" || ch == "e")) { + if (has_e) return false; + return has_e = after_e = true; + } + if (ch == "-") { + if (after_e || (i == 0 && !prefix)) return true; + return false; + } + after_e = false; + return is_alphanumeric_char(ch) || ch == "."; + }); + if (prefix) + num = prefix + num; + var valid = parse_js_number(num); + if (!isNaN(valid)) { + return token("num", valid); + } else { + parse_error("Invalid syntax: " + num); + } + }; + + function read_escaped_char() { + var ch = next(true); + switch (ch) { + case "n" : return "\n"; + case "r" : return "\r"; + case "t" : return "\t"; + case "b" : return "\b"; + case "v" : return "\v"; + case "f" : return "\f"; + case "0" : return "\0"; + case "x" : return String.fromCharCode(hex_bytes(2)); + case "u" : return String.fromCharCode(hex_bytes(4)); + default : return ch; + } + }; + + function hex_bytes(n) { + var num = 0; + for (; n > 0; --n) { + var digit = parseInt(next(true), 16); + if (isNaN(digit)) + parse_error("Invalid hex-character pattern in string"); + num = (num << 4) | digit; + } + return num; + }; + + function read_string() { + return with_eof_error("Unterminated string constant", function(){ + var quote = next(), ret = ""; + for (;;) { + var ch = next(true); + if (ch == "\\") ch = read_escaped_char(); + else if (ch == quote) break; + ret += ch; + } + return token("string", ret); + }); + }; + + function read_line_comment() { + next(); + var i = find("\n"), ret; + if (i == -1) { + ret = S.text.substr(S.pos); + S.pos = S.text.length; + } else { + ret = S.text.substring(S.pos, i); + S.pos = i; + } + return token("comment1", ret); + }; + + function read_multiline_comment() { + next(); + return with_eof_error("Unterminated multiline comment", function(){ + var i = find("*/", true), + text = S.text.substring(S.pos, i), + tok = token("comment2", text); + S.pos = i + 2; + S.newline_before = text.indexOf("\n") >= 0; + return tok; + }); + }; + + function read_regexp() { + return with_eof_error("Unterminated regular expression", function(){ + var prev_backslash = false, regexp = "", ch, in_class = false; + while ((ch = next(true))) if (prev_backslash) { + regexp += "\\" + ch; + prev_backslash = false; + } else if (ch == "[") { + in_class = true; + regexp += ch; + } else if (ch == "]" && in_class) { + in_class = false; + regexp += ch; + } else if (ch == "/" && !in_class) { + break; + } else if (ch == "\\") { + prev_backslash = true; + } else { + regexp += ch; + } + var mods = read_while(function(ch){ + return HOP(REGEXP_MODIFIERS, ch); + }); + return token("regexp", [ regexp, mods ]); + }); + }; + + function read_operator(prefix) { + function grow(op) { + var bigger = op + peek(); + if (HOP(OPERATORS, bigger)) { + next(); + return grow(bigger); + } else { + return op; + } + }; + return token("operator", grow(prefix || next())); + }; + + var handle_slash = skip_comments ? function() { + next(); + switch (peek()) { + case "/": read_line_comment(); return next_token(); + case "*": read_multiline_comment(); return next_token(); + } + return S.regex_allowed ? read_regexp() : read_operator("/"); + } : function() { + next(); + switch (peek()) { + case "/": return read_line_comment(); + case "*": return read_multiline_comment(); + } + return S.regex_allowed ? read_regexp() : read_operator("/"); + }; + + function handle_dot() { + next(); + return is_digit(peek()) + ? read_num(".") + : token("punc", "."); + }; + + function read_word() { + var word = read_while(is_identifier_char); + return !HOP(KEYWORDS, word) + ? token("name", word) + : HOP(OPERATORS, word) + ? token("operator", word) + : HOP(KEYWORDS_ATOM, word) + ? token("atom", word) + : token("keyword", word); + }; + + function with_eof_error(eof_error, cont) { + try { + return cont(); + } catch(ex) { + if (ex === EX_EOF) parse_error(eof_error); + else throw ex; + } + }; + + function next_token() { + skip_whitespace(); + start_token(); + var ch = peek(); + if (!ch) return token("eof"); + if (is_digit(ch)) return read_num(); + if (ch == '"' || ch == "'") return read_string(); + if (HOP(PUNC_CHARS, ch)) return token("punc", next()); + if (ch == ".") return handle_dot(); + if (ch == "/") return handle_slash(); + if (HOP(OPERATOR_CHARS, ch)) return read_operator(); + if (is_identifier_char(ch)) return read_word(); + parse_error("Unexpected character '" + ch + "'"); + }; + + next_token.context = function(nc) { + if (nc) S = nc; + return S; + }; + + return next_token; + +}; + +/* -----[ Parser (constants) ]----- */ + +var UNARY_PREFIX = array_to_hash([ + "typeof", + "void", + "delete", + "--", + "++", + "!", + "~", + "-", + "+" +]); + +var UNARY_POSTFIX = array_to_hash([ "--", "++" ]); + +var ASSIGNMENT = (function(a, ret, i){ + while (i < a.length) { + ret[a[i]] = a[i].substr(0, a[i].length - 1); + i++; + } + return ret; +})( + ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "~=", "%=", "|=", "^="], + { "=": true }, + 0 +); + +var PRECEDENCE = (function(a, ret){ + for (var i = 0, n = 1; i < a.length; ++i, ++n) { + var b = a[i]; + for (var j = 0; j < b.length; ++j) { + ret[b[j]] = n; + } + } + return ret; +})( + [ + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"] + ], + {} +); + +var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); + +var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); + +/* -----[ Parser ]----- */ + +function NodeWithToken(str, start, end) { + this.name = str; + this.start = start; + this.end = end; +}; + +NodeWithToken.prototype.toString = function() { return this.name; }; + +function parse($TEXT, strict_mode, embed_tokens) { + + var S = { + input: tokenizer($TEXT, true), + token: null, + prev: null, + peeked: null, + in_function: 0, + in_loop: 0, + labels: [] + }; + + S.token = next(); + + function is(type, value) { + return is_token(S.token, type, value); + }; + + function peek() { return S.peeked || (S.peeked = S.input()); }; + + function next() { + S.prev = S.token; + if (S.peeked) { + S.token = S.peeked; + S.peeked = null; + } else { + S.token = S.input(); + } + return S.token; + }; + + function prev() { + return S.prev; + }; + + function croak(msg, line, col, pos) { + var ctx = S.input.context(); + js_error(msg, + line != null ? line : ctx.tokline, + col != null ? col : ctx.tokcol, + pos != null ? pos : ctx.tokpos); + }; + + function token_error(token, msg) { + croak(msg, token.line, token.col); + }; + + function unexpected(token) { + if (token == null) + token = S.token; + token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); + }; + + function expect_token(type, val) { + if (is(type, val)) { + return next(); + } + token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type); + }; + + function expect(punc) { return expect_token("punc", punc); }; + + function can_insert_semicolon() { + return !strict_mode && ( + S.token.nlb || is("eof") || is("punc", "}") + ); + }; + + function semicolon() { + if (is("punc", ";")) next(); + else if (!can_insert_semicolon()) unexpected(); + }; + + function as() { + return slice(arguments); + }; + + function parenthesised() { + expect("("); + var ex = expression(); + expect(")"); + return ex; + }; + + function add_tokens(str, start, end) { + return new NodeWithToken(str, start, end); + }; + + var statement = embed_tokens ? function(allow_case) { + var start = S.token; + var stmt = $statement(allow_case); + stmt[0] = add_tokens(stmt[0], start, prev()); + return stmt; + } : $statement; + + function $statement(allow_case) { + switch (S.token.type) { + case "num": + case "string": + case "regexp": + case "operator": + case "atom": + return simple_statement(); + + case "name": + return is_token(peek(), "punc", ":") + ? labeled_statement(prog1(S.token.value, next, next)) + : simple_statement(); + + case "punc": + switch (S.token.value) { + case "{": + return as("block", block_()); + case "[": + case "(": + return simple_statement(); + case ";": + next(); + return as("block"); + default: + unexpected(); + } + + case "keyword": + switch (prog1(S.token.value, next)) { + case "break": + return break_cont("break"); + + case "continue": + return break_cont("continue"); + + case "case": + if (!allow_case) + unexpected(); + return as("case", prog1(expression, curry(expect, ":"))); + + case "debugger": + semicolon(); + return as("debugger"); + + case "default": + if (!allow_case) + unexpected(); + expect(":"); + return as("default"); + + case "do": + return (function(body){ + expect_token("keyword", "while"); + return as("do", prog1(parenthesised, semicolon), body); + })(in_loop(statement)); + + case "for": + return for_(); + + case "function": + return function_(true); + + case "if": + return if_(); + + case "return": + if (S.in_function == 0) + croak("'return' outside of function"); + return as("return", + is("punc", ";") + ? (next(), null) + : can_insert_semicolon() + ? null + : prog1(expression, semicolon)); + + case "switch": + return as("switch", parenthesised(), in_loop(curry(block_, true))); + + case "throw": + return as("throw", prog1(expression, semicolon)); + + case "try": + return try_(); + + case "var": + return prog1(var_, semicolon); + + case "const": + return prog1(const_, semicolon); + + case "while": + return as("while", parenthesised(), in_loop(statement)); + + case "with": + return as("with", parenthesised(), statement()); + + default: + unexpected(); + } + } + }; + + function labeled_statement(label) { + S.labels.push(label); + var start = S.token, stat = statement(); + if (strict_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0])) + unexpected(start); + S.labels.pop(); + return as("label", label, stat); + }; + + function simple_statement() { + return as("stat", prog1(expression, semicolon)); + }; + + function break_cont(type) { + if (S.in_loop == 0) + croak(type + " not inside a loop or switch"); + var name = is("name") ? S.token.value : null; + if (name != null) { + next(); + if (!member(name, S.labels)) + croak("Label " + name + " without matching loop or statement"); + } + semicolon(); + return as(type, name); + }; + + function for_() { + expect("("); + var has_var = is("keyword", "var"); + if (has_var) + next(); + if (is("name") && is_token(peek(), "operator", "in")) { + // for (i in foo) + var name = S.token.value; + next(); next(); + var obj = expression(); + expect(")"); + return as("for-in", has_var, name, obj, in_loop(statement)); + } else { + // classic for + var init = is("punc", ";") ? null : has_var ? var_() : expression(); + expect(";"); + var test = is("punc", ";") ? null : expression(); + expect(";"); + var step = is("punc", ")") ? null : expression(); + expect(")"); + return as("for", init, test, step, in_loop(statement)); + } + }; + + function function_(in_statement) { + var name = is("name") ? prog1(S.token.value, next) : null; + if (in_statement && !name) + unexpected(); + expect("("); + return as(in_statement ? "defun" : "function", + name, + // arguments + (function(first, a){ + while (!is("punc", ")")) { + if (first) first = false; else expect(","); + if (!is("name")) unexpected(); + a.push(S.token.value); + next(); + } + next(); + return a; + })(true, []), + // body + (function(){ + ++S.in_function; + var a = block_(); + --S.in_function; + return a; + })()); + }; + + function if_() { + var cond = parenthesised(), body = statement(), belse; + if (is("keyword", "else")) { + next(); + belse = statement(); + } + return as("if", cond, body, belse); + }; + + function block_(allow_case) { + expect("{"); + var a = []; + while (!is("punc", "}")) { + if (is("eof")) unexpected(); + a.push(statement(allow_case)); + } + next(); + return a; + }; + + function try_() { + var body = block_(), bcatch, bfinally; + if (is("keyword", "catch")) { + next(); + expect("("); + if (!is("name")) + croak("Name expected"); + var name = S.token.value; + next(); + expect(")"); + bcatch = [ name, block_() ]; + } + if (is("keyword", "finally")) { + next(); + bfinally = block_(); + } + if (!bcatch && !bfinally) + croak("Missing catch/finally blocks"); + return as("try", body, bcatch, bfinally); + }; + + function vardefs() { + var a = []; + for (;;) { + if (!is("name")) + unexpected(); + var name = S.token.value; + next(); + if (is("operator", "=")) { + next(); + a.push([ name, expression(false) ]); + } else { + a.push([ name ]); + } + if (!is("punc", ",")) + break; + next(); + } + return a; + }; + + function var_() { + return as("var", vardefs()); + }; + + function const_() { + return as("const", vardefs()); + }; + + function new_() { + var newexp = expr_atom(false), args; + if (is("punc", "(")) { + next(); + args = expr_list(")"); + } else { + args = []; + } + return subscripts(as("new", newexp, args), true); + }; + + function expr_atom(allow_calls) { + if (is("operator", "new")) { + next(); + return new_(); + } + if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) { + return make_unary("unary-prefix", + prog1(S.token.value, next), + expr_atom(allow_calls)); + } + if (is("punc")) { + switch (S.token.value) { + case "(": + next(); + return subscripts(prog1(expression, curry(expect, ")")), allow_calls); + case "[": + next(); + return subscripts(array_(), allow_calls); + case "{": + next(); + return subscripts(object_(), allow_calls); + } + unexpected(); + } + if (is("keyword", "function")) { + next(); + return subscripts(function_(false), allow_calls); + } + if (HOP(ATOMIC_START_TOKEN, S.token.type)) { + var atom = S.token.type == "regexp" + ? as("regexp", S.token.value[0], S.token.value[1]) + : as(S.token.type, S.token.value); + return subscripts(prog1(atom, next), allow_calls); + } + unexpected(); + }; + + function expr_list(closing, allow_trailing_comma) { + var first = true, a = []; + while (!is("punc", closing)) { + if (first) first = false; else expect(","); + if (allow_trailing_comma && is("punc", closing)) + break; + a.push(expression(false)); + } + next(); + return a; + }; + + function array_() { + return as("array", expr_list("]", !strict_mode)); + }; + + function object_() { + var first = true, a = []; + while (!is("punc", "}")) { + if (first) first = false; else expect(","); + if (!strict_mode && is("punc", "}")) + // allow trailing comma + break; + var name = as_property_name(); + expect(":"); + var value = expression(false); + a.push([ name, value ]); + } + next(); + return as("object", a); + }; + + function as_property_name() { + switch (S.token.type) { + case "num": + case "string": + return prog1(S.token.value, next); + } + return as_name(); + }; + + function as_name() { + switch (S.token.type) { + case "name": + case "operator": + case "keyword": + case "atom": + return prog1(S.token.value, next); + default: + unexpected(); + } + }; + + function subscripts(expr, allow_calls) { + if (is("punc", ".")) { + next(); + return subscripts(as("dot", expr, as_name()), allow_calls); + } + if (is("punc", "[")) { + next(); + return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls); + } + if (allow_calls && is("punc", "(")) { + next(); + return subscripts(as("call", expr, expr_list(")")), true); + } + if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) { + return prog1(curry(make_unary, "unary-postfix", S.token.value, expr), + next); + } + return expr; + }; + + function make_unary(tag, op, expr) { + if ((op == "++" || op == "--") && !is_assignable(expr)) + croak("Invalid use of " + op + " operator"); + return as(tag, op, expr); + }; + + function expr_op(left, min_prec) { + var op = is("operator") ? S.token.value : null; + var prec = op != null ? PRECEDENCE[op] : null; + if (prec != null && prec > min_prec) { + next(); + var right = expr_op(expr_atom(true), prec); + return expr_op(as("binary", op, left, right), min_prec); + } + return left; + }; + + function expr_ops() { + return expr_op(expr_atom(true), 0); + }; + + function maybe_conditional(commas) { + if (arguments.length == 0) + commas = true; + var expr = expr_ops(); + if (is("operator", "?")) { + next(); + var yes = expression(); + expect(":"); + return as("conditional", expr, yes, expression(commas)); + } + return expr; + }; + + function is_assignable(expr) { + expr = expr[0]; + return expr == "name" || expr == "dot" || expr == "sub"; + }; + + function maybe_assign(commas) { + if (arguments.length == 0) + commas = true; + var left = maybe_conditional(commas), val = S.token.value; + if (is("operator") && HOP(ASSIGNMENT, val)) { + if (is_assignable(left)) { + next(); + return as("assign", ASSIGNMENT[val], left, maybe_assign(commas)); + } + croak("Invalid assignment"); + } + return left; + }; + + function expression(commas) { + if (arguments.length == 0) + commas = true; + var expr = maybe_assign(commas); + if (commas && is("punc", ",")) { + next(); + return as("seq", expr, expression()); + } + return expr; + }; + + function in_loop(cont) { + try { + ++S.in_loop; + return cont(); + } finally { + --S.in_loop; + } + }; + + return as("toplevel", (function(a){ + while (!is("eof")) + a.push(statement()); + return a; + })([])); + +}; + +/* -----[ Utilities ]----- */ + +function curry(f) { + var args = slice(arguments, 1); + return function() { return f.apply(this, args.concat(slice(arguments))); }; +}; + +function prog1(ret) { + if (ret instanceof Function) + ret = ret(); + for (var i = 1, n = arguments.length; --n > 0; ++i) + arguments[i](); + return ret; +}; + +function array_to_hash(a) { + var ret = {}; + for (var i = 0; i < a.length; ++i) + ret[a[i]] = true; + return ret; +}; + +function slice(a, start) { + return Array.prototype.slice.call(a, start == null ? 0 : start); +}; + +function characters(str) { + return str.split(""); +}; + +function member(name, array) { + for (var i = array.length; --i >= 0;) + if (array[i] === name) + return true; + return false; +}; + +function HOP(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +}; + +/* -----[ Exports ]----- */ + +jsp.tokenizer = tokenizer; +jsp.parse = parse; +jsp.slice = slice; +jsp.curry = curry; +jsp.member = member; +jsp.array_to_hash = array_to_hash; +jsp.PRECEDENCE = PRECEDENCE; +jsp.KEYWORDS_ATOM = KEYWORDS_ATOM; +jsp.RESERVED_WORDS = RESERVED_WORDS; +jsp.KEYWORDS = KEYWORDS; +jsp.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN; +jsp.is_alphanumeric_char = is_alphanumeric_char; diff --git a/lib/process._js b/lib/process._js new file mode 100644 index 0000000..2f069cf --- /dev/null +++ b/lib/process._js @@ -0,0 +1,1201 @@ +/*********************************************************************** + + 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, include_toplevel) -- mangles the + variable/function names in the AST. Returns an AST. Pass true + as second argument to mangle toplevel names too. + + - ast_squeeze(ast) -- employs various optimizations to make the + final generated code even smaller. Returns an AST. + + - gen_code(ast, beautify) -- 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 + + http://mihai.bazon.net/blog + + Distributed under a ZLIB license: + + Copyright 2010 (c) Mihai Bazon + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + ***********************************************************************/ + +var uglify = {}, + slice = jsp.slice, + member = jsp.member, + PRECEDENCE = jsp.PRECEDENCE; + +/* -----[ helper for AST traversal ]----- */ + +function ast_walker(ast) { + function _vardefs(defs) { + return defs.map(function(def){ + var a = [ def[0] ]; + if (def.length > 1) + a[1] = walk(def[1]); + return a; + }); + }; + var walkers = { + "string": function(str) { + return [ "string", str ]; + }, + "num": function(num) { + return [ "num", num ]; + }, + "name": function(name) { + return [ "name", name ]; + }, + "toplevel": function(statements) { + return [ "toplevel", statements.map(walk) ]; + }, + "block": function(statements) { + var out = [ "block" ]; + if (statements != null) + out.push(statements.map(walk)); + return out; + }, + "var": function(defs) { + return [ "var", _vardefs(defs) ]; + }, + "const": function(defs) { + return [ "const", _vardefs(defs) ]; + }, + "try": function(t, c, f) { + return [ + "try", + t.map(walk), + c != null ? [ c[0], c[1].map(walk) ] : null, + f != null ? f.map(walk) : null + ]; + }, + "throw": function(expr) { + return [ "throw", walk(expr) ]; + }, + "new": function(ctor, args) { + return [ "new", walk(ctor), args.map(walk) ]; + }, + "switch": function(expr, body) { + return [ "switch", walk(expr), body.map(walk) ]; + }, + "case": function(expr) { + return [ "case", walk(expr) ]; + }, + "default": function() { + return [ "default" ]; + }, + "break": function(label) { + return [ "break", label ]; + }, + "continue": function(label) { + return [ "continue", label ]; + }, + "conditional": function(cond, t, e) { + return [ "conditional", walk(cond), walk(t), walk(e) ]; + }, + "assign": function(op, lvalue, rvalue) { + return [ "assign", op, walk(lvalue), walk(rvalue) ]; + }, + "dot": function(expr) { + return [ "dot", walk(expr) ].concat(slice(arguments, 1)); + }, + "call": function(expr, args) { + return [ "call", walk(expr), args.map(walk) ]; + }, + "function": function(name, args, body) { + return [ "function", name, args.slice(), body.map(walk) ]; + }, + "defun": function(name, args, body) { + return [ "defun", name, args.slice(), body.map(walk) ]; + }, + "if": function(conditional, t, e) { + return [ "if", walk(conditional), walk(t), walk(e) ]; + }, + "for": function(init, cond, step, block) { + return [ "for", walk(init), walk(cond), walk(step), walk(block) ]; + }, + "for-in": function(has_var, key, hash, block) { + return [ "for-in", has_var, key, walk(hash), walk(block) ]; + }, + "while": function(cond, block) { + return [ "while", walk(cond), walk(block) ]; + }, + "do": function(cond, block) { + return [ "do", walk(cond), walk(block) ]; + }, + "return": function(expr) { + return [ "return", walk(expr) ]; + }, + "binary": function(op, left, right) { + return [ "binary", op, walk(left), walk(right) ]; + }, + "unary-prefix": function(op, expr) { + return [ "unary-prefix", op, walk(expr) ]; + }, + "unary-postfix": function(op, expr) { + return [ "unary-postfix", op, walk(expr) ]; + }, + "sub": function(expr, subscript) { + return [ "sub", walk(expr), walk(subscript) ]; + }, + "object": function(props) { + return [ "object", props.map(function(p){ + return [ p[0], walk(p[1]) ]; + }) ]; + }, + "regexp": function(rx, mods) { + return [ "regexp", rx, mods ]; + }, + "array": function(elements) { + return [ "array", elements.map(walk) ]; + }, + "stat": function(stat) { + return [ "stat", walk(stat) ]; + }, + "seq": function() { + return [ "seq" ].concat(slice(arguments).map(walk)); + }, + "label": function(name, block) { + return [ "label", name, walk(block) ]; + }, + "with": function(expr, block) { + return [ "with", walk(expr), walk(block) ]; + }, + "atom": function(name) { + return [ "atom", 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 with_walkers(walkers, cont){ + var save = {}, i; + for (i in walkers) if (HOP(walkers, i)) { + save[i] = user[i]; + user[i] = walkers[i]; + } + try { return cont(); } + finally { + for (i in save) if (HOP(save, i)) { + if (!save[i]) delete user[i]; + else user[i] = save[i]; + } + } + }; + + return { + walk: walk, + 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 eval() is detected in this or any subscopes + this.uses_eval = false; // will become TRUE if with() 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$_"; + return function(num) { + var ret = ""; + do { + ret = DIGITS.charAt(num % 54) + ret; + num = Math.floor(num / 54); + } 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; + } + }, + 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 + + var m = s.next_mangled(); + s.rev_mangled[m] = name; + return s.mangled[name] = m; + }, + define: function(name) { + if (name != null) + return this.names[name] = 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); + try { + var ret = current_scope.body = cont(); + ret.scope = current_scope; + return ret; + } + finally { + current_scope = current_scope.parent; + } + }; + + function define(name) { + return current_scope.define(name); + }; + + function reference(name) { + current_scope.refs[name] = true; + }; + + function _lambda(name, args, body) { + return [ this[0], define(name), args, with_new_scope(function(){ + args.map(define); + return body.map(walk); + })]; + }; + + return with_new_scope(function(){ + // process AST + var ret = w.with_walkers({ + "function": _lambda, + "defun": _lambda, + "with": function(expr, block) { + for (var s = current_scope; s; s = s.parent) + s.uses_with = true; + }, + "var": function(defs) { + defs.map(function(d){ define(d[0]) }); + }, + "const": function(defs) { + defs.map(function(d){ define(d[0]) }); + }, + "try": function(t, c, f) { + if (c != null) return [ + "try", + t.map(walk), + with_new_scope(function(){ + return [ define(c[0]), c[1].map(walk) ]; + }), + f != null ? f.map(walk) : null + ]; + }, + "name": function(name) { + if (name == "eval") + having_eval.push(current_scope); + reference(name); + }, + "for-in": function(has_var, name) { + if (has_var) define(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. + having_eval.map(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, do_toplevel) { + var w = ast_walker(), walk = w.walk, scope; + + function get_mangled(name, newMangle) { + if (!do_toplevel && !scope.parent) return name; // don't mangle toplevel + return scope.get_mangled(name, newMangle); + }; + + function _lambda(name, args, body) { + if (name) name = get_mangled(name); + body = with_scope(body.scope, function(){ + args = args.map(function(name){ return get_mangled(name) }); + return body.map(walk); + }); + return [ this[0], name, args, body ]; + }; + + function with_scope(s, cont) { + var _scope = scope; + scope = s; + for (var i in s.names) if (HOP(s.names, i)) { + get_mangled(i, true); + } + try { var ret = cont(); ret.scope = s; return ret; } + finally { scope = _scope; }; + }; + + function _vardefs(defs) { + return defs.map(function(d){ + return [ get_mangled(d[0]), walk(d[1]) ]; + }); + }; + + return w.with_walkers({ + "function": _lambda, + "defun": _lambda, + "var": function(defs) { + return [ "var", _vardefs(defs) ]; + }, + "const": function(defs) { + return [ "const", _vardefs(defs) ]; + }, + "name": function(name) { + return [ "name", get_mangled(name) ]; + }, + "try": function(t, c, f) { + return [ "try", + t.map(walk), + c ? with_scope(c.scope, function(){ + return [ get_mangled(c[0]), c[1].map(walk) ]; + }) : null, + f != null ? f.map(walk) : null ]; + }, + "toplevel": function(body) { + return with_scope(this.scope, function(){ + return [ "toplevel", body.map(walk) ]; + }); + }, + "for-in": function(has_var, name, obj, stat) { + return [ "for-in", has_var, get_mangled(name), walk(obj), walk(stat) ]; + } + }, function() { + return walk(ast_add_scope(ast)); + }); +}; + +// function ast_has_side_effects(ast) { +// var w = ast_walker(); +// var FOUND_SIDE_EFFECTS = {}; +// function _found() { throw FOUND_SIDE_EFFECTS }; +// try { +// w.with_walkers({ +// "new": _found, +// "call": _found, +// "assign": _found, +// "defun": _found, +// "var": _found, +// "const": _found, +// "throw": _found, +// "return": _found, +// "break": _found, +// "continue": _found, +// "case": _found, +// "default": _found, +// "label": _found, +// "function": function(name) { +// if (name) _found(); +// } +// }, function(){ +// w.walk(ast); +// }); +// } catch(ex) { +// if (ex === FOUND_SIDE_EFFECTS) +// return true; +// throw ex; +// } +// }; + +/* -----[ + - 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()} + ]----- */ + +function ast_squeeze(ast, options) { + options = defaults(options, { + make_seqs: true + }); + + var w = ast_walker(), walk = w.walk; + + function is_constant(node) { + return node[0] == "string" || node[0] == "num"; + }; + + function rmblock(block) { + if (block != null && block[0] == "block" && block[1] && block[1].length == 1) + block = block[1][0]; + return block; + }; + + function _lambda(name, args, body) { + return [ this[0], name, args, tighten(body.map(walk)) ]; + }; + + // we get here for blocks that have been already transformed. + // this function does two things: + // 1. discard useless blocks + // 2. join consecutive var declarations + function tighten(statements) { + var cur, prev; + for (var i = 0, ret1 = []; i < statements.length; ++i) { + cur = statements[i]; + if (cur[0] == "block") { + if (cur[1]) { + ret1.push.apply(ret1, cur[1]); + } + } else { + ret1.push(cur); + } + } + prev = null; + for (var i = 0, ret2 = []; i < ret1.length; ++i) { + cur = ret1[i]; + if (prev && ((cur[0] == "var" && prev[0] == "var") || + (cur[0] == "const" && prev[0] == "const"))) { + prev[1] = prev[1].concat(cur[1]); + } else { + ret2.push(cur); + prev = cur; + } + } + if (!options.make_seqs) + return ret2; + prev = null; + for (var i = 0, ret3 = []; i < ret2.length; ++i) { + cur = ret2[i]; + if (!prev) { + if (cur[0] == "stat") { + prev = [ "seq", cur[1] ]; + ret3.push([ "stat", prev ]); + } else { + ret3.push(cur); + } + } else if (cur[0] == "stat") { + prev.push(cur[1]); + } else if (cur[0] == "seq") { + prev.push.apply(prev, slice(cur, 1)); + } else { + prev = null; + ret3.push(cur); + } + } + return ret3.map(walk); + }; + + function best_of(ast1, ast2) { + return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1; + }; + + function aborts(t) { + if (t[0] == "block" && t[1] && t[1].length > 0) + t = t[1][t[1].length - 1]; // interested in last statement + if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw") + return true; + }; + + function make_conditional(c, t, e) { + if (c[0] == "unary-prefix" && c[1] == "!") { + return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ]; + } else { + return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ]; + } + }; + + function empty(b) { + return !b || (b[0] == "block" && (!b[1] || b[1].length == 0)); + }; + + 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 ]; + } + } + }, + "if": function(c, t, e) { + c = walk(c); + t = walk(t); + e = walk(e); + var negated = c[0] == "unary-prefix" && c[1] == "!"; + if (empty(t)) { + if (negated) c = c[2]; + else c = [ "unary-prefix", "!", c ]; + t = e; + e = null; + } + if (empty(e)) { + e = null; + } else { + if (negated) { + c = c[2]; + var tmp = t; t = e; e = tmp; + } + } + if (empty(e) && empty(t)) + return [ "stat", c ]; + var ret = [ "if", c, t, e ]; + if (t[0] == "stat") { + if (e) { + if (e[0] == "stat") { + ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]); + } + } + else { + ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]); + } + } + else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw")) { + 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 = [ "block", ret ]; + } + return ret; + }, + "toplevel": function(body) { + return [ "toplevel", tighten(body.map(walk)) ]; + }, + "switch": function(expr, body) { + return [ "switch", walk(expr), tighten(body.map(walk)) ]; + }, + "function": _lambda, + "defun": _lambda, + "block": function(body) { + if (body) return rmblock([ "block", tighten(body.map(walk)) ]); + }, + "binary": function(op, left, right) { + left = walk(left); + right = walk(right); + var best = [ "binary", op, left, right ]; + if (is_constant(left) && is_constant(right)) { + var val = null; + switch (op) { + case "+": val = left[1] + right[1]; break; + case "*": val = left[1] * right[1]; break; + case "/": val = left[1] / right[1]; break; + case "-": val = left[1] - right[1]; break; + } + if (val != null) { + best = best_of(best, [ typeof val == "string" ? "string" : "num", val ]); + } + } + return best; + }, + "conditional": function(c, t, e) { + if (c[0] == "unary-prefix" && c[1] == "!") + return [ "conditional", walk(c[2]), walk(e), walk(t) ]; + }, + "seq": function() { + if (arguments.length == 1) + return walk(arguments[0]); + } + }, function() { + return walk(ast); + }); + +}; + +/* -----[ re-generate code from the AST ]----- */ + +var DOT_CALL_NO_PARENS = jsp.array_to_hash([ + "name", + "array", + "string", + "dot", + "sub", + "call", + "regexp" +]); + +function gen_code(ast, beautify) { + if (beautify) beautify = defaults(beautify, { + indent_start : 0, + indent_level : 4, + quote_keys : false, + space_colon : false + }); + var indentation = 0, + newline = beautify ? "\n" : "", + space = beautify ? " " : ""; + + function indent(line) { + if (line == null) + line = ""; + if (beautify) + line = repeat_string(" ", beautify.indent_start + indentation * beautify.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)) ]); + }; + + var generators = { + "string": make_string, + "num": function(num) { + var str = num.toString(10), a = [ str ], m; + if (Math.floor(num) === num) { + 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 + 1), + str.substr(str.indexOf("."))); + } + return best_of(a); + }, + "name": make_name, + "toplevel": function(statements) { + return make_block_statements(statements) + .join(newline + newline); + }, + "block": make_block, + "var": function(defs) { + return "var " + add_commas(defs.map(make_1vardef)) + ";"; + }, + "const": function(defs) { + return "const " + add_commas(defs.map(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(args.map(make)) + ")" : ""; + 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(){ + 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_block(body) ]); + }, + "case": function(expr) { + return add_spaces([ "\x08case", make(expr) + ":" ]); + }, + "default": function() { + return "\x08default:"; + }, + "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"), "?", + parenthesize(th, "seq"), ":", + parenthesize(el, "seq") ]); + }, + "assign": function(op, lvalue, rvalue) { + if (op && op !== true) op += "="; + else op = "="; + return add_spaces([ make(lvalue), op, make(rvalue) ]); + }, + "dot": function(expr) { + var out = make(expr), i = 1; + if (!HOP(DOT_CALL_NO_PARENS, expr[0])) + out = "(" + out + ")"; + while (i < arguments.length) + out += "." + make_name(arguments[i++]); + return out; + }, + "call": function(func, args) { + var f = make(func); + if (!HOP(DOT_CALL_NO_PARENS, func[0])) + f = "(" + f + ")"; + return f + "(" + add_commas(args.map(make)) + ")"; + }, + "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(has_var, key, hash, block) { + var out = add_spaces([ "for", "(" ]); + if (has_var) + out += "var "; + out += add_spaces([ make_name(key) + " in " + make(hash) + ")", make(block) ]); + return out; + }, + "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]]) { + left = "(" + left + ")"; + } + if (member(rvalue[0], [ "assign", "conditional", "seq" ]) || + rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]]) { + right = "(" + right + ")"; + } + return add_spaces([ left, operator, right ]); + }, + "unary-prefix": function(operator, expr) { + var val = make(expr); + if (!(HOP(DOT_CALL_NO_PARENS, expr[0]) || expr[0] == "num")) + val = "(" + val + ")"; + return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val; + }, + "unary-postfix": function(operator, expr) { + var val = make(expr); + if (!(HOP(DOT_CALL_NO_PARENS, expr[0]) || expr[0] == "num")) + val = "(" + val + ")"; + return val + operator; + }, + "sub": function(expr, subscript) { + var hash = make(expr); + if (!HOP(DOT_CALL_NO_PARENS, expr[0])) + hash = "(" + hash + ")"; + return hash + "[" + make(subscript) + "]"; + }, + "object": function(props) { + if (props.length == 0) + return "{}"; + return "{" + newline + with_indent(function(){ + return props.map(function(p){ + var key = p[0], val = make(p[1]); + if (beautify && beautify.quote_keys || !is_identifier(key)) + key = make_string(key); + return indent(add_spaces(beautify && beautify.space_colon + ? [ key, ":", val ] + : [ key + ":", val ])); + }).join("," + newline); + }) + newline + indent("}"); + }, + "regexp": function(rx, mods) { + return "/" + rx + "/" + mods; + }, + "array": function(elements) { + if (elements.length == 0) return "[]"; + return "[" + add_commas(elements.map(make)) + "]"; + }, + "stat": function(stmt) { + return make(stmt).replace(/;*\s*$/, ";"); + }, + "seq": function() { + return add_commas(slice(arguments).map(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); + }, + "comment1": function(text) { + return "//" + text + "\n"; + }, + "comment2": function(text) { + return "/*" + text + "*/"; + } + }; + + // 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) { + 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) { + var out = "function"; + if (name) { + out += " " + make_name(name); + } + out += "(" + add_commas(args.map(make_name)) + ")"; + return add_spaces([ out, make_block(body) ]); + }; + + function make_string(str) { + // return '"' + + // str.replace(/\x5c/g, "\\\\") + // .replace(/\r?\n/g, "\\n") + // .replace(/\t/g, "\\t") + // .replace(/\r/g, "\\r") + // .replace(/\f/g, "\\f") + // .replace(/[\b]/g, "\\b") + // .replace(/\x22/g, "\\\"") + // .replace(/[\x00-\x1f]|[\x80-\xff]/g, function(c){ + // var hex = c.charCodeAt(0).toString(16); + // if (hex.length < 2) + // hex = "0" + hex; + // return "\\x" + hex; + // }) + // + '"'; + return JSON.stringify(str); // STILL cheating. + }; + + function make_name(name) { + return name.toString(); + }; + + function make_block_statements(statements) { + 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) + code = code.replace(/;+\s*$/, ""); + a.push(code); + } + } + return a.map(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([ name, "=", make(val) ]); + return name; + }; + + function make(node) { + var type = node[0]; + var gen = generators[type]; + if (!gen) + throw new Error("Can't find generator for \"" + type + "\""); + return gen.apply(type, node.slice(1)); + }; + + var out = make(ast); + if (beautify) { + var rx = repeat_string(" ", beautify.indent_level / 2) + "\x08"; + rx = new RegExp(rx, "g"); + out = out.replace(rx, ""); + } else { + out = out.replace(/\x08/g, ""); + } + return out; +}; + +/* -----[ 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) && + !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); +}; + +/* -----[ Exports ]----- */ + +uglify.ast_walker = ast_walker; +uglify.ast_mangle = ast_mangle; +uglify.ast_squeeze = ast_squeeze; +uglify.gen_code = gen_code; +uglify.ast_add_scope = ast_add_scope; diff --git a/resources/SCXIB.xslt b/resources/SCXIB.xslt new file mode 100644 index 0000000..a33aada --- /dev/null +++ b/resources/SCXIB.xslt @@ -0,0 +1,883 @@ + + + + + + + + + + + /*globals */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + : + + + + + + + + + "" + + , + + + + + + + + + + + + + .CustomClassName + + + + + exampleView: . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + layout: { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00 + + + + + + + + + + bottom: , + + + bottom: 0, + + + + + + height:, + + + + + + right:, + + + + width:, + + + + left:, + top:, + }, + + + + + + + + + + = + + SC.Page.create({ + + mainView: + + + + }); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . = SC.Page.design({ + mainPane: SC.MainPane.design({ + layout: {top:0, left:0, right:0, bottom:0}, + childViews:[ + + + + ] + }) + }); + + + + + + + + . = SC.View.design({ + layout: {top:0, left:0, right:0, bottom:0}, + childViews:[ + + + + ] + }); + + + + + + + + . = SC.PanelPane.design({ + + + + childViews:[ + + + + ] + }); + + + + + + + .design({ + + + + + + + + + childViews:[ + + + + + + ] + + }), + + + SC.View.design({ + + + + + + + + + childViews:[ + + + + + + ] + + }), + + + + + + + + + + SC.LabelView.design({ + + + SC.TextFieldView.design({ + + hint: "", + + + + + + + + + + + value: "" + }), + + + + + SC.SplitView.design({ + + + + + + + + layoutDirection: + + + SC.LAYOUT_VERTICAL + + + SC.LAYOUT_HORIZONTAL + + , + dividerThickness: + + + 1 + + + 5 + + , + topLeftView: + + + + dividerView: SC.SplitDividerView, + bottomRightView: + + + + }), + + + + + SC.ImageView.design({ + + + + + + + + value: "" + }), + + + + + + + + SC.CheckboxView.design({ + + + SC.ButtonView.design({ + + + + + + + + + + + title: "" + }), + + + + + + SC.SelectFieldView.design({ + + + + + + + + objects:[], + disableSort: YES + }), + + + + + + SC.SegmentedView.design({ + + + + + + + + items: [ + + "", + + ] + }), + + + + + SC.SourceListView.design({ + + + + + + + + + + + }), + + + + + SC.ScrollView.design({ + + + + + + + + contentView: + + + }), + + + + + + SC.WebView.design({ + + + + + + + + value: "http://www.sproutcore.com/" + }), + + + + + SC.RadioView.design({ + + + + + + + + items: [ + + "", + + ] + }), + + + + + SC.TabView.design({ + itemTitleKey: "title", + itemValueKey: "value", + + + + + + + + + + + + + + + + + + + + + + nowShowing: ".mainView", + items: [ + + { + title: "", + value: ".mainView" + }, + + ], + }), + + + + + SC.SeparatorView.design({ + layoutDirection: + + + SC.LAYOUT_HORIZONTAL, + + + SC.LAYOUT_VERTICAL, + + + + + + + + + + }), + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ERROR: template implementation of str:replace relies on exsl:node-set(). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file