css_parser 1.2.6 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/css_parser.rb +6 -5
- data/lib/css_parser/parser.rb +19 -0
- data/lib/css_parser/regexps.rb +1 -0
- data/lib/css_parser/rule_set.rb +3 -4
- metadata +16 -56
- data/test/fixtures/import-circular-reference.css +0 -4
- data/test/fixtures/import-with-media-types.css +0 -3
- data/test/fixtures/import1.css +0 -3
- data/test/fixtures/simple.css +0 -6
- data/test/fixtures/subdir/import2.css +0 -3
- data/test/test_css_parser_basic.rb +0 -64
- data/test/test_css_parser_loading.rb +0 -146
- data/test/test_css_parser_media_types.rb +0 -114
- data/test/test_css_parser_misc.rb +0 -164
- data/test/test_css_parser_regexps.rb +0 -69
- data/test/test_helper.rb +0 -6
- data/test/test_merging.rb +0 -116
- data/test/test_rule_set.rb +0 -90
- data/test/test_rule_set_creating_shorthand.rb +0 -143
- data/test/test_rule_set_expanding_shorthand.rb +0 -223
data/lib/css_parser.rb
CHANGED
@@ -24,8 +24,9 @@ module CssParser
|
|
24
24
|
# If no specificity is explicitly set and the RuleSet has *one* selector,
|
25
25
|
# the specificity is calculated using that selector.
|
26
26
|
#
|
27
|
-
# If no selectors
|
28
|
-
#
|
27
|
+
# If no selectors the specificity is treated as 0.
|
28
|
+
#
|
29
|
+
# If multiple selectors are present then the greatest specificity is used.
|
29
30
|
#
|
30
31
|
# ==== Example #1
|
31
32
|
# rs1 = RuleSet.new(nil, 'color: black;')
|
@@ -67,10 +68,10 @@ module CssParser
|
|
67
68
|
|
68
69
|
specificity = rule_set.specificity
|
69
70
|
unless specificity
|
70
|
-
if rule_set.selectors.length ==
|
71
|
-
specificity = calculate_specificity(rule_set.selectors[0])
|
72
|
-
else
|
71
|
+
if rule_set.selectors.length == 0
|
73
72
|
specificity = 0
|
73
|
+
else
|
74
|
+
specificity = rule_set.selectors.map { |s| calculate_specificity(s) }.compact.max || 0
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
data/lib/css_parser/parser.rb
CHANGED
@@ -73,6 +73,20 @@ module CssParser
|
|
73
73
|
end
|
74
74
|
alias_method :[], :find_by_selector
|
75
75
|
|
76
|
+
# Finds the rule sets that match the given selectors
|
77
|
+
def find_rule_sets(selectors, media_types = :all)
|
78
|
+
rule_sets = []
|
79
|
+
|
80
|
+
selectors.each do |selector|
|
81
|
+
each_rule_set(media_types) do |rule_set|
|
82
|
+
if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
|
83
|
+
rule_sets << rule_set
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
rule_sets
|
89
|
+
end
|
76
90
|
|
77
91
|
# Add a raw block of CSS.
|
78
92
|
#
|
@@ -356,6 +370,11 @@ module CssParser
|
|
356
370
|
add_block!(src, {:media_types => media_types, :base_dir => base_dir})
|
357
371
|
end
|
358
372
|
|
373
|
+
# Load a local CSS string.
|
374
|
+
def load_string!(src, base_dir = nil, media_types = :all)
|
375
|
+
add_block!(src, {:media_types => media_types, :base_dir => base_dir})
|
376
|
+
end
|
377
|
+
|
359
378
|
|
360
379
|
|
361
380
|
protected
|
data/lib/css_parser/regexps.rb
CHANGED
@@ -22,6 +22,7 @@ module CssParser
|
|
22
22
|
|
23
23
|
RE_URI = Regexp.new('(url\([\s]*([\s]*' + RE_STRING.to_s + '[\s]*)[\s]*\))|(url\([\s]*([!#$%&*\-~]|' + RE_NON_ASCII.to_s + '|' + RE_ESCAPE.to_s + ')*[\s]*)\)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE, 'n')
|
24
24
|
URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im
|
25
|
+
RE_GRADIENT = /[a-z\-]*gradient\([a-z ,#%0-9\(\)]*\)$/im
|
25
26
|
|
26
27
|
# Initial parsing
|
27
28
|
RE_AT_IMPORT_RULE = /\@import[\s]+(url\()?["''"]?(.[^'"\s"']*)["''"]?\)?([\w\s\,^\])]*)\)?;?/
|
data/lib/css_parser/rule_set.rb
CHANGED
@@ -22,7 +22,6 @@ module CssParser
|
|
22
22
|
parse_declarations!(block)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
25
|
# Get the value of a property
|
27
26
|
def get_value(property)
|
28
27
|
return '' unless property and not property.empty?
|
@@ -118,7 +117,7 @@ module CssParser
|
|
118
117
|
# Return the CSS rule set as a string.
|
119
118
|
def to_s
|
120
119
|
decs = declarations_to_s
|
121
|
-
"#{@selectors.join} { #{decs} }"
|
120
|
+
"#{@selectors.join(',')} { #{decs} }"
|
122
121
|
end
|
123
122
|
|
124
123
|
# Split shorthand declarations (e.g. +margin+ or +font+) into their constituent parts.
|
@@ -146,7 +145,7 @@ module CssParser
|
|
146
145
|
end
|
147
146
|
end
|
148
147
|
|
149
|
-
split_declaration('background', 'background-image', value.slice!(Regexp.union(CssParser::URI_RX, /none/i)))
|
148
|
+
split_declaration('background', 'background-image', value.slice!(Regexp.union(CssParser::URI_RX, CssParser::RE_GRADIENT, /none/i)))
|
150
149
|
split_declaration('background', 'background-attachment', value.slice!(CssParser::RE_SCROLL_FIXED))
|
151
150
|
split_declaration('background', 'background-repeat', value.slice!(CssParser::RE_REPEAT))
|
152
151
|
split_declaration('background', 'background-color', value.slice!(CssParser::RE_COLOUR))
|
@@ -480,7 +479,7 @@ module CssParser
|
|
480
479
|
# TODO: way too simplistic
|
481
480
|
#++
|
482
481
|
def parse_selectors!(selectors) # :nodoc:
|
483
|
-
@selectors = selectors.split(',')
|
482
|
+
@selectors = selectors.split(',').map { |s| s.strip }
|
484
483
|
end
|
485
484
|
end
|
486
485
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: css_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: addressable
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,63 +21,27 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: rdoc
|
27
|
-
requirement: &2160263460 !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
|
-
requirements:
|
30
|
-
- - ! '>='
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '0'
|
33
|
-
type: :runtime
|
34
|
-
prerelease: false
|
35
|
-
version_requirements: *2160263460
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: rake
|
38
|
-
requirement: &2160258520 !ruby/object:Gem::Requirement
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
25
|
none: false
|
40
26
|
requirements:
|
41
27
|
- - ! '>='
|
42
28
|
- !ruby/object:Gem::Version
|
43
29
|
version: '0'
|
44
|
-
type: :development
|
45
|
-
prerelease: false
|
46
|
-
version_requirements: *2160258520
|
47
30
|
description: A set of classes for parsing CSS in Ruby.
|
48
31
|
email: code@dunae.ca
|
49
32
|
executables: []
|
50
33
|
extensions: []
|
51
34
|
extra_rdoc_files: []
|
52
35
|
files:
|
53
|
-
- lib/css_parser.rb
|
54
36
|
- lib/css_parser/parser.rb
|
55
37
|
- lib/css_parser/regexps.rb
|
56
38
|
- lib/css_parser/rule_set.rb
|
57
|
-
-
|
58
|
-
- test/fixtures/import-with-media-types.css
|
59
|
-
- test/fixtures/import1.css
|
60
|
-
- test/fixtures/simple.css
|
61
|
-
- test/fixtures/subdir/import2.css
|
62
|
-
- test/test_css_parser_basic.rb
|
63
|
-
- test/test_css_parser_loading.rb
|
64
|
-
- test/test_css_parser_media_types.rb
|
65
|
-
- test/test_css_parser_misc.rb
|
66
|
-
- test/test_css_parser_regexps.rb
|
67
|
-
- test/test_helper.rb
|
68
|
-
- test/test_merging.rb
|
69
|
-
- test/test_rule_set.rb
|
70
|
-
- test/test_rule_set_creating_shorthand.rb
|
71
|
-
- test/test_rule_set_expanding_shorthand.rb
|
39
|
+
- lib/css_parser.rb
|
72
40
|
homepage: https://github.com/alexdunae/css_parser
|
73
|
-
licenses:
|
41
|
+
licenses:
|
42
|
+
- MIT
|
74
43
|
post_install_message:
|
75
|
-
rdoc_options:
|
76
|
-
- --all
|
77
|
-
- --inline-source
|
78
|
-
- --line-numbers
|
79
|
-
- --charset
|
80
|
-
- utf-8
|
44
|
+
rdoc_options: []
|
81
45
|
require_paths:
|
82
46
|
- lib
|
83
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -86,26 +50,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
50
|
- - ! '>='
|
87
51
|
- !ruby/object:Gem::Version
|
88
52
|
version: '0'
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
hash: 3425931588029901136
|
89
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
57
|
none: false
|
91
58
|
requirements:
|
92
59
|
- - ! '>='
|
93
60
|
- !ruby/object:Gem::Version
|
94
61
|
version: '0'
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
hash: 3425931588029901136
|
95
65
|
requirements: []
|
96
66
|
rubyforge_project:
|
97
|
-
rubygems_version: 1.8.
|
67
|
+
rubygems_version: 1.8.25
|
98
68
|
signing_key:
|
99
69
|
specification_version: 3
|
100
70
|
summary: Ruby CSS parser.
|
101
|
-
test_files:
|
102
|
-
- test/test_css_parser_basic.rb
|
103
|
-
- test/test_css_parser_loading.rb
|
104
|
-
- test/test_css_parser_media_types.rb
|
105
|
-
- test/test_css_parser_misc.rb
|
106
|
-
- test/test_css_parser_regexps.rb
|
107
|
-
- test/test_helper.rb
|
108
|
-
- test/test_merging.rb
|
109
|
-
- test/test_rule_set.rb
|
110
|
-
- test/test_rule_set_creating_shorthand.rb
|
111
|
-
- test/test_rule_set_expanding_shorthand.rb
|
71
|
+
test_files: []
|
data/test/fixtures/import1.css
DELETED
data/test/fixtures/simple.css
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require File.expand_path(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
|
-
.content { color: red; }
|
14
|
-
EOT
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_finding_by_selector
|
18
|
-
@cp.add_block!(@css)
|
19
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('body').join(' ')
|
20
|
-
assert_equal 'margin: 0px; padding: 0px;', @cp.find_by_selector('p').join(' ')
|
21
|
-
assert_equal 'font: 12px/normal sans-serif;', @cp.find_by_selector('#content').join(' ')
|
22
|
-
assert_equal 'color: red;', @cp.find_by_selector('.content').join(' ')
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_adding_block
|
26
|
-
@cp.add_block!(@css)
|
27
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('body').join
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_adding_block_without_closing_brace
|
31
|
-
@cp.add_block!('p { color: red;')
|
32
|
-
assert_equal 'color: red;', @cp.find_by_selector('p').join
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_adding_a_rule
|
36
|
-
@cp.add_rule!('div', 'color: blue;')
|
37
|
-
assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_adding_a_rule_set
|
41
|
-
rs = CssParser::RuleSet.new('div', 'color: blue;')
|
42
|
-
@cp.add_rule_set!(rs)
|
43
|
-
assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_toggling_uri_conversion
|
47
|
-
# with conversion
|
48
|
-
cp_with_conversion = Parser.new(:absolute_paths => true)
|
49
|
-
cp_with_conversion.add_block!("body { background: url('../style/yellow.png?abc=123') };",
|
50
|
-
:base_uri => 'http://example.org/style/basic.css')
|
51
|
-
|
52
|
-
assert_equal "background: url('http://example.org/style/yellow.png?abc=123');",
|
53
|
-
cp_with_conversion['body'].join(' ')
|
54
|
-
|
55
|
-
# without conversion
|
56
|
-
cp_without_conversion = Parser.new(:absolute_paths => false)
|
57
|
-
cp_without_conversion.add_block!("body { background: url('../style/yellow.png?abc=123') };",
|
58
|
-
:base_uri => 'http://example.org/style/basic.css')
|
59
|
-
|
60
|
-
assert_equal "background: url('../style/yellow.png?abc=123');",
|
61
|
-
cp_without_conversion['body'].join(' ')
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
@@ -1,146 +0,0 @@
|
|
1
|
-
require File.expand_path(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_local_file_with_scheme
|
42
|
-
file_name = 'file://' + File.expand_path(File.dirname(__FILE__)) + '/fixtures/simple.css'
|
43
|
-
@cp.load_uri!(file_name)
|
44
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_loading_a_remote_file
|
48
|
-
@cp.load_uri!("#{@uri_base}/simple.css")
|
49
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
|
50
|
-
end
|
51
|
-
|
52
|
-
# http://github.com/alexdunae/css_parser/issues#issue/4
|
53
|
-
def test_loading_a_remote_file_over_ssl
|
54
|
-
# TODO: test SSL locally
|
55
|
-
@cp.load_uri!("https://dialect.ca/inc/screen.css")
|
56
|
-
assert_match /margin\: 0\;/, @cp.find_by_selector('body').join(' ')
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
def test_following_at_import_rules_local
|
61
|
-
base_dir = File.dirname(__FILE__) + '/fixtures'
|
62
|
-
@cp.load_file!('import1.css', base_dir)
|
63
|
-
|
64
|
-
# from '/import1.css'
|
65
|
-
assert_equal 'color: lime;', @cp.find_by_selector('div').join(' ')
|
66
|
-
|
67
|
-
# from '/subdir/import2.css'
|
68
|
-
assert_equal 'text-decoration: none;', @cp.find_by_selector('a').join(' ')
|
69
|
-
|
70
|
-
# from '/subdir/../simple.css'
|
71
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
|
72
|
-
end
|
73
|
-
|
74
|
-
def test_following_at_import_rules_remote
|
75
|
-
@cp.load_uri!("#{@uri_base}/import1.css")
|
76
|
-
|
77
|
-
# from '/import1.css'
|
78
|
-
assert_equal 'color: lime;', @cp.find_by_selector('div').join(' ')
|
79
|
-
|
80
|
-
# from '/subdir/import2.css'
|
81
|
-
assert_equal 'text-decoration: none;', @cp.find_by_selector('a').join(' ')
|
82
|
-
|
83
|
-
# from '/subdir/../simple.css'
|
84
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
|
85
|
-
end
|
86
|
-
|
87
|
-
def test_following_badly_escaped_import_rules
|
88
|
-
css_block = '@import "http://example.com/css?family=Droid+Sans:regular,bold|Droid+Serif:regular,italic,bold,bolditalic&subset=latin";'
|
89
|
-
|
90
|
-
assert_nothing_raised do
|
91
|
-
@cp.add_block!(css_block, :base_uri => "#{@uri_base}/subdir/")
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def test_following_at_import_rules_from_add_block
|
96
|
-
css_block = '@import "../simple.css";'
|
97
|
-
|
98
|
-
@cp.add_block!(css_block, :base_uri => "#{@uri_base}/subdir/")
|
99
|
-
|
100
|
-
# from 'simple.css'
|
101
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
|
102
|
-
end
|
103
|
-
|
104
|
-
def test_importing_with_media_types
|
105
|
-
@cp.load_uri!("#{@uri_base}/import-with-media-types.css")
|
106
|
-
|
107
|
-
# from simple.css with :screen media type
|
108
|
-
assert_equal 'margin: 0px;', @cp.find_by_selector('p', :screen).join(' ')
|
109
|
-
assert_equal '', @cp.find_by_selector('p', :tty).join(' ')
|
110
|
-
end
|
111
|
-
|
112
|
-
def test_local_circular_reference_exception
|
113
|
-
assert_raise CircularReferenceError do
|
114
|
-
@cp.load_file!(File.dirname(__FILE__) + '/fixtures/import-circular-reference.css')
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def test_remote_circular_reference_exception
|
119
|
-
assert_raise CircularReferenceError do
|
120
|
-
@cp.load_uri!("#{@uri_base}/import-circular-reference.css")
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def test_suppressing_circular_reference_exceptions
|
125
|
-
cp_without_exceptions = Parser.new(:io_exceptions => false)
|
126
|
-
|
127
|
-
assert_nothing_raised CircularReferenceError do
|
128
|
-
cp_without_exceptions.load_uri!("#{@uri_base}/import-circular-reference.css")
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def test_toggling_not_found_exceptions
|
133
|
-
cp_with_exceptions = Parser.new(:io_exceptions => true)
|
134
|
-
|
135
|
-
assert_raise RemoteFileError do
|
136
|
-
cp_with_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
|
137
|
-
end
|
138
|
-
|
139
|
-
cp_without_exceptions = Parser.new(:io_exceptions => false)
|
140
|
-
|
141
|
-
assert_nothing_raised RemoteFileError do
|
142
|
-
cp_without_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
end
|