css_parser_master 1.2.4

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.
@@ -0,0 +1,33 @@
1
+ require 'css_parser_master/declaration_api'
2
+
3
+ module CssParserMaster
4
+ class Selector
5
+ include CssParserMaster::DeclarationAPI
6
+
7
+ attr_accessor :selector, :declarations, :specificity
8
+
9
+ def initialize(selector, declarations, specificity)
10
+ @selector = selector
11
+ @order = 0
12
+ @declarations = {}
13
+ parse_declarations!(declarations)
14
+ # puts "init @declarations: #{@declarations}"
15
+ @specificity = specificity
16
+ end
17
+
18
+ def declarations_to_s(options = {})
19
+ # puts "declarations_to_s: #{declarations.inspect}"
20
+ s = declarations.map do |decl|
21
+ decl[1].to_text
22
+ end.join('')
23
+ # puts "res: #{s}"
24
+ s
25
+ end
26
+
27
+
28
+ def to_text
29
+ "#{selector}\n{\n#{declarations_to_s}\n} \n"
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ module CssParserMaster
2
+ class Selectors
3
+ include Enumerable
4
+
5
+ attr_reader :selectors
6
+
7
+ def << selector
8
+ selectors << selector
9
+ end
10
+
11
+ def all
12
+ map{|sel| sel.split(',') }
13
+ end
14
+
15
+ def each
16
+ selectors.each { |sel| yield sel }
17
+ end
18
+
19
+ def empty?
20
+ selectors.empty?
21
+ end
22
+
23
+ def initialize(selectors = [])
24
+ @selectors = selectors
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ @import "import-circular-reference.css";
2
+
3
+ body { color: black; background: white; }
4
+ p { margin: 0px; }
@@ -0,0 +1,3 @@
1
+ @import "simple.css" print, tv, screen;
2
+
3
+ div { color: lime; }
@@ -0,0 +1,3 @@
1
+ @import 'subdir/import2.css';
2
+
3
+ div { color: lime; }
@@ -0,0 +1,6 @@
1
+ body {
2
+ color: black;
3
+ background: white;
4
+ }
5
+
6
+ p { margin: 0px; }
@@ -0,0 +1,3 @@
1
+ @import "../simple.css";
2
+
3
+ a { text-decoration: none; }
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for reading and generating CSS shorthand properties
4
+ class CssParserBasicTests < Test::Unit::TestCase
5
+ include CssParser
6
+
7
+ def setup
8
+ @cp = CssParser::Parser.new
9
+ @css = <<-EOT
10
+ html, body, p { margin: 0px; }
11
+ p { padding: 0px; }
12
+ #content { font: 12px/normal sans-serif; }
13
+ EOT
14
+ end
15
+
16
+ def test_finding_by_selector
17
+ @cp.add_block!(@css)
18
+ assert_equal 'margin: 0px;', @cp.find_by_selector('body').join(' ')
19
+ assert_equal 'margin: 0px; padding: 0px;', @cp.find_by_selector('p').join(' ')
20
+ end
21
+
22
+ def test_adding_block
23
+ @cp.add_block!(@css)
24
+ # @cp.selector_declarations do |sel, decl|
25
+ # puts "sel: #{sel.inspect}"
26
+ # puts "decl: #{decl.inspect}"
27
+ # end
28
+
29
+ assert_equal 'margin: 0px;', @cp.find_by_selector('body').join
30
+ end
31
+
32
+ def test_adding_a_rule
33
+ @cp.add_rule!('div', 'color: blue;')
34
+
35
+ # @cp.selector_declarations do |sel, decl|
36
+ # puts "sel: #{sel.inspect}"
37
+ # puts "decl: #{decl.inspect}"
38
+ # end
39
+
40
+ assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
41
+ end
42
+
43
+ def test_adding_a_rule_set
44
+ rs = CssParser::RuleSet.new('div', 'color: blue;')
45
+ @cp.add_rule_set!(rs)
46
+ assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
47
+ end
48
+
49
+ def test_selector_declarations
50
+ expected = [
51
+ {:selector => "#content p", :declarations => "color: #fff;", :specificity => 101},
52
+ {:selector => "a", :declarations => "color: #fff;", :specificity => 1}
53
+ ]
54
+
55
+ actual = []
56
+ rs = RuleSet.new('#content p, a', 'color: #fff;')
57
+ @cp.add_rule_set!(rs)
58
+ @cp.selector_declarations do |sel, decl|
59
+ # puts "sel: #{sel.to_text}"
60
+ # puts "decl: #{decl[1].to_text}"
61
+ end
62
+ # assert_equal(expected, actual)
63
+ end
64
+
65
+
66
+ def test_toggling_uri_conversion
67
+ # with conversion
68
+ cp_with_conversion = Parser.new(:absolute_paths => true)
69
+ cp_with_conversion.add_block!("body { background: url('../style/yellow.png?abc=123') };",
70
+ :base_uri => 'http://example.org/style/basic.css')
71
+
72
+ assert_equal "background: url('http://example.org/style/yellow.png?abc=123');",
73
+ cp_with_conversion['body'].join(' ')
74
+
75
+ # without conversion
76
+ cp_without_conversion = Parser.new(:absolute_paths => false)
77
+ cp_without_conversion.add_block!("body { background: url('../style/yellow.png?abc=123') };",
78
+ :base_uri => 'http://example.org/style/basic.css')
79
+
80
+ assert_equal "background: url('../style/yellow.png?abc=123');",
81
+ cp_without_conversion['body'].join(' ')
82
+ end
83
+
84
+ end
@@ -0,0 +1,110 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the CssParser's loading functions.
4
+ class CssParserLoadingTests < Test::Unit::TestCase
5
+ include CssParser
6
+ include WEBrick
7
+
8
+ def setup
9
+ # from http://nullref.se/blog/2006/5/17/testing-with-webrick
10
+ @cp = Parser.new
11
+
12
+ @uri_base = 'http://localhost:12000'
13
+
14
+ www_root = File.dirname(__FILE__) + '/fixtures/'
15
+
16
+ @server_thread = Thread.new do
17
+ s = WEBrick::HTTPServer.new(:Port => 12000, :DocumentRoot => www_root, :Logger => Log.new(nil, BasicLog::FATAL), :AccessLog => [])
18
+ @port = s.config[:Port]
19
+ begin
20
+ s.start
21
+ ensure
22
+ s.shutdown
23
+ end
24
+ end
25
+
26
+ sleep 1 # ensure the server has time to load
27
+ end
28
+
29
+ def teardown
30
+ @server_thread.kill
31
+ @server_thread.join(5)
32
+ @server_thread = nil
33
+ end
34
+
35
+ def test_loading_a_local_file
36
+ file_name = File.dirname(__FILE__) + '/fixtures/simple.css'
37
+ @cp.load_file!(file_name)
38
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
39
+ end
40
+
41
+ def test_loading_a_remote_file
42
+ @cp.load_uri!("#{@uri_base}/simple.css")
43
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
44
+ end
45
+
46
+ def test_following_at_import_rules_local
47
+ base_dir = File.dirname(__FILE__) + '/fixtures'
48
+ @cp.load_file!('import1.css', base_dir)
49
+
50
+ # from '/import1.css'
51
+ assert_equal 'color: lime;', @cp.find_by_selector('div').join(' ')
52
+
53
+ # from '/subdir/import2.css'
54
+ assert_equal 'text-decoration: none;', @cp.find_by_selector('a').join(' ')
55
+
56
+ # from '/subdir/../simple.css'
57
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
58
+ end
59
+
60
+ def test_following_at_import_rules_remote
61
+ @cp.load_uri!("#{@uri_base}/import1.css")
62
+
63
+ # from '/import1.css'
64
+ assert_equal 'color: lime;', @cp.find_by_selector('div').join(' ')
65
+
66
+ # from '/subdir/import2.css'
67
+ assert_equal 'text-decoration: none;', @cp.find_by_selector('a').join(' ')
68
+
69
+ # from '/subdir/../simple.css'
70
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
71
+ end
72
+
73
+ def test_following_at_import_rules_from_add_block
74
+ css_block = '@import "../simple.css";'
75
+
76
+ @cp.add_block!(css_block, :base_uri => "#{@uri_base}/subdir/")
77
+
78
+ # from 'simple.css'
79
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
80
+ end
81
+
82
+ def test_importing_with_media_types
83
+ @cp.load_uri!("#{@uri_base}/import-with-media-types.css")
84
+
85
+ # from simple.css with :screen media type
86
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p', :screen).join(' ')
87
+ assert_equal '', @cp.find_by_selector('p', :tty).join(' ')
88
+ end
89
+
90
+ def test_throwing_circular_reference_exception
91
+ assert_raise CircularReferenceError do
92
+ @cp.load_uri!("#{@uri_base}/import-circular-reference.css")
93
+ end
94
+ end
95
+
96
+ def test_toggling_not_found_exceptions
97
+ cp_with_exceptions = Parser.new(:io_exceptions => true)
98
+
99
+ assert_raise RemoteFileError do
100
+ cp_with_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
101
+ end
102
+
103
+ cp_without_exceptions = Parser.new(:io_exceptions => false)
104
+
105
+ assert_nothing_raised RemoteFileError do
106
+ cp_without_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,71 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the handling of media types
4
+ class CssParserMediaTypesTests < Test::Unit::TestCase
5
+ include CssParser
6
+
7
+ def setup
8
+ @cp = Parser.new
9
+ end
10
+
11
+ def test_finding_by_media_type
12
+ # from http://www.w3.org/TR/CSS21/media.html#at-media-rule
13
+ css = <<-EOT
14
+ @media print {
15
+ body { font-size: 10pt }
16
+ }
17
+ @media screen {
18
+ body { font-size: 13px }
19
+ }
20
+ @media screen, print {
21
+ body { line-height: 1.2 }
22
+ }
23
+ EOT
24
+
25
+ @cp.add_block!(css)
26
+
27
+ assert_equal 'font-size: 10pt; line-height: 1.2;', @cp.find_by_selector('body', :print).join(' ')
28
+ assert_equal 'font-size: 13px; line-height: 1.2;', @cp.find_by_selector('body', :screen).join(' ')
29
+ end
30
+
31
+ def atest_finding_by_multiple_media_types
32
+ css = <<-EOT
33
+ @media print {
34
+ body { font-size: 10pt }
35
+ }
36
+ @media handheld {
37
+ body { font-size: 13px }
38
+ }
39
+ @media screen, print {
40
+ body { line-height: 1.2 }
41
+ }
42
+ EOT
43
+ @cp.add_block!(css)
44
+
45
+ assert_equal 'font-size: 13px; line-height: 1.2;', @cp.find_by_selector('body', [:screen,:handheld]).join(' ')
46
+ end
47
+
48
+ def test_adding_block_with_media_types
49
+ css = <<-EOT
50
+ body { font-size: 10pt }
51
+ EOT
52
+
53
+ @cp.add_block!(css, :media_types => [:screen])
54
+
55
+ assert_equal 'font-size: 10pt;', @cp.find_by_selector('body', :screen).join(' ')
56
+ assert @cp.find_by_selector('body', :handheld).empty?
57
+ end
58
+
59
+ def atest_adding_rule_set_with_media_type
60
+ @cp.add_rule!('body', 'color: black;', [:handheld,:tty])
61
+ @cp.add_rule!('body', 'color: blue;', :screen)
62
+ assert_equal 'color: black;', @cp.find_by_selector('body', :handheld).join(' ')
63
+ end
64
+
65
+ def atest_selecting_with_all_meda_type
66
+ @cp.add_rule!('body', 'color: black;', [:handheld,:tty])
67
+ assert_equal 'color: black;', @cp.find_by_selector('body', :all).join(' ')
68
+ end
69
+
70
+
71
+ end
@@ -0,0 +1,151 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the CssParser.
4
+ class CssParserTests < Test::Unit::TestCase
5
+ include CssParser
6
+
7
+ def setup
8
+ @cp = Parser.new
9
+ end
10
+
11
+ def test_at_page_rule
12
+ # from http://www.w3.org/TR/CSS21/page.html#page-selectors
13
+ css = <<-EOT
14
+ @page { margin: 2cm }
15
+
16
+ @page :first {
17
+ margin-top: 10cm
18
+ }
19
+ EOT
20
+
21
+ @cp.add_block!(css)
22
+
23
+ assert_equal 'margin: 2cm;', @cp.find_by_selector('@page').join(' ')
24
+ assert_equal 'margin-top: 10cm;', @cp.find_by_selector('@page :first').join(' ')
25
+ end
26
+
27
+ def test_should_ignore_comments
28
+ # see http://www.w3.org/Style/CSS/Test/CSS2.1/current/html4/t040109-c17-comments-00-b.htm
29
+ css =<<-EOT
30
+ /* This is a CSS comment. */
31
+ .one {color: green;} /* Another comment */
32
+ /* The following should not be used:
33
+ .one {color: red;} */
34
+ .two {color: green; /* color: yellow; */}
35
+ /**
36
+ .three {color: red;} */
37
+ .three {color: green;}
38
+ /**/
39
+ .four a b{color: green;}
40
+ /*********/
41
+ .five a {color: green;}
42
+ /* a comment **/
43
+ .six {color: green;}
44
+ EOT
45
+
46
+ @cp.add_block!(css)
47
+ @cp.each_selector_sorted(:all, :order => :asc) do |sel|
48
+ # puts sel.inspect
49
+ assert_equal 'color: green;', sel.declarations_to_s
50
+ end
51
+
52
+ puts "DESCENDING specificity ORDER"
53
+ @cp.each_selector_sorted(:all, :order => :desc) do |sel|
54
+ # puts sel.inspect
55
+ assert_equal 'color: green;', sel.declarations_to_s
56
+ end
57
+
58
+ end
59
+
60
+ def test_parsing_blocks
61
+ # dervived from http://www.w3.org/TR/CSS21/syndata.html#rule-sets
62
+ css = <<-EOT
63
+ div[name='test'] {
64
+
65
+ color:
66
+
67
+ red;
68
+
69
+ }div:hover{coloR:red;
70
+ }div:first-letter{color:red;/*color:blue;}"commented out"*/}
71
+
72
+ p[example="public class foo\
73
+ {\
74
+ private string x;\
75
+ \
76
+ foo(int x) {\
77
+ this.x = 'test';\
78
+ this.x = \"test\";\
79
+ }\
80
+ \
81
+ }"] { color: red }
82
+
83
+ p { color:red}
84
+ EOT
85
+
86
+ @cp.add_block!(css)
87
+
88
+ @cp.each_selector do |sel|
89
+ assert_equal 'color: red;', sel.declarations_to_s
90
+ end
91
+ end
92
+
93
+ # def test_ignoring_malformed_declarations
94
+ # flunk
95
+ # # dervived from http://www.w3.org/TR/CSS21/syndata.html#parsing-errors
96
+ # css = <<-EOT
97
+ # p { color:green }
98
+ # p { color:green; color } /* malformed declaration missing ':', value */
99
+ # p { color:red; color; color:green } /* same with expected recovery */
100
+ # p { color:green; color: } /* malformed declaration missing value */
101
+ # p { color:red; color:; color:green } /* same with expected recovery */
102
+ # p { color:green; color{;color:maroon} } /* unexpected tokens { } */
103
+ # p { color:red; color{;color:maroon}; color:green } /* same with recovery */
104
+ # EOT
105
+ #
106
+ # @cp.add_block!(css)
107
+ #
108
+ # @cp.each_selector do |sel|
109
+ # assert_equal 'color: green;', sel.declarations
110
+ # end
111
+ # end
112
+
113
+ def test_calculating_specificity
114
+ # from http://www.w3.org/TR/CSS21/cascade.html#specificity
115
+ assert_equal 0, CssParser.calculate_specificity('*')
116
+ assert_equal 1, CssParser.calculate_specificity('li')
117
+ assert_equal 2, CssParser.calculate_specificity('li:first-line')
118
+ assert_equal 2, CssParser.calculate_specificity('ul li')
119
+ assert_equal 3, CssParser.calculate_specificity('ul ol+li')
120
+ assert_equal 11, CssParser.calculate_specificity('h1 + *[rel=up]')
121
+ assert_equal 13, CssParser.calculate_specificity('ul ol li.red')
122
+ assert_equal 21, CssParser.calculate_specificity('li.red.level')
123
+ assert_equal 100, CssParser.calculate_specificity('#x34y')
124
+
125
+ # from http://www.hixie.ch/tests/adhoc/css/cascade/specificity/003.html
126
+ assert_equal CssParser.calculate_specificity('div *'), CssParser.calculate_specificity('p')
127
+ assert CssParser.calculate_specificity('body div *') > CssParser.calculate_specificity('div *')
128
+
129
+ # other tests
130
+ assert_equal 11, CssParser.calculate_specificity('h1[id|=123]')
131
+ end
132
+
133
+ def test_converting_uris
134
+ base_uri = 'http://www.example.org/style/basic.css'
135
+ ["body { background: url(yellow) };", "body { background: url('yellow') };",
136
+ "body { background: url('/style/yellow') };",
137
+ "body { background: url(\"../style/yellow\") };",
138
+ "body { background: url(\"lib/../../style/yellow\") };"].each do |css|
139
+ converted_css = CssParser.convert_uris(css, base_uri)
140
+ assert_equal "body { background: url('http://www.example.org/style/yellow') };", converted_css
141
+ end
142
+
143
+ converted_css = CssParser.convert_uris("body { background: url(../style/yellow-dot_symbol$.png?abc=123&amp;def=456&ghi=789#1011) };", base_uri)
144
+ assert_equal "body { background: url('http://www.example.org/style/yellow-dot_symbol$.png?abc=123&amp;def=456&ghi=789#1011') };", converted_css
145
+
146
+ # taken from error log: 2007-10-23 04:37:41#2399
147
+ converted_css = CssParser.convert_uris('.specs {font-family:Helvetica;font-weight:bold;font-style:italic;color:#008CA8;font-size:1.4em;list-style-image:url("images/bullet.gif");}', 'http://www.example.org/directory/file.html')
148
+ assert_equal ".specs {font-family:Helvetica;font-weight:bold;font-style:italic;color:#008CA8;font-size:1.4em;list-style-image:url('http://www.example.org/directory/images/bullet.gif');}", converted_css
149
+ end
150
+
151
+ end