js2 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Changelog +0 -0
- data/History.txt +4 -0
- data/LICENSE +0 -0
- data/Manifest.txt +43 -0
- data/PostInstall.txt +7 -0
- data/README +0 -0
- data/README.rdoc +48 -0
- data/Rakefile +42 -0
- data/bin/js2 +61 -0
- data/examples/js2.yml +8 -0
- data/examples/test.yml +5 -0
- data/lib/javascript/sel_marker.js2 +139 -0
- data/lib/javascript/test.js2 +73 -0
- data/lib/js2/ast/class_node.rb +105 -0
- data/lib/js2/ast/comment_node.rb +2 -0
- data/lib/js2/ast/haml_node.rb +22 -0
- data/lib/js2/ast/include_node.rb +11 -0
- data/lib/js2/ast/inherited_node.rb +7 -0
- data/lib/js2/ast/member_node.rb +18 -0
- data/lib/js2/ast/method_node.rb +29 -0
- data/lib/js2/ast/module_node.rb +6 -0
- data/lib/js2/ast/node.rb +14 -0
- data/lib/js2/ast/nodes.rb +123 -0
- data/lib/js2/ast/stuff_node.rb +6 -0
- data/lib/js2/config.rb +34 -0
- data/lib/js2/daemon.rb +35 -0
- data/lib/js2/decorator/app.rb +7 -0
- data/lib/js2/decorator/cleanser.rb +54 -0
- data/lib/js2/decorator/standard.rb +127 -0
- data/lib/js2/decorator/test.rb +148 -0
- data/lib/js2/parser/fast.rb +3959 -0
- data/lib/js2/parser/haml.rb +123 -0
- data/lib/js2/process/file_handler.rb +84 -0
- data/lib/js2/process/haml_engine.rb +19 -0
- data/lib/js2/process/universe.rb +57 -0
- data/lib/js2/processor.rb +150 -0
- data/lib/js2/test/selenium.rb +111 -0
- data/lib/js2/test/selenium_element.rb +220 -0
- data/lib/js2/test/selenium_helper.rb +27 -0
- data/lib/js2.rb +95 -0
- data/lib/tasks/js2.rake +9 -0
- data/meta/c_tokenizer.rl.erb +322 -0
- data/meta/replace.rb +126 -0
- metadata +110 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
|
2
|
+
# ------------------------------------------------------------------------------
|
3
|
+
# AKA Tokens
|
4
|
+
# Hooks into Ragel
|
5
|
+
# this system could be more elegant, but we choose iteration over OO for
|
6
|
+
# efficiency
|
7
|
+
# HOOKS:
|
8
|
+
# << # to simulate <<
|
9
|
+
# ------------------------------------------------------------------------------
|
10
|
+
class JS2::AST::Nodes < Array
|
11
|
+
CLOSE_CLASS_LEVEL = 0
|
12
|
+
CLOSE_METHOD_LEVEL = 1
|
13
|
+
|
14
|
+
STATE_IDX = 0
|
15
|
+
STRING_IDX = 1
|
16
|
+
METHODS_IDX = 2
|
17
|
+
|
18
|
+
attr_accessor :stack, :filename, :line_number
|
19
|
+
|
20
|
+
def initialize (filename, str)
|
21
|
+
@cleanser = JS2::Decorator::Cleanser.new
|
22
|
+
@str = str
|
23
|
+
@filename = filename
|
24
|
+
@line_number = 0
|
25
|
+
@mems = []
|
26
|
+
@static_mems = []
|
27
|
+
@includes = []
|
28
|
+
@foreach = []
|
29
|
+
@comments = []
|
30
|
+
@curries = []
|
31
|
+
@in_class = false
|
32
|
+
|
33
|
+
@static = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def do_next (type, begin_index, end_index, line)
|
38
|
+
val = @str[begin_index..end_index]
|
39
|
+
|
40
|
+
if type == :FOREACH
|
41
|
+
@foreach.push([begin_index, end_index, val])
|
42
|
+
|
43
|
+
elsif type == :CURRY
|
44
|
+
@curries.push([begin_index, end_index, val])
|
45
|
+
|
46
|
+
elsif type != :COMMENT
|
47
|
+
offset = 0
|
48
|
+
@foreach.reverse.each do |f|
|
49
|
+
s = f[0] - begin_index
|
50
|
+
e = f[1] - begin_index
|
51
|
+
before = val[s..e]
|
52
|
+
after = @cleanser.do_foreach(val[s..e])
|
53
|
+
offset += after.length - before.length
|
54
|
+
|
55
|
+
val[s..e] = after
|
56
|
+
end
|
57
|
+
|
58
|
+
@curries.reverse.each do |f|
|
59
|
+
s = f[0] - begin_index + offset
|
60
|
+
e = f[1] - begin_index + offset
|
61
|
+
val[s..e] = @cleanser.do_curry(val[s..e])
|
62
|
+
end
|
63
|
+
|
64
|
+
@foreach = []
|
65
|
+
@curries = []
|
66
|
+
end
|
67
|
+
|
68
|
+
if type == :METHOD
|
69
|
+
mems << JS2::AST::MethodNode.new(@filename, line, val)
|
70
|
+
reset_static
|
71
|
+
|
72
|
+
elsif type == :MEMBER
|
73
|
+
mems << JS2::AST::MemberNode.new(@filename, line, val)
|
74
|
+
reset_static
|
75
|
+
|
76
|
+
elsif type == :PROPERTY
|
77
|
+
@mems += JS2::AST::MethodNode.properties(@filename, line, val)
|
78
|
+
reset_static
|
79
|
+
|
80
|
+
elsif type == :CLASS
|
81
|
+
self << JS2::AST::ClassNode.new(@filename, line, val, @mems, @includes, @static_mems)
|
82
|
+
|
83
|
+
@static_mems = []
|
84
|
+
@mems = []
|
85
|
+
@includes = []
|
86
|
+
|
87
|
+
elsif type == :STUFF
|
88
|
+
self << JS2::AST::StuffNode.new(@filename, line, val)
|
89
|
+
@mems = []
|
90
|
+
|
91
|
+
elsif type == :COMMENT
|
92
|
+
mems << JS2::AST::CommentNode.new(@filename, line, val)
|
93
|
+
|
94
|
+
elsif type == :INCLUDE
|
95
|
+
@includes << JS2::AST::IncludeNode.new(@filename, line, val)
|
96
|
+
|
97
|
+
elsif type == :MODULE
|
98
|
+
self << JS2::AST::ModuleNode.new(@filename, line, val, @mems, [], @static_mems)
|
99
|
+
|
100
|
+
@static_mems = []
|
101
|
+
@mems = []
|
102
|
+
@includes = []
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
def mems
|
108
|
+
if @static
|
109
|
+
return @static_mems
|
110
|
+
else
|
111
|
+
return @mems
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_static
|
116
|
+
@static = true
|
117
|
+
end
|
118
|
+
|
119
|
+
def reset_static
|
120
|
+
@static = false
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
data/lib/js2/config.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class JS2::Config
|
4
|
+
DEFAULTS = {
|
5
|
+
:framework => 'jquery',
|
6
|
+
:highlight => true,
|
7
|
+
:js2_dir => './js2',
|
8
|
+
:haml_engine_class => false,
|
9
|
+
:reference_dir => nil,
|
10
|
+
:write_dir => './public/javascripts',
|
11
|
+
:test_mode => false,
|
12
|
+
:js2_haml_dir => './js2',
|
13
|
+
:haml_dir => './app/views',
|
14
|
+
:selenium => { }
|
15
|
+
}
|
16
|
+
|
17
|
+
attr_accessor *DEFAULTS.keys
|
18
|
+
|
19
|
+
def initialize (hash = nil)
|
20
|
+
DEFAULTS.each_pair do |k,v|
|
21
|
+
self.send(k.to_s + '=', v)
|
22
|
+
end
|
23
|
+
|
24
|
+
load_hash(hash) if hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_hash(hash)
|
28
|
+
DEFAULTS.keys.each do |k|
|
29
|
+
k = k.to_s
|
30
|
+
self.send(k + '=', hash[k]) if hash.has_key?(k)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/lib/js2/daemon.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class JS2::Daemon
|
2
|
+
attr_accessor :haml_parser, :decorator, :parser, :universe, :haml_engine
|
3
|
+
INTERVAL = 1
|
4
|
+
|
5
|
+
def initialize (processor)
|
6
|
+
@processor = processor
|
7
|
+
end
|
8
|
+
|
9
|
+
def run (n=nil)
|
10
|
+
n ||= -1
|
11
|
+
i = 0
|
12
|
+
while i != n
|
13
|
+
i += 1
|
14
|
+
|
15
|
+
if check_files
|
16
|
+
yield if block_given?
|
17
|
+
else
|
18
|
+
sleep INTERVAL
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def check_files
|
27
|
+
unless @processor.fh.need_update!
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
@processor.write_files
|
31
|
+
rescue Exception => e
|
32
|
+
print e.to_s
|
33
|
+
print "Error in processing files"
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class JS2::Decorator::Cleanser
|
2
|
+
@@enum = 0
|
3
|
+
|
4
|
+
def cleanse (str)
|
5
|
+
do_foreach(str)
|
6
|
+
end
|
7
|
+
|
8
|
+
def do_foreach (str)
|
9
|
+
# TODO support types
|
10
|
+
# TODO foreach:pair (var key,item in itemHash)
|
11
|
+
# TODO foreach:time (var i in 25)
|
12
|
+
# TODO foreach:key (var key in hash)
|
13
|
+
#
|
14
|
+
# SUPPORTED foreach (var item:i in items)
|
15
|
+
if m = str.match(%r|foreach\s*(:([^\s]))?\s*\(\s*var\s+([^\s\:]+)\s*(:?([^\s]))?\s+in\s+([^\s]+)\s*\)|)
|
16
|
+
type = m[2]
|
17
|
+
decl = m[3]
|
18
|
+
it = m[5] || ('it_' + (@@enum += 1).to_s)
|
19
|
+
arr = m[6]
|
20
|
+
len = it + '_len'
|
21
|
+
|
22
|
+
return "for (var #{decl},#{it}=0,#{len}=#{arr}.length; (#{decl}=#{arr}[#{it}]) || #{it}<#{len}; #{it}++)"
|
23
|
+
else
|
24
|
+
return str
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def do_curry (str)
|
29
|
+
|
30
|
+
# decl code
|
31
|
+
if m = str.match(%r|^curry\s*([^\{]*)?(\{.*)$|m)
|
32
|
+
decl = m[1].strip
|
33
|
+
code = m[2]
|
34
|
+
|
35
|
+
scoped_vars = ''
|
36
|
+
args = ''
|
37
|
+
|
38
|
+
if m = decl.match(%r|with\s+\(([^)]*)\)|)
|
39
|
+
vars = m[1].split(',').collect { |v| v.strip }
|
40
|
+
scoped_vars = vars.collect { |v| v == 'this' ? 'self' : v }.join(', ')
|
41
|
+
in_scoped_vars = vars.join(', ')
|
42
|
+
end
|
43
|
+
|
44
|
+
if m = decl.match(%r|^\s*\(([^)]*)\)|)
|
45
|
+
args = m[1].strip
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
return %{(function (#{scoped_vars}) { return function (#{args}) #{code} })(#{in_scoped_vars})}
|
50
|
+
else
|
51
|
+
return str
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class JS2::Decorator::Standard
|
5
|
+
def draw_nodes (nodes)
|
6
|
+
str = ''
|
7
|
+
nodes.each do |node|
|
8
|
+
if node.is_a? JS2::AST::StuffNode
|
9
|
+
str << draw_stuff(node)
|
10
|
+
elsif node.is_a? JS2::AST::ClassNode
|
11
|
+
str << draw_class(node)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
return str
|
15
|
+
end
|
16
|
+
|
17
|
+
def draw_classes (nodes)
|
18
|
+
ret = []
|
19
|
+
visited = Hash.new
|
20
|
+
funct = "function () { if (this.initialize) this.initialize.apply(this, arguments); }"
|
21
|
+
nodes.select { |n| n.is_a? JS2::AST::ClassNode }.sort { |a,b| a.name <=> b.name }.each do |n|
|
22
|
+
splitted = n.name.split(/\./)
|
23
|
+
running_name = []
|
24
|
+
|
25
|
+
splitted.each_with_index do |sub_name, i|
|
26
|
+
running_name << sub_name
|
27
|
+
name = running_name.join('.')
|
28
|
+
next if visited[name]
|
29
|
+
visited[name] = true
|
30
|
+
|
31
|
+
if i == 0
|
32
|
+
ret << "var #{name}=#{funct};"
|
33
|
+
else
|
34
|
+
ret << " #{name}=#{funct};"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
return ret.join("\n")
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def draw_method (node)
|
45
|
+
return %{ // #{node.filename}:#{node.line_number}
|
46
|
+
#{node.name}:function #{node.text}}
|
47
|
+
end
|
48
|
+
|
49
|
+
def draw_inherited (node)
|
50
|
+
return " #{node.name}: #{node.extends}.prototype.#{node.name}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def draw_haml (node)
|
54
|
+
text = node.attrs.collect do |attr|
|
55
|
+
%{#{attr[0]}:#{attr[1]}}
|
56
|
+
end.join(',')
|
57
|
+
return %{ htmlCache: {#{text}}}
|
58
|
+
end
|
59
|
+
|
60
|
+
def draw_comment (node)
|
61
|
+
return ' ' + node.text
|
62
|
+
end
|
63
|
+
|
64
|
+
def draw_member (node)
|
65
|
+
if node.is_a? JS2::AST::MethodNode
|
66
|
+
return draw_method(node)
|
67
|
+
elsif node.is_a? JS2::AST::InheritedNode
|
68
|
+
return draw_inherited(node)
|
69
|
+
elsif node.is_a? JS2::AST::HamlNode
|
70
|
+
return draw_haml(node)
|
71
|
+
elsif node.is_a? JS2::AST::CommentNode
|
72
|
+
return draw_comment(node)
|
73
|
+
end
|
74
|
+
|
75
|
+
return %{ // #{node.filename}:#{node.line_number}
|
76
|
+
#{node.name}:#{node.text}}
|
77
|
+
end
|
78
|
+
|
79
|
+
def draw_class (node)
|
80
|
+
meta = ''
|
81
|
+
if node.extends
|
82
|
+
meta << "_parent:#{node.extends},"
|
83
|
+
end
|
84
|
+
|
85
|
+
members = node.members
|
86
|
+
|
87
|
+
meta << "_klass:'#{node.name}'"
|
88
|
+
meta << ',' unless members.empty?
|
89
|
+
|
90
|
+
handle_super = ''
|
91
|
+
node.super_hash.each_pair do |k,v|
|
92
|
+
handle_super << "#{node.name}.prototype.#{k}._super=#{v}.prototype.#{k};\n"
|
93
|
+
end
|
94
|
+
|
95
|
+
handle_static =
|
96
|
+
node.statics.collect do |s|
|
97
|
+
handle_static = "#{node.name}.#{s.name}=function #{s.text};"
|
98
|
+
end.join("\n")
|
99
|
+
|
100
|
+
member_js = [ ]
|
101
|
+
member_str = ''
|
102
|
+
members.each do |m|
|
103
|
+
member_str << draw_member(m)
|
104
|
+
if m.is_a? JS2::AST::CommentNode
|
105
|
+
member_str << "\n"
|
106
|
+
else
|
107
|
+
member_js.push(member_str)
|
108
|
+
member_str = ''
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
return <<-END
|
113
|
+
// #{node.filename}:#{node.line_number}
|
114
|
+
#{node.name}.prototype={#{meta}
|
115
|
+
#{member_js.join(",\n") }
|
116
|
+
};
|
117
|
+
// adding super
|
118
|
+
#{handle_super}
|
119
|
+
#{handle_static}
|
120
|
+
#{node.name}.prototype['class']=#{node.name};
|
121
|
+
END
|
122
|
+
end
|
123
|
+
|
124
|
+
def draw_stuff (node)
|
125
|
+
return node.text
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class JS2::Decorator::Test < JS2::Decorator::Standard
|
4
|
+
|
5
|
+
def initialize ()
|
6
|
+
super()
|
7
|
+
@references = Hash.new
|
8
|
+
@in_class = false
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def draw_class (node)
|
13
|
+
str = super(node)
|
14
|
+
@scope = node.name
|
15
|
+
@in_class = true
|
16
|
+
str = process_str(str)
|
17
|
+
@in_class = false
|
18
|
+
return str
|
19
|
+
end
|
20
|
+
|
21
|
+
def draw_stuff (node)
|
22
|
+
str = super(node)
|
23
|
+
@scope = nil
|
24
|
+
return process_str(str)
|
25
|
+
end
|
26
|
+
|
27
|
+
def write_references (dir)
|
28
|
+
@references.each_pair do |klass, val|
|
29
|
+
File.open(dir + '/' + klass + '.yml', 'w') do |out|
|
30
|
+
YAML.dump(val, out)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def process_str (str)
|
38
|
+
lines = str.split(/\r?\n/)
|
39
|
+
processed = []
|
40
|
+
|
41
|
+
lines.each do |line|
|
42
|
+
# release comment and put it back in the queue
|
43
|
+
line.sub!(%r|\s*//@=\s*.*|) do |str|
|
44
|
+
if m = str.match(%r|(\s*)//@=\s*(.*)|)
|
45
|
+
m[1] + m[2]
|
46
|
+
else
|
47
|
+
str
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
while line = lines.shift
|
53
|
+
|
54
|
+
# scope is defined
|
55
|
+
if m = line.match(%r|^\s*//@\s*Scope\(([\w\.]+)\)|)
|
56
|
+
@scope = "Page.#{m[1]}"
|
57
|
+
processed.push(line)
|
58
|
+
|
59
|
+
elsif @scope
|
60
|
+
# if its a marker, reverse the next 2 lines
|
61
|
+
if new_line = process_marker(line, lines.first)
|
62
|
+
processed.push(lines.shift)
|
63
|
+
processed.push(new_line)
|
64
|
+
|
65
|
+
# var button = $('.button'); //@ Button
|
66
|
+
elsif new_lines = process_tailing_marker(line)
|
67
|
+
|
68
|
+
# reorder this and send it back through the loop
|
69
|
+
# but with the marker on top
|
70
|
+
lines.unshift(new_lines[1])
|
71
|
+
lines.unshift(new_lines[0])
|
72
|
+
next
|
73
|
+
|
74
|
+
# proceed as normal
|
75
|
+
else
|
76
|
+
processed.push(line)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
processed.push(line)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return processed.join("\n")
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_marker (line, next_line)
|
87
|
+
return false unless @scope && next_line
|
88
|
+
|
89
|
+
# //@ Foo // Bar
|
90
|
+
# //@+ Foo // Bar
|
91
|
+
# pad type marker comment
|
92
|
+
if m = line.match(%r|^(\s*)//@(\+)?\s+([^/]+)\s*(//(.*))?$|)
|
93
|
+
|
94
|
+
padding = m[1]
|
95
|
+
type = m[2]
|
96
|
+
marker = m[3].strip
|
97
|
+
|
98
|
+
whole_comment = m[4]
|
99
|
+
comment = (m[5] || '').strip
|
100
|
+
|
101
|
+
# get lval
|
102
|
+
lval_match = next_line.match(/^\s*(var)?\s*([^\s=;]+)\s*/)
|
103
|
+
|
104
|
+
return false unless lval_match
|
105
|
+
lval = lval_match[2]
|
106
|
+
|
107
|
+
# figure out what the lval should be
|
108
|
+
# Button(<jquery selector>)
|
109
|
+
key, find = parse_marker(marker)
|
110
|
+
finder = find ? find.to_json : "null"
|
111
|
+
|
112
|
+
# method to use for JS2.TEST.<addEle>(...)
|
113
|
+
insert = type == "+" ? 'appendVal' : 'addVal'
|
114
|
+
|
115
|
+
(@references[@scope] ||= {})[key] = comment
|
116
|
+
scope =
|
117
|
+
if @in_class
|
118
|
+
"this._klass"
|
119
|
+
else
|
120
|
+
@scope.to_json
|
121
|
+
end
|
122
|
+
|
123
|
+
return padding + %{TMP_SEL_MARKER = this.SEL_MARKER || JS2.SEL_MARKER; TMP_SEL_MARKER.#{insert}(#{scope}, #{key.to_json}, #{lval}, #{finder});}
|
124
|
+
else
|
125
|
+
return false
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# split a trailing marker into 2 lines
|
130
|
+
def process_tailing_marker (line)
|
131
|
+
if m = line.match(%r|^(\s*)([^\s]+.*)(//@.*)$|)
|
132
|
+
new_line = m[1] + m[2]
|
133
|
+
marker = m[1] + m[3]
|
134
|
+
return [ marker, new_line ]
|
135
|
+
else
|
136
|
+
return false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_marker (marker)
|
141
|
+
# key find
|
142
|
+
if m = marker.match(/^([^\(]+)\(([^\)]*)\)?/)
|
143
|
+
return [ m[1], m[2] ]
|
144
|
+
else
|
145
|
+
return [ marker, nil ]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|