css_parser 1.2.6 → 1.3.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/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
|