js2 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|