fdlint 0.1.0
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/Gemfile +8 -0
- data/Gemfile.lock +14 -0
- data/README.md +68 -0
- data/Rakefile +92 -0
- data/bin/fdlint +17 -0
- data/lib/base_parser.rb +143 -0
- data/lib/cmd_runner.rb +145 -0
- data/lib/context.rb +31 -0
- data/lib/css/parser.rb +186 -0
- data/lib/css/reader.rb +30 -0
- data/lib/css/rule/check_compression_rule.rb +48 -0
- data/lib/css/rule/checklist.rb +45 -0
- data/lib/css/struct.rb +111 -0
- data/lib/encoding_error.rb +6 -0
- data/lib/file_validator.rb +38 -0
- data/lib/helper/code_type.rb +50 -0
- data/lib/helper/color_string.rb +44 -0
- data/lib/helper/file_reader.rb +22 -0
- data/lib/helper/strenc.rb +65 -0
- data/lib/html/parser.rb +212 -0
- data/lib/html/query.rb +96 -0
- data/lib/html/rule/check_tag_rule.rb +80 -0
- data/lib/html/struct.rb +291 -0
- data/lib/js/expr/expr.rb +66 -0
- data/lib/js/expr/left_hand.rb +63 -0
- data/lib/js/expr/operate.rb +92 -0
- data/lib/js/expr/primary.rb +166 -0
- data/lib/js/parser.rb +116 -0
- data/lib/js/rule/all.rb +35 -0
- data/lib/js/rule/checklist.rb +41 -0
- data/lib/js/rule/file_checker.rb +42 -0
- data/lib/js/rule/helper.rb +96 -0
- data/lib/js/rule/no_global.rb +87 -0
- data/lib/js/stat/if.rb +25 -0
- data/lib/js/stat/iter.rb +85 -0
- data/lib/js/stat/stat.rb +117 -0
- data/lib/js/stat/switch.rb +65 -0
- data/lib/js/stat/try.rb +28 -0
- data/lib/js/stat/var.rb +40 -0
- data/lib/js/struct.rb +248 -0
- data/lib/log_entry.rb +49 -0
- data/lib/node.rb +28 -0
- data/lib/parse_error.rb +13 -0
- data/lib/parser_visitable.rb +138 -0
- data/lib/position_info.rb +46 -0
- data/lib/printer/base_printer.rb +24 -0
- data/lib/printer/console_printer.rb +66 -0
- data/lib/printer/nocolor_printer.rb +27 -0
- data/lib/printer/vim_printer.rb +19 -0
- data/lib/rule.rb +241 -0
- data/lib/rule_helper.rb +14 -0
- data/lib/runner.rb +225 -0
- data/rules.d/css.rule +127 -0
- data/rules.d/html.dtd.rule +22 -0
- data/rules.d/html.prop.rule +51 -0
- data/rules.d/html.tag.rule +136 -0
- data/rules.d/js.file.rule +13 -0
- data/rules.d/js.jquery.rule +56 -0
- data/rules.d/js.mergefile.rule +71 -0
- data/rules.d/js.rule +84 -0
- data/test/all_tests.rb +84 -0
- data/test/cli/cli_test.rb +70 -0
- data/test/cli/log_level_test.rb +51 -0
- data/test/cli/output_format_test.rb +47 -0
- data/test/cli/type_test.rb +77 -0
- data/test/css/mac_line_end_support_test.rb +38 -0
- data/test/css/parser_test.rb +276 -0
- data/test/css/rule/check_encoding_test.rb +66 -0
- data/test/css/rule/check_list_rule_test.rb +167 -0
- data/test/css/rule/compression_test.rb +53 -0
- data/test/css/rule/file_name_test.rb +76 -0
- data/test/fixtures/css/broken.css +4 -0
- data/test/fixtures/css/cbu/36.css +52 -0
- data/test/fixtures/css/cbu/china_top.css +324 -0
- data/test/fixtures/css/cbu/default-merge.css +3 -0
- data/test/fixtures/css/cbu/default.css +13 -0
- data/test/fixtures/css/cbu/diy-merge.css +25 -0
- data/test/fixtures/css/cbu/fns-v1.css +27 -0
- data/test/fixtures/css/cbu/index_v0.1.css +12 -0
- data/test/fixtures/css/cbu/merge.css +11 -0
- data/test/fixtures/css/cbu/min.css +2 -0
- data/test/fixtures/css/cbu/my_home_admin.css +126 -0
- data/test/fixtures/css/cbu/nav.css +95 -0
- data/test/fixtures/css/cbu/pic_list.css +386 -0
- data/test/fixtures/css/cbu/quote-edit.css +18 -0
- data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
- data/test/fixtures/css/cbu/v1.css +9 -0
- data/test/fixtures/css/css3.css +30 -0
- data/test/fixtures/css/empty-min.css +0 -0
- data/test/fixtures/css/empty.css +0 -0
- data/test/fixtures/css/font-family.css +4 -0
- data/test/fixtures/css/gb-good.css +14 -0
- data/test/fixtures/css/gb_using_star.css +4 -0
- data/test/fixtures/css/import.css +18 -0
- data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-err.css +1 -0
- data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-good.css +1 -0
- data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
- data/test/fixtures/css/simple.css +1 -0
- data/test/fixtures/css/using_expr.css +8 -0
- data/test/fixtures/css/using_hack.css +21 -0
- data/test/fixtures/css/using_id.css +1 -0
- data/test/fixtures/css/using_star.css +4 -0
- data/test/fixtures/css/utf8_good.css +6 -0
- data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
- data/test/fixtures/css/utf8_using_star.css +5 -0
- data/test/fixtures/html/1-1.html +120 -0
- data/test/fixtures/html/1-2.html +120 -0
- data/test/fixtures/html/cms.html +373 -0
- data/test/fixtures/html/css_out_of_head.html +9 -0
- data/test/fixtures/html/fdev-template.html +22 -0
- data/test/fixtures/html/google.com.html +33 -0
- data/test/fixtures/html/mixed_log_levels.html +4 -0
- data/test/fixtures/html/mixed_types.html +13 -0
- data/test/fixtures/html/no_dtd.html +6 -0
- data/test/fixtures/html/readme.html +94 -0
- data/test/fixtures/html/review.board.html +163 -0
- data/test/fixtures/html/syntax_err.html +3 -0
- data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
- data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
- data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
- data/test/fixtures/html/train/index.html +37 -0
- data/test/fixtures/html/train/test.html +1 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/344/270/200/347/272/247/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
- data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/344/270/200/347/272/247/345/210/206/347/261/273.html +113 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
- data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
- data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
- data/test/fixtures/html/train//350/256/276/347/275/256/351/246/226/351/241/265/346/216/250/350/215/220.html +168 -0
- data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
- data/test/fixtures/html/train//351/246/226/351/241/265/345/271/277/345/221/212/350/256/276/347/275/256.html +297 -0
- data/test/fixtures/html/unescaped.html +2 -0
- data/test/fixtures/html/view.vm +916 -0
- data/test/fixtures/js/jquery-1.7.js +9300 -0
- data/test/fixtures/js/scope-test.js +22 -0
- data/test/helper.rb +41 -0
- data/test/html/mixed_type_test.rb +35 -0
- data/test/html/parser/parse_comment_test.rb +47 -0
- data/test/html/parser/parse_dtd_test.rb +46 -0
- data/test/html/parser/parse_script_tag_test.rb +55 -0
- data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
- data/test/html/parser/parse_with_diff_case_test.rb +38 -0
- data/test/html/parser/parse_with_emtpy_test.rb +22 -0
- data/test/html/parser/parse_with_multi_children_test.rb +27 -0
- data/test/html/parser/parse_with_multi_line_test.rb +41 -0
- data/test/html/parser/parse_with_prop_test.rb +88 -0
- data/test/html/parser/parse_with_script_tag_test.rb +26 -0
- data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
- data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
- data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
- data/test/html/parser/parse_with_style_tag_test.rb +22 -0
- data/test/html/parser/parse_with_text_test.rb +45 -0
- data/test/html/parser_test.rb +52 -0
- data/test/html/query_test.rb +52 -0
- data/test/html/rule/check_block_level_element_test.rb +52 -0
- data/test/html/rule/check_button_test.rb +45 -0
- data/test/html/rule/check_class_count_test.rb +36 -0
- data/test/html/rule/check_css_in_head_test.rb +53 -0
- data/test/html/rule/check_dtd_test.rb +46 -0
- data/test/html/rule/check_form_element_name_test.rb +49 -0
- data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
- data/test/html/rule/check_html_template_test.rb +103 -0
- data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
- data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
- data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
- data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
- data/test/html/rule/check_no_import_css_test.rb +36 -0
- data/test/html/rule/check_prop_have_value_test.rb +32 -0
- data/test/html/rule/check_prop_seperator_test.rb +32 -0
- data/test/html/rule/check_style_prop_test.rb +30 -0
- data/test/html/rule/check_tag_closed_test.rb +59 -0
- data/test/html/rule/check_tag_downcase_test.rb +51 -0
- data/test/html/rule/check_unescape_char_test.rb +35 -0
- data/test/html/rule/check_unique_import_test.rb +56 -0
- data/test/html/rule_test.rb +62 -0
- data/test/js/expr/expr.rb +57 -0
- data/test/js/expr/left_hand.rb +25 -0
- data/test/js/expr/operate.rb +145 -0
- data/test/js/expr/primary.rb +89 -0
- data/test/js/parser_test.rb +98 -0
- data/test/js/rule/alert_check_test.rb +37 -0
- data/test/js/rule/all_test.rb +23 -0
- data/test/js/rule/base_test.rb +34 -0
- data/test/js/rule/file_checker_test.rb +131 -0
- data/test/js/rule/jq_check_test.rb +90 -0
- data/test/js/rule/nest_try_catch_test.rb +71 -0
- data/test/js/rule/new_object_and_new_array_test.rb +38 -0
- data/test/js/rule/no_eval_test.rb +34 -0
- data/test/js/rule/no_global_test.rb +88 -0
- data/test/js/rule/private_method_check_test.rb +58 -0
- data/test/js/rule/semicolon_test.rb +63 -0
- data/test/js/rule/stat_if_with_brace_test.rb +68 -0
- data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
- data/test/js/rule/use_strict_equal_test.rb +44 -0
- data/test/js/rule_test.rb +47 -0
- data/test/js/stat/if.rb +26 -0
- data/test/js/stat/iter.rb +115 -0
- data/test/js/stat/stat.rb +91 -0
- data/test/js/stat/switch.rb +37 -0
- data/test/js/stat/try.rb +32 -0
- data/test/js/stat/var.rb +38 -0
- data/test/parser_visitable_test.rb +102 -0
- data/test/position_info_test.rb +66 -0
- data/test/rule_dsl/dsl_basic_test.rb +91 -0
- data/test/rule_dsl/importing_test.rb +48 -0
- data/test/runner/log_level_test.rb +58 -0
- metadata +317 -0
data/lib/html/query.rb
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'strscan'
|
|
2
|
+
|
|
3
|
+
module XRay
|
|
4
|
+
module HTML
|
|
5
|
+
|
|
6
|
+
class Element
|
|
7
|
+
|
|
8
|
+
WORD = /[A-Za-z][-_\w]*/
|
|
9
|
+
CLASS = %r(\.#{WORD})
|
|
10
|
+
ID = %r(##{WORD})
|
|
11
|
+
PROP_PAIR = %r(\s*,?\s*#{WORD}(==[^\s,])?)
|
|
12
|
+
PROP = %r(\[#{WORD}.*?\])
|
|
13
|
+
|
|
14
|
+
###
|
|
15
|
+
# This method implemented CSS selector for
|
|
16
|
+
# HTML (like Sizzle) very simply. It is not
|
|
17
|
+
# fully supported CSS selector.
|
|
18
|
+
#
|
|
19
|
+
# TODO: support full CSS3 selector
|
|
20
|
+
###
|
|
21
|
+
def match?(str)
|
|
22
|
+
return false if is_a?(TextElement)
|
|
23
|
+
obj = query_obj(str)
|
|
24
|
+
tag = obj[:tag]
|
|
25
|
+
cls = obj[:classes]
|
|
26
|
+
props = obj[:properties]
|
|
27
|
+
unless tag.nil? or tag_name_equal? tag
|
|
28
|
+
return false
|
|
29
|
+
end
|
|
30
|
+
classes = prop_value(:class)
|
|
31
|
+
cls.each { |c| return false unless classes.include? c }
|
|
32
|
+
props.each do |n, v|
|
|
33
|
+
if v.nil?
|
|
34
|
+
return false unless has_prop? n
|
|
35
|
+
else
|
|
36
|
+
return false unless prop_value(n) == v
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias_method :===, :match?
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
def query_obj(str)
|
|
46
|
+
classes = []
|
|
47
|
+
props = {}
|
|
48
|
+
sc = StringScanner.new(str)
|
|
49
|
+
tag = sc.scan WORD
|
|
50
|
+
if sc.check ID
|
|
51
|
+
sc.skip /#/
|
|
52
|
+
id = sc.scan WORD
|
|
53
|
+
end
|
|
54
|
+
while sc.check CLASS
|
|
55
|
+
sc.skip /\./
|
|
56
|
+
classes << (sc.scan WORD)
|
|
57
|
+
end
|
|
58
|
+
if sc.check PROP
|
|
59
|
+
sc.skip /\[/
|
|
60
|
+
while sc.check PROP_PAIR
|
|
61
|
+
sc.skip /\s*/
|
|
62
|
+
sc.skip /,/
|
|
63
|
+
sc.skip /\s*/
|
|
64
|
+
prop = sc.scan WORD
|
|
65
|
+
sc.skip /\==/
|
|
66
|
+
sc.skip /['"]/
|
|
67
|
+
value = sc.scan /[^\]]+/
|
|
68
|
+
props[prop] = value
|
|
69
|
+
end
|
|
70
|
+
sc.skip /\]/
|
|
71
|
+
end
|
|
72
|
+
props["id"] = id if id
|
|
73
|
+
{:tag => tag, :classes => classes, :properties => props }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
public
|
|
77
|
+
def query( selector, &block )
|
|
78
|
+
ret = []
|
|
79
|
+
if match?(selector)
|
|
80
|
+
ret << self
|
|
81
|
+
yield self if block_given?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
children && children.each do |node|
|
|
85
|
+
ret += node.query(selector, &block)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
ret
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
alias_method :*, :query
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require_relative '../../rule'
|
|
3
|
+
require_relative '../struct'
|
|
4
|
+
require_relative '../query'
|
|
5
|
+
require_relative '../../context'
|
|
6
|
+
|
|
7
|
+
module XRay
|
|
8
|
+
module HTML
|
|
9
|
+
module Rule
|
|
10
|
+
|
|
11
|
+
class CheckTagRule
|
|
12
|
+
|
|
13
|
+
include ::XRay::Rule, Context
|
|
14
|
+
|
|
15
|
+
attr_reader :imported_scripts, :imported_css
|
|
16
|
+
|
|
17
|
+
def initialize(opt=nil)
|
|
18
|
+
@imported_scripts, @imported_css = [], []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def script_used?( src )
|
|
22
|
+
@imported_scripts.include? src
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def style_used?( src )
|
|
26
|
+
@imported_css.include? src
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def visit_doc(html)
|
|
30
|
+
check_html_doc html
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def visit_dtd(dtd)
|
|
34
|
+
@have_dtd = true
|
|
35
|
+
check_html_dtd dtd
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def visit_tag(tag)
|
|
39
|
+
results = check_html_tag tag
|
|
40
|
+
record_script(tag) or record_style(tag)
|
|
41
|
+
results
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def visit_text(tag)
|
|
45
|
+
check_html_text tag
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def visit_property(prop)
|
|
49
|
+
check_html_property prop
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def record_script( tag )
|
|
53
|
+
if tag.tag_name_equal? 'script'
|
|
54
|
+
src = tag.prop_value(:src).to_s
|
|
55
|
+
unless src.empty? or script_used? src
|
|
56
|
+
@imported_scripts << src
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def record_style( tag )
|
|
62
|
+
if tag.tag_name_equal? 'link' and tag.prop_value(:rel) =~ /stylesheet/i
|
|
63
|
+
src = tag.prop_value(:href).to_s
|
|
64
|
+
unless src.empty? or style_used? src
|
|
65
|
+
@imported_css << src
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def has_dtd?
|
|
71
|
+
@have_dtd
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
alias_method :have_dtd?, :has_dtd?
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/html/struct.rb
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
require_relative '../node'
|
|
2
|
+
|
|
3
|
+
module XRay
|
|
4
|
+
module HTML
|
|
5
|
+
|
|
6
|
+
Node = XRay::Node
|
|
7
|
+
AUTO_CLOSE_TAGS = %w(area base basefont br col frame hr img input link meta param)
|
|
8
|
+
INLINE_ELEMENTS = %w(a br label abbr legend address link
|
|
9
|
+
area mark audio meter bm nav cite optgroup
|
|
10
|
+
code option del q details small dfn select
|
|
11
|
+
command source datalist span em strong
|
|
12
|
+
font sub i summary iframe sup img tbody
|
|
13
|
+
input td ins time kbd var)
|
|
14
|
+
|
|
15
|
+
class Element < Node
|
|
16
|
+
|
|
17
|
+
attr_reader :tag, :props, :children
|
|
18
|
+
attr_accessor :parent, :close_type, :ending, :scopes
|
|
19
|
+
|
|
20
|
+
def initialize(tag, props=[], children=[], close_type=:after, ending=nil)
|
|
21
|
+
@tag, @props, @children, @close_type, @ending = tag, to_props(props), Array.[](children).flatten || [], close_type, ending
|
|
22
|
+
@position = @tag.position.dup if tag.is_a?(Node) and tag.position
|
|
23
|
+
@children.each { |el| el.parent = self }
|
|
24
|
+
@scopes = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def has_scope?
|
|
28
|
+
!(parent.nil? and @scopes.empty?)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def in_scope?(scp)
|
|
32
|
+
if parent
|
|
33
|
+
parent.tag_name_equal? scp
|
|
34
|
+
else
|
|
35
|
+
scopes.include?(scp)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def text
|
|
40
|
+
children.inject("") do |s,child|
|
|
41
|
+
s + child.text
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def tag_name
|
|
46
|
+
@tag.is_a?(Node) ? @tag.text : @tag.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def tag_name_equal?(name)
|
|
50
|
+
tag_name.downcase == name.downcase
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def inner_html
|
|
54
|
+
@children.inject('') { |s,c| s + c.outer_html }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def outer_html
|
|
58
|
+
if children.empty?
|
|
59
|
+
"<#{@tag}#{prop_text} />"
|
|
60
|
+
else
|
|
61
|
+
cls = ending || "</#{@tag}>"
|
|
62
|
+
"<#{@tag}#{prop_text}>#{inner_html}#{cls}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def inner_text
|
|
67
|
+
@children.inject('') { |s,c| s + c.inner_text }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
alias_method :text, :inner_text
|
|
71
|
+
|
|
72
|
+
def prop_text
|
|
73
|
+
props.inject('') { |s, p| s << " " << p.to_s }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def ==(other)
|
|
77
|
+
other.is_a?(Element) and tag_name == tag_name.to_s && prop_text == other.prop_text && inner_html == other.inner_html
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def has_prop?(name)
|
|
81
|
+
@props.any? { |p| p.name_equal? name }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def prop(name)
|
|
85
|
+
@props.find { |p| p.name_equal? name }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def prop_value(*arg)
|
|
89
|
+
name, value = *arg
|
|
90
|
+
if arg.size > 1
|
|
91
|
+
unless @props.find { |p| p.value = value if p.name_equal? name }
|
|
92
|
+
@props << Property.new(name, value)
|
|
93
|
+
end
|
|
94
|
+
nil
|
|
95
|
+
else
|
|
96
|
+
p = prop(name)
|
|
97
|
+
p.value if p
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def to_s
|
|
102
|
+
"[HTML: #{outer_html}]"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def inline?
|
|
106
|
+
INLINE_ELEMENTS.include? tag_name.downcase
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def auto_close?
|
|
110
|
+
AUTO_CLOSE_TAGS.include? tag_name.downcase
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def closed?
|
|
114
|
+
@close_type != :none
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self_closed?
|
|
118
|
+
@close_type == :self
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def each(&block)
|
|
122
|
+
children.each {|node| yield node }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def empty?
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
protected
|
|
130
|
+
def parse(text)
|
|
131
|
+
@outer_html = text
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
def to_props(src)
|
|
137
|
+
case src
|
|
138
|
+
when Array
|
|
139
|
+
src
|
|
140
|
+
when Hash
|
|
141
|
+
src.map do |n, v|
|
|
142
|
+
Property.new(n, v)
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
[]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class Document < Element
|
|
153
|
+
def initialize( children=[] )
|
|
154
|
+
super(nil, {}, children || [])
|
|
155
|
+
@position = Position.new(0,0,0)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def ==(other)
|
|
159
|
+
if other.is_a?(Array)
|
|
160
|
+
return children == other
|
|
161
|
+
elsif other.is_a?(Document)
|
|
162
|
+
return children == other.children
|
|
163
|
+
else
|
|
164
|
+
super
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def empty?
|
|
169
|
+
children.empty?
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
class TextElement < Element
|
|
175
|
+
|
|
176
|
+
def initialize(text="")
|
|
177
|
+
super(nil)
|
|
178
|
+
@text = text.to_s
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def text; @text; end
|
|
182
|
+
def tag_name; nil; end
|
|
183
|
+
|
|
184
|
+
alias_method :inner_text, :text
|
|
185
|
+
alias_method :inner_html, :text
|
|
186
|
+
alias_method :outer_html, :text
|
|
187
|
+
|
|
188
|
+
def ==(other)
|
|
189
|
+
text == other.text
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def tag_name_equal?(name); false; end
|
|
193
|
+
def inline?; true; end
|
|
194
|
+
def auto_close? ; false; end
|
|
195
|
+
def closed? ; true; end
|
|
196
|
+
|
|
197
|
+
def to_s
|
|
198
|
+
"[TEXT: #{text}]"
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class CommentElement < Element
|
|
205
|
+
def initialize(text)
|
|
206
|
+
super(nil)
|
|
207
|
+
@text = text.to_s
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def content; @text; end
|
|
211
|
+
def tag_name; nil; end
|
|
212
|
+
|
|
213
|
+
alias_method :inner_text, :text
|
|
214
|
+
alias_method :inner_html, :text
|
|
215
|
+
|
|
216
|
+
def outer_html
|
|
217
|
+
"<!--#{text}-->"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def ==(other)
|
|
221
|
+
text == other.text
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def tag_name_equal?(name); false; end
|
|
225
|
+
def inline? ; true; end
|
|
226
|
+
def auto_close? ; false; end
|
|
227
|
+
def closed? ; true; end
|
|
228
|
+
|
|
229
|
+
def to_s
|
|
230
|
+
"[Comment: #{text}]"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
class DTDElement < Element
|
|
236
|
+
|
|
237
|
+
attr_accessor :type
|
|
238
|
+
|
|
239
|
+
def initialize(type, pre="DOCTYPE", pos=nil)
|
|
240
|
+
@type, @pre = type, pre
|
|
241
|
+
@position = pos || Position.new(0,0,0)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def to_s
|
|
245
|
+
"<!#{@pre} #{type}>"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def inline?
|
|
249
|
+
true
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
alias_method :outer_html, :to_s
|
|
253
|
+
|
|
254
|
+
def ==(other)
|
|
255
|
+
other.is_a?(DTDElement) and other.type == type
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def =~(other)
|
|
259
|
+
to_s =~ other
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
class Property < Node
|
|
265
|
+
|
|
266
|
+
attr_reader :name, :sep
|
|
267
|
+
attr_accessor :value
|
|
268
|
+
|
|
269
|
+
def initialize(name, value, sep='"')
|
|
270
|
+
@name, @value, @sep = name, value, sep
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def position; name.position; end
|
|
274
|
+
|
|
275
|
+
def name_equal?(text)
|
|
276
|
+
@name.to_s.downcase == text.to_s.downcase
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def to_s
|
|
280
|
+
if value.nil?
|
|
281
|
+
name.to_s
|
|
282
|
+
else
|
|
283
|
+
"#{name}=#{sep}#{value}#{sep}"
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
end
|
|
291
|
+
end
|
data/lib/js/expr/expr.rb
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require_relative 'primary'
|
|
2
|
+
require_relative 'left_hand'
|
|
3
|
+
require_relative 'operate'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module XRay
|
|
7
|
+
module JS
|
|
8
|
+
module Expr
|
|
9
|
+
|
|
10
|
+
module Expr
|
|
11
|
+
include Primary, LeftHand, Operate
|
|
12
|
+
|
|
13
|
+
def parse_expression
|
|
14
|
+
log 'parse expression'
|
|
15
|
+
parse_expr_with_operate :parse_expr_assignment, /,/
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def parse_expr_assignment
|
|
19
|
+
log 'parse expr assignment'
|
|
20
|
+
|
|
21
|
+
expr = parse_expr_condition
|
|
22
|
+
|
|
23
|
+
r = /=|\*=|\/=|%=|\+=|-=|<<=|>>=|>>>=|&=|\^=|\|=/
|
|
24
|
+
if expr.left_hand? && check(r)
|
|
25
|
+
op = scan r
|
|
26
|
+
expr = create_element Expression, op.text, expr, parse_expr_assignment
|
|
27
|
+
end
|
|
28
|
+
expr
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def parse_expr_condition
|
|
32
|
+
log 'parse expr condition'
|
|
33
|
+
expr = parse_expr_logical_or
|
|
34
|
+
if check /\?/
|
|
35
|
+
skip /\?/
|
|
36
|
+
left = parse_expr_assignment
|
|
37
|
+
skip /:/
|
|
38
|
+
right = parse_expr_assignment
|
|
39
|
+
|
|
40
|
+
expr = create_element ConditionExpression, expr, left, right
|
|
41
|
+
end
|
|
42
|
+
expr
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
protected
|
|
46
|
+
|
|
47
|
+
def parse_expr_with_operate(left, pattern = nil, &block)
|
|
48
|
+
block = block || lambda {
|
|
49
|
+
if check pattern
|
|
50
|
+
op = scan pattern
|
|
51
|
+
[op.text, self.send(left)]
|
|
52
|
+
end
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
expr = self.send left
|
|
56
|
+
while (ret = block.call)
|
|
57
|
+
expr = create_element Expression, ret[0], expr, ret[1]
|
|
58
|
+
end
|
|
59
|
+
expr
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module XRay
|
|
2
|
+
module JS
|
|
3
|
+
module Expr
|
|
4
|
+
|
|
5
|
+
module LeftHand
|
|
6
|
+
|
|
7
|
+
def parse_expr_lefthand
|
|
8
|
+
log 'parse expr lefthand'
|
|
9
|
+
expr = check(/new\b/) ? parse_expr_new : parse_expr_member
|
|
10
|
+
expr.left_hand = true
|
|
11
|
+
expr
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse_expr_new
|
|
15
|
+
log 'parse expr new'
|
|
16
|
+
|
|
17
|
+
pos = skip /new/
|
|
18
|
+
expr = check(/new\b/) ? parse_expr_new : parse_expr_member
|
|
19
|
+
args = check(/\(/) ? parse_arguments_list : nil
|
|
20
|
+
|
|
21
|
+
create_element Expression, 'new', expr, args, pos
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def parse_expr_member
|
|
25
|
+
log 'parse expr member'
|
|
26
|
+
parse_expr_with_operate(:parse_expr_member_left) do
|
|
27
|
+
if check /[.]/
|
|
28
|
+
skip /[.]/
|
|
29
|
+
['.', parse_expr_identifier]
|
|
30
|
+
elsif check /[\[]/
|
|
31
|
+
skip /\[/
|
|
32
|
+
expr = parse_expression
|
|
33
|
+
skip /\]/
|
|
34
|
+
['[', expr]
|
|
35
|
+
elsif check /\(/
|
|
36
|
+
['(', parse_arguments_list]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
def parse_expr_member_left
|
|
44
|
+
if check /function\b/
|
|
45
|
+
create_element FunctionExpression, parse_function_declaration(true)
|
|
46
|
+
else
|
|
47
|
+
parse_expr_primary
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_arguments_list
|
|
53
|
+
log 'parse arguments list'
|
|
54
|
+
skip /\(/
|
|
55
|
+
params = batch :parse_expr_assignment, /\)/, /,/
|
|
56
|
+
skip /\)/
|
|
57
|
+
Elements.new params
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module XRay
|
|
2
|
+
module JS
|
|
3
|
+
module Expr
|
|
4
|
+
|
|
5
|
+
module Operate
|
|
6
|
+
|
|
7
|
+
def parse_expr_postfix
|
|
8
|
+
log 'parse expr postfix'
|
|
9
|
+
expr = parse_expr_lefthand
|
|
10
|
+
if check(/[ \t]*(?:\+\+|--)/, true)
|
|
11
|
+
op = scan /\+\+|--/
|
|
12
|
+
expr = create_element Expression, op.text, expr
|
|
13
|
+
end
|
|
14
|
+
expr
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def parse_expr_unary
|
|
18
|
+
log 'parse expr unary'
|
|
19
|
+
r = /delete|void|typeof|\+\+|--|\+|-|~|!/
|
|
20
|
+
if check r
|
|
21
|
+
op = scan r
|
|
22
|
+
create_element Expression, op.text, nil, parse_expr_unary
|
|
23
|
+
else
|
|
24
|
+
parse_expr_postfix
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parse_expr_mul
|
|
29
|
+
log 'parse expr mul'
|
|
30
|
+
parse_expr_with_operate :parse_expr_unary, /(?:\*|\/|%)(?!=)/
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_expr_add
|
|
34
|
+
log 'parse expr add'
|
|
35
|
+
parse_expr_with_operate :parse_expr_mul, /(?:\+|-)(?!=)/
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse_expr_shift
|
|
39
|
+
log 'parse expr shift'
|
|
40
|
+
parse_expr_with_operate :parse_expr_add, /(?:<<|>>>|>>)(?!=)/
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse_expr_relation
|
|
44
|
+
not_in = expr_operate_not_in?
|
|
45
|
+
log "parse expr relational#{not_in ? '(notin)' : ''}"
|
|
46
|
+
pattern = not_in ? (/>=|<=|>|<|\binstanceof\b/) : (/>=|<=|>|<|\binstanceof\b|\bin\b/)
|
|
47
|
+
parse_expr_with_operate :parse_expr_shift, pattern
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parse_expr_equal
|
|
51
|
+
log 'parse expr equal'
|
|
52
|
+
parse_expr_with_operate :parse_expr_relation, /===|!==|==|!=/
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def parse_expr_bit_and
|
|
56
|
+
log 'parse expr bit and'
|
|
57
|
+
parse_expr_with_operate :parse_expr_equal, /&(?![&=])/
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_expr_bit_xor
|
|
61
|
+
log 'parse expr bit xor'
|
|
62
|
+
parse_expr_with_operate :parse_expr_bit_and, /\^(?!=)/
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def parse_expr_bit_or
|
|
66
|
+
log 'parse expr bit or'
|
|
67
|
+
parse_expr_with_operate :parse_expr_bit_xor, /\|(?![|=])/
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def parse_expr_logical_and
|
|
71
|
+
log 'parse expr logical and'
|
|
72
|
+
parse_expr_with_operate :parse_expr_bit_or, /&&/
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def parse_expr_logical_or
|
|
76
|
+
log 'parse expr logical or'
|
|
77
|
+
parse_expr_with_operate :parse_expr_logical_and, /\|\|/
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def expr_operate_not_in?
|
|
81
|
+
@operate_not_in || false
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def expr_operate_not_in=(value)
|
|
85
|
+
@operate_not_in = !!value
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|