js2 0.0.10 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/Manifest +42 -0
  2. data/README.md +65 -0
  3. data/Rakefile +19 -35
  4. data/bin/js2 +80 -66
  5. data/config/js2.yml +2 -0
  6. data/js2.gemspec +33 -0
  7. data/lib/js2/{haml_parser.rb → parser/haml.rb} +2 -2
  8. data/lib/js2/{haml_engine.rb → parser/haml_engine.rb} +1 -1
  9. data/lib/js2/parser/lexer.rb +37 -0
  10. data/lib/js2/{parser.rb → parser/tokenizer.rb} +157 -143
  11. data/lib/js2/{replace.rb → ragel/helper.rb} +16 -5
  12. data/lib/js2/ragel/tokenizer.rl +561 -0
  13. data/lib/js2/{tokenizer.rl.erb → ragel/tokenizer.rl.erb} +12 -19
  14. data/lib/js2/standard/factory.rb +289 -0
  15. data/lib/js2/standard/node.rb +75 -0
  16. data/lib/js2/util/compilation.rb +77 -0
  17. data/lib/js2/util/config.rb +84 -0
  18. data/lib/js2/util/exec.rb +34 -0
  19. data/lib/js2/util/file_handler.rb +73 -0
  20. data/lib/js2/{js2bootstrap.js2 → util/js2bootstrap.js2} +12 -68
  21. data/lib/js2/util/processor.rb +88 -0
  22. data/lib/js2/util/rdoc.rb +35 -0
  23. data/lib/js2/{sel_decorator.rb → util/sel_decorator.rb} +11 -1
  24. data/lib/js2.rb +22 -45
  25. data/test/compiled/bar.js +3 -0
  26. data/test/compiled/basic.comp.js +31 -0
  27. data/test/compiled/basic.js +27 -0
  28. data/test/compiled/foo.js +3 -0
  29. data/test/fixtures/bar.js2 +3 -0
  30. data/test/fixtures/basic.js2 +27 -0
  31. data/test/fixtures/basic.js2.haml +4 -0
  32. data/test/fixtures/basic.js2.yml +5 -0
  33. data/test/fixtures/curry.js2 +5 -0
  34. data/test/fixtures/foo.js2 +3 -0
  35. data/test/fixtures/member.js2 +14 -0
  36. data/test/fixtures/private.js2 +5 -0
  37. data/test/fixtures/property.js2 +4 -0
  38. data/test/test_helper.rb +25 -0
  39. data/test/test_js2.rb +43 -0
  40. data/wiki/features.md +106 -0
  41. data/wiki/installation.md +53 -0
  42. metadata +89 -83
  43. data/Changelog +0 -33
  44. data/History.txt +0 -4
  45. data/Manifest.txt +0 -35
  46. data/PostInstall.txt +0 -7
  47. data/README +0 -69
  48. data/README.rdoc +0 -69
  49. data/README.txt +0 -69
  50. data/examples/js2.yml +0 -8
  51. data/examples/test.yml +0 -5
  52. data/lib/javascript/sel_marker.js2 +0 -150
  53. data/lib/javascript/test.js2 +0 -73
  54. data/lib/js2/config.rb +0 -39
  55. data/lib/js2/daemon.rb +0 -35
  56. data/lib/js2/file_handler.rb +0 -91
  57. data/lib/js2/foo.js2.haml +0 -3
  58. data/lib/js2/js2.js +0 -110
  59. data/lib/js2/processor.rb +0 -112
  60. data/lib/js2/test/selenium.rb +0 -119
  61. data/lib/js2/test/selenium_element.rb +0 -234
  62. data/lib/js2/test/selenium_helper.rb +0 -27
  63. data/lib/js2/tree.rb +0 -351
  64. data/lib/js2/universe.rb +0 -123
  65. data/lib/tasks/js2.rake +0 -9
  66. data/website/index.txt +0 -86
  67. /data/{LICENSE → lib/js2/standard/class_node.rb} +0 -0
@@ -74,20 +74,21 @@
74
74
  this[name] = member;
75
75
  },
76
76
 
77
- 'setHTMLCache': function (hash) {
77
+ 'setHTMLAssets': function (hash) {
78
78
  // create temp class
79
79
  var tempClass = function () {};
80
80
 
81
- // look for super htmlCache
81
+ // look for super htmlAssets
82
82
  var par = this.parent;
83
83
  if (par) {
84
- var parCache = par.prototype.htmlCache;
84
+ var parCache = par.prototype.htmlAssets;
85
85
  if (parCache) tempClass.prototype = parCache;
86
86
  }
87
87
 
88
- var htmlCache = new tempClass();
89
- for (var k in hash) htmlCache[k] = hash[k];
90
- this.oo('member', 'htmlCache', htmlCache);
88
+ var htmlAssets = new tempClass();
89
+ for (var k in hash) htmlAssets[k] = hash[k];
90
+ this.oo('member', 'htmlCache', htmlAssets);
91
+ this.oo('member', 'htmlAssets', htmlAssets);
91
92
  },
92
93
 
93
94
  'super': function (member) {
@@ -199,66 +200,6 @@
199
200
 
200
201
  })(window);
201
202
 
202
- class JS2.Observer {
203
- function initialize (owner) {
204
- this.lookupBefore = {};
205
- this.lookupAfter = {};
206
-
207
- this.replaced = {};
208
- this.replacedValues = {};
209
- }
210
-
211
- function replaceFunction (owner, eventName) {
212
- if (this.replaced[eventName]) return;
213
-
214
- this.replacedValues[eventName] = owner[eventName];
215
- this.replaced[eventName] = true;
216
- owner[eventName] = this.getTrigger(eventName);
217
- }
218
-
219
- function trigger (owner, eventName, args) {
220
- var beforeChain = this.lookupBefore[eventName];
221
- if (beforeChain) this.executeChain(beforeChain, args);
222
-
223
- var funct = this.replacedValues[eventName];
224
- if (funct) funct.apply(owner, args);
225
-
226
- var afterChain = this.lookupAfter[eventName];
227
- if (afterChain) this.executeChain(afterChain, args);
228
- }
229
-
230
- function addListener (eventName, funct, before) {
231
- var lookup = before ? this.lookupBefore : this.lookupAfter;
232
-
233
- var chain = lookup[eventName] = lookup[eventName] || [];
234
- chain.push(funct);
235
- }
236
-
237
- private
238
-
239
- function getTrigger (eventName) {
240
- return function () { this.__observer.trigger(this, eventName, arguments); };
241
- }
242
-
243
- function executeChain (chain, args) {
244
- foreach (var f:i in chain) if (f) f.apply(this, args);
245
- }
246
- }
247
-
248
- module JS2.Observable {
249
- function addListener (eventName, funct, before) {
250
- if (! this.__observer) this.__observer = new Factual.Core.Observer();
251
-
252
- var id = this.__observer.addListener(eventName, funct, before);
253
- this.__observer.replaceFunction(this, eventName);
254
- return id;
255
- }
256
-
257
- function triggerEvent (eventName, args) {
258
- if (this.__observer) this.__observer.trigger(this, eventName, args);
259
- }
260
- }
261
-
262
203
 
263
204
  class JS2.App.Notifier {
264
205
  var autoInc = 1;
@@ -340,7 +281,6 @@ class JS2.App.Notifier {
340
281
 
341
282
 
342
283
  class JS2.App {
343
- include JS2.Observer;
344
284
 
345
285
  function start (options) {
346
286
  // hack to get notifier
@@ -401,7 +341,11 @@ class JS2.App {
401
341
  if (!config['class']) alert("Invalid class defined for " + name + ':' + config['class']);
402
342
  var klass = JS2.OO.get(config['class']);
403
343
 
404
- components[config.name] = new klass();
344
+ if (klass) {
345
+ components[config.name] = new klass();
346
+ } else if (console) {
347
+ console.log('class "' + config.name + '" was not found."');
348
+ }
405
349
  this.register(components[config.name]);
406
350
  }
407
351
 
@@ -0,0 +1,88 @@
1
+ class JS2::Util::Processor
2
+ attr_accessor :errors
3
+
4
+ def initialize (config)
5
+ @file_handler = config.file_handler
6
+ @lexer = config.lexer
7
+ @factory = config.node_factory
8
+ @haml_engine = config.haml_engine
9
+ @haml_parser = JS2::Parser::Haml.new(config.haml_engine, config.haml_vars)
10
+
11
+ @lookup = Hash.new
12
+
13
+ @config = config
14
+ end
15
+
16
+ def process!
17
+ @errors = []
18
+
19
+ ret = { :processed => [], :changed => [], :errors => @errors, :klasses => [], :pages => [] }
20
+ ret[:changed] = @file_handler.needs_update
21
+ return ret unless ret[:changed].any?
22
+
23
+ pages = ret[:pages]
24
+ klasses = Hash.new
25
+
26
+ @file_handler.get_files(:js2).each do |file|
27
+ begin
28
+ page = @lexer.parse_file(file, @factory)
29
+ page.klasses.each do |k|
30
+ (klasses[k.name] ||= []) << page.file
31
+ end
32
+
33
+ ret[:klasses] += page.klasses
34
+
35
+ pages << page
36
+
37
+ outfile = @file_handler.outfile(page.file)
38
+ outdir = File.dirname(outfile)
39
+
40
+ FileUtils.mkdir_p(outdir)
41
+ File.open(@file_handler.outfile(page.file), 'w') { |f| f << page.to_s() }
42
+ rescue Exception => e
43
+ @errors << [ "Can't compile #{file}", e ]
44
+ end
45
+ end
46
+
47
+ js2_file = File.dirname(__FILE__) + '/js2bootstrap.js2'
48
+ js2_page = @lexer.parse_file(js2_file, @factory)
49
+ out_file = @file_handler.out_dir + '/js2bootstrap.js'
50
+ FileUtils.mkdir_p(@file_handler.out_dir)
51
+ File.open(out_file, 'w') { |f| f << js2_page.to_s() }
52
+
53
+ @file_handler.get_files(:haml).each do |file|
54
+ begin
55
+ result = @haml_parser.parse(file)
56
+ result.keys.each do |klass_name|
57
+ hash = result[klass_name]
58
+
59
+ out = []
60
+ hash.keys.sort.each do |key|
61
+ out << %{"#{key}":#{hash[key]}}
62
+ end
63
+
64
+ if files = klasses[klass_name]
65
+ outfile = @file_handler.outfile(files.first)
66
+ File.open(outfile, 'a') { |f| f << "#{klass_name}.oo('setHTMLAssets', {#{out.join(',')}});" }
67
+ end
68
+ end
69
+ rescue Exception => e
70
+ @errors << [ "Can't compile #{file}", e ]
71
+ end
72
+ end
73
+
74
+ @file_handler.get_files(:yml).each do |file|
75
+ begin
76
+ comps = JS2::Util::Compilation.parse(file, @file_handler)
77
+ comps.each do |c|
78
+ c.compile(klasses, errors)
79
+ end
80
+ rescue Exception => e
81
+ @errors << [ "Can't compile #{file}", e ]
82
+ end
83
+ end
84
+
85
+ return ret
86
+ end
87
+
88
+ end
@@ -0,0 +1,35 @@
1
+ class JS2::Util::Rdoc
2
+
3
+ def self.build (pages, file_handler)
4
+
5
+ pages.each do |p|
6
+ str = ''
7
+ p.klasses.each do |k|
8
+ if k.comment
9
+ str << k.comment.clean.gsub(/^/, "# ") if k.comment
10
+ end
11
+ str << "class #{k.name.gsub(/\./, '::')}\n"
12
+ puts k.name
13
+
14
+ k.methods.each_with_index do |m,i|
15
+ puts ' - ' + m.name
16
+ str << m.comment.clean.gsub(/^/, " # ") if m.comment
17
+ str << " def #{m.name} (#{m.args})\n#{m.to_s.gsub(/^/, ' #')} end\n"
18
+ end
19
+
20
+ str << "end\n"
21
+ end
22
+
23
+ outfile = file_handler.docfile(p.file)
24
+ outdir = File.dirname(outfile)
25
+ FileUtils.mkdir_p(outdir)
26
+ File.open(outfile, 'w') { |f| f << str }
27
+ end
28
+
29
+ # TODO make this portable
30
+ jamis = File.dirname(__FILE__) + '/jamis.rb'
31
+ system("rdoc --template #{jamis} --extension js=rb #{file_handler.doc_dir}")
32
+ end
33
+
34
+
35
+ end
@@ -1,6 +1,6 @@
1
1
  require 'json'
2
2
 
3
- class JS2::SelDecorator
3
+ class JS2::Util::SelDecorator
4
4
 
5
5
  def initialize ()
6
6
  @references = Hash.new
@@ -12,6 +12,16 @@ class JS2::SelDecorator
12
12
  @in_class = false
13
13
  end
14
14
 
15
+ def decorate (str, node)
16
+ if node.is_a?(JS2::Standard::ClassNode)
17
+ return translate(str, nil, true)
18
+ elsif node.is_a?(JS2::Standard::StuffNode)
19
+ return translate(str, nil, false)
20
+ else
21
+ return str
22
+ end
23
+ end
24
+
15
25
  def translate (str, scope = nil, in_class = false)
16
26
  @scope = scope
17
27
  @in_class = in_class
data/lib/js2.rb CHANGED
@@ -1,55 +1,32 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
-
4
-
5
1
  module JS2
6
- VERSION = '0.0.10'
7
- module Test
8
- end
9
2
  end
10
3
 
11
- require 'js2/config'
12
- require 'js2/daemon'
13
- require 'js2/processor'
14
- require 'js2/sel_decorator'
15
-
16
- require 'js2/tree'
17
- require 'js2/parser'
18
- require 'js2/file_handler'
19
- require 'js2/universe'
20
-
21
- require 'js2/test/selenium'
22
- require 'js2/test/selenium_element'
23
- require 'js2/test/selenium_helper'
24
-
25
- $:.unshift File.dirname(__FILE__)
4
+ module JS2::Parser
5
+ end
26
6
 
27
- begin
28
- require 'rubygems'
29
- require 'json'
30
- rescue Exception => e
31
- raise "JS2 Requires RubyGems and JSON gems"
7
+ module JS2::Standard
32
8
  end
33
9
 
34
- begin
35
- require 'haml'
36
- require 'sass'
37
- require 'js2/haml_parser'
38
- require 'js2/haml_engine'
39
- rescue Exception => e
40
- puts "HAML is not installed deactivating support"
10
+ module JS2::Util
41
11
  end
42
12
 
43
13
 
44
- if JS2::HamlParser
45
- # for haml filter
46
- module JS2
47
- include Haml::Filters::Base
14
+ dir = File.dirname(__FILE__)
48
15
 
49
- def render (text)
50
- parser = ::JS2::Parser.new
51
- tree = parser.parse(text)
52
- return '<script language="Javascript">' + tree.to_s + "</script>"
53
- end
54
- end
55
- end
16
+ # compiling stuff
17
+ require "#{dir}/js2/parser/lexer"
18
+ require "#{dir}/js2/parser/tokenizer"
19
+ require "#{dir}/js2/parser/haml_engine"
20
+ require "#{dir}/js2/parser/haml"
21
+
22
+ # standard suite
23
+ require "#{dir}/js2/standard/node"
24
+ require "#{dir}/js2/standard/class_node"
25
+ require "#{dir}/js2/standard/factory"
26
+
27
+ # util stuff
28
+ require "#{dir}/js2/util/processor"
29
+ require "#{dir}/js2/util/file_handler"
30
+ require "#{dir}/js2/util/config"
31
+ require "#{dir}/js2/util/compilation"
32
+ require "#{dir}/js2/util/rdoc"
@@ -0,0 +1,3 @@
1
+ JS2.OO.createClass("Bar"); (function (K,Package) {var self=K; var _super=JS2.OO['super'];
2
+
3
+ })(Bar, null);
@@ -0,0 +1,31 @@
1
+
2
+ /*
3
+ * Comments
4
+ * This is a comment
5
+ */
6
+ JS2.OO.createClass("Test.Foo"); (function (K,Package) {var self=K; var _super=JS2.OO['super'];
7
+ K.oo('accessor', [ 'testAcc' ]);
8
+ K.oo('property', [ 'testProp' ]);
9
+
10
+ /*
11
+ * hello
12
+ */
13
+
14
+ K.oo('method', "testFunct", function () {
15
+
16
+ });
17
+
18
+ K.oo('method', "testForeach", function () {
19
+ for (var i=0,ele,i__arr=elements,i__len=i__arr.length; (ele=i__arr[i]) || i<i__len; i++){
20
+ for (var i=0,yo,i__arr=yos,i__len=i__arr.length; (yo=i__arr[i]) || i<i__len; i++){
21
+
22
+ }
23
+
24
+ }
25
+ });
26
+
27
+ })(Test.Foo, Test);Test.Foo.oo('setHTMLCache', {"main":function(){return "<div class='hello'>world</div>"}});JS2.OO.createClass("Bar"); (function (K,Package) {var self=K; var _super=JS2.OO['super'];
28
+
29
+ })(Bar, null);JS2.OO.createClass("Foo"); (function (K,Package) {var self=K; var _super=JS2.OO['super'];
30
+
31
+ })(Foo, null);
@@ -0,0 +1,27 @@
1
+
2
+ /*
3
+ * Comments
4
+ * This is a comment
5
+ */
6
+ JS2.OO.createClass("Test.Foo"); (function (K,Package) {var self=K; var _super=JS2.OO['super'];
7
+ K.oo('accessor', [ 'testAcc' ]);
8
+ K.oo('property', [ 'testProp' ]);
9
+
10
+ /*
11
+ * hello
12
+ */
13
+
14
+ K.oo('method', "testFunct", function () {
15
+
16
+ });
17
+
18
+ K.oo('method', "testForeach", function () {
19
+ for (var i=0,ele,i__arr=elements,i__len=i__arr.length; (ele=i__arr[i]) || i<i__len; i++){
20
+ for (var i=0,yo,i__arr=yos,i__len=i__arr.length; (yo=i__arr[i]) || i<i__len; i++){
21
+
22
+ }
23
+
24
+ }
25
+ });
26
+
27
+ })(Test.Foo, Test);Test.Foo.oo('setHTMLCache', {"main":function(){return "<div class='hello'>world</div>"}});
@@ -0,0 +1,3 @@
1
+ JS2.OO.createClass("Foo"); (function (K,Package) {var self=K; var _super=JS2.OO['super'];
2
+
3
+ })(Foo, null);
@@ -0,0 +1,3 @@
1
+ class Bar {
2
+
3
+ }
@@ -0,0 +1,27 @@
1
+
2
+ /*
3
+ * Comments
4
+ * This is a comment
5
+ */
6
+ class Test.Foo {
7
+ accessor testAcc;
8
+ property testProp;
9
+
10
+ /*
11
+ * hello
12
+ */
13
+
14
+ function testFunct () {
15
+
16
+ }
17
+
18
+ function testForeach () {
19
+ foreach (var ele:i in elements) {
20
+ foreach (var yo:i in yos) {
21
+
22
+ }
23
+
24
+ }
25
+ }
26
+
27
+ }
@@ -0,0 +1,4 @@
1
+ Test.Foo
2
+ main
3
+ .hello
4
+ world
@@ -0,0 +1,5 @@
1
+ Test.Foo:
2
+ make_compilation: true
3
+ template:
4
+ - class: Bar
5
+ - class: Foo
@@ -0,0 +1,5 @@
1
+ curry (hello) with (this) {
2
+
3
+ }
4
+
5
+ hello.foo(curry (hello) with (this) { });
@@ -0,0 +1,3 @@
1
+ class Foo {
2
+
3
+ }
@@ -0,0 +1,14 @@
1
+ class TestMember {
2
+
3
+ var WIDTH = 84;
4
+ var MENU_ACTIONS = [ 'sortData', 'sortData', 'hideColumn' ];
5
+
6
+ var arr = [ 'hello', 'world' ];
7
+ var one, two, three;
8
+ var foo = {};
9
+ var bar;
10
+ var foo1 = 'hello';
11
+ var bar1 = {
12
+ hello: "world"
13
+ };
14
+ }
@@ -0,0 +1,5 @@
1
+ class TestPrivate {
2
+
3
+ private
4
+
5
+ }
@@ -0,0 +1,4 @@
1
+ class TestProperty {
2
+ property foo;
3
+ property foo, bar;
4
+ }
@@ -0,0 +1,25 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/js2'
4
+ JS2_TEST_DIR = File.dirname(__FILE__)
5
+
6
+ class Test::Unit::TestCase
7
+ def js2_fixture (name)
8
+ return JS2_TEST_DIR + "/fixtures/#{name}.js2"
9
+ end
10
+
11
+ def js_read_fixture (name)
12
+ return File.read(JS2_TEST_DIR + "/fixtures/#{name}.js").chomp
13
+ end
14
+
15
+ def compare_dir (dir1, dir2)
16
+ files1 = Dir["#{dir1}/*.js"]
17
+
18
+ files1.each do |file|
19
+ content1 = File.read(file)
20
+ file2 = file.sub(/^#{dir1}/, dir2)
21
+ content2 = File.read(file2)
22
+ assert_equal(content1.chomp, content2.chomp, "Comparing #{file} and #{file2}")
23
+ end
24
+ end
25
+ end
data/test/test_js2.rb ADDED
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestJs2 < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @lexer = JS2::Parser::Lexer.new
7
+ @factory = JS2::Standard::Factory.new
8
+ end
9
+
10
+ def test_processor
11
+
12
+ config = JS2::Util::Config.new
13
+ fh = config.file_handler
14
+ fh.js2_dir = './test/fixtures'
15
+ fh.out_dir = './test/out'
16
+ fh.haml_dir = './test/fixtures'
17
+
18
+ system("rm -rf #{fh.out_dir}")
19
+
20
+ processor = JS2::Util::Processor.new(config)
21
+ ret1 = processor.process!
22
+ ret2 = processor.process!
23
+ assert_equal(ret2[:changed].any?, false)
24
+
25
+ compare_dir(fh.out_dir, './test/compiled')
26
+ end
27
+
28
+ def test_rdoc
29
+
30
+ config = JS2::Util::Config.new
31
+ fh = config.file_handler
32
+ fh.js2_dir = './test/fixtures'
33
+ fh.out_dir = './test/out'
34
+ fh.haml_dir = './test/fixtures'
35
+ fh.doc_dir = './test/doc'
36
+ system("rm -rf #{fh.out_dir}")
37
+
38
+ processor = JS2::Util::Processor.new(config)
39
+ ret = processor.process!
40
+ JS2::Util::Rdoc.build(ret[:pages], fh)
41
+ end
42
+
43
+ end
data/wiki/features.md ADDED
@@ -0,0 +1,106 @@
1
+ ## Object Oriented
2
+
3
+ ### Class Definition
4
+
5
+ > class Foo {
6
+ > function method () {
7
+ > }
8
+ > }
9
+
10
+ ### Inheritance
11
+
12
+ > class Vehicle {
13
+ > function drive () {
14
+ > alert('drive');
15
+ > }
16
+ > }
17
+ >
18
+ > class Car extends Vehicle {
19
+ > }
20
+
21
+ ### Getters and Setters
22
+
23
+ > class Duck {
24
+ > property color; // adds getColor() and setColor()
25
+ > }
26
+
27
+ ### Mixins (Ruby's multiple inheritance solution)
28
+
29
+ > module Flyable {
30
+ > function fly () {
31
+ > alert('Flying!');
32
+ > }
33
+ > }
34
+ >
35
+ > class Duck {
36
+ > include Flyable;
37
+ > }
38
+
39
+ ### AOP (Aspect Oriented Programming)
40
+
41
+ > var me = new Human();
42
+ > me.addListener('walk', function () { alert('walking') });
43
+
44
+ ### Static Methods
45
+
46
+ > class Human {
47
+ > static function getCount () {
48
+ > return this.count;
49
+ > }
50
+ > static function create () {
51
+ > if (this.count) {
52
+ > this.count++;
53
+ > } else {
54
+ > this.count = 1;
55
+ > }
56
+ > return new this();
57
+ > }
58
+ > }
59
+
60
+ ## Syntactic Sugar
61
+
62
+ ### Currying
63
+
64
+ > var nonScoped = [ ... lots of data .. ];
65
+ > var submitBtn = new Button();
66
+ > var ele = document.getElementById('submitBtn');
67
+ > ele.onClick = curry (evt) with (submitBtn) {
68
+ > submitBtn.click();
69
+ > };
70
+
71
+ ### Foreach
72
+
73
+ > foreach (var item in array) alert(item);
74
+ > foreach (var item:i in array) alert(i + ' is ' + item);
75
+ > curry (arg1, arg2) with (scopeVar1, scopeVar2) { };
76
+
77
+ ##Other Features
78
+
79
+ ### Templating in HAML/SASS (useful for ajax applications)
80
+
81
+ > //--- in uiBuilder.js2.haml
82
+ > UIBuilder
83
+ > button(name)
84
+ > %div.button= "#name#"
85
+ >
86
+ > //--- in uiBuilder.js2
87
+ > class UIBuilder {
88
+ > function getButton (name) {
89
+ > this.htmlCache.button(name);
90
+ > }
91
+ > }
92
+ >
93
+ > //--- in page.html
94
+ > var ui = new UIBuilder();
95
+ > var ele = document.getElementById('buttonContainer');
96
+ > ele.innerHTML = ui.button('my button');
97
+
98
+ ### Selenium Testing Integration
99
+
100
+ ... Coming soon ...
101
+
102
+ This is a little bit harder to explain, and is only for Ruby Selenium RC developers. The idea is that complex javascript applications usually reference most important DOM elements in javascript for things such as altering html or adding event handlers. What selenium integration in js2 means is that one can use annotations in his/her code to "mark" DOM elements instead of using xpaths.
103
+
104
+ One of the pain points in Selenium testing is that the xpaths are always changing with each iteration of the view layer. In a complex javascript application, one has to maintain the references to DOM objects in javascript anyway, so this would be an easier way to maintain the Selenium references to the DOM.
105
+
106
+ While this is available, its not quite ready for public consumption.