csspool 4.0.0.pre → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +12 -0
- data/Gemfile.lock +6 -6
- data/Manifest.txt +10 -2
- data/Rakefile +1 -0
- data/lib/csspool/css/declaration.rb +1 -1
- data/lib/csspool/css/document.rb +5 -1
- data/lib/csspool/css/document_handler.rb +43 -11
- data/lib/csspool/css/fontface_rule.rb +13 -0
- data/lib/csspool/css/import_rule.rb +0 -5
- data/lib/csspool/css/media_feature.rb +14 -0
- data/lib/csspool/css/media_query.rb +19 -0
- data/lib/csspool/css/media_query_list.rb +41 -0
- data/lib/csspool/css/{media.rb → media_type.rb} +6 -4
- data/lib/csspool/css/parser.rb +1272 -671
- data/lib/csspool/css/parser.y +223 -53
- data/lib/csspool/css/rule_set.rb +4 -3
- data/lib/csspool/css/supports_rule.rb +14 -0
- data/lib/csspool/css/tokenizer.rb +92 -16
- data/lib/csspool/css/tokenizer.rex +42 -19
- data/lib/csspool/css.rb +6 -1
- data/lib/csspool/node.rb +22 -0
- data/lib/csspool/selector.rb +5 -4
- data/lib/csspool/selectors/pseudo.rb +2 -2
- data/lib/csspool/terms/function.rb +1 -1
- data/lib/csspool/terms/resolution.rb +13 -0
- data/lib/csspool/terms.rb +1 -0
- data/lib/csspool/visitors/children.rb +16 -2
- data/lib/csspool/visitors/comparable.rb +27 -8
- data/lib/csspool/visitors/iterator.rb +5 -3
- data/lib/csspool/visitors/to_css.rb +73 -20
- data/test/css/test_document_query.rb +37 -38
- data/test/css/test_font_face.rb +16 -0
- data/test/css/test_import_rule.rb +0 -3
- data/test/css/test_media_rule.rb +92 -0
- data/test/css/test_node_position.rb +81 -0
- data/test/css/test_parser.rb +12 -39
- data/test/css/test_supports_rule.rb +133 -0
- data/test/css/test_tokenizer.rb +4 -4
- data/test/css/test_variables.rb +33 -0
- data/test/helper.rb +3 -3
- data/test/sac/test_parser.rb +1 -0
- data/test/test_parser.rb +22 -9
- data/test/test_selector.rb +44 -4
- data/test/test_term.rb +29 -0
- data/test/visitors/test_comparable.rb +8 -0
- data/test/visitors/test_to_css.rb +89 -13
- metadata +54 -58
- data/test/_local_helper.rb +0 -2
@@ -7,10 +7,8 @@ macro
|
|
7
7
|
w [\s]*
|
8
8
|
nonascii [^\0-\177]
|
9
9
|
num ([0-9]*\.[0-9]+|[0-9]+)
|
10
|
-
length {num}(px|cm|mm|in|pt|pc)
|
10
|
+
length {num}(px|cm|mm|in|pt|pc|mozmm|em|ex|ch|rem|vh|vw|vmin|vmax)
|
11
11
|
percentage {num}%
|
12
|
-
ems {num}em
|
13
|
-
exs {num}ex
|
14
12
|
unicode \\[0-9A-Fa-f]{1,6}(\r\n|[\s])?
|
15
13
|
nth ([\+\-]?[0-9]*n({w}[\+\-]{w}[0-9]+)?|[\+\-]?[0-9]+|odd|even)
|
16
14
|
vendorprefix \-[A-Za-z]+\-
|
@@ -18,8 +16,7 @@ macro
|
|
18
16
|
escape {unicode}|\\[^\n\r\f0-9A-Fa-f]
|
19
17
|
nmchar [_A-Za-z0-9-]|{nonascii}|{escape}
|
20
18
|
nmstart [_A-Za-z]|{nonascii}|{escape}
|
21
|
-
ident
|
22
|
-
func [-@]?({nmstart})({nmchar}|[.])*
|
19
|
+
ident \-?({nmstart})({nmchar})*
|
23
20
|
name ({nmchar})+
|
24
21
|
string1 "([^\n\r\f\\"]|\\{nl}|{nonascii}|{escape})*"
|
25
22
|
string2 '([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*'
|
@@ -28,24 +25,43 @@ macro
|
|
28
25
|
invalid2 '([^\n\r\f\\']|\\{nl}|{nonascii}|{escape})*
|
29
26
|
invalid ({invalid1}|{invalid2})
|
30
27
|
comment \/\*(.|{w})*?\*\/
|
31
|
-
|
32
|
-
unit ({num}|{length}|{percentage}|{ems}|{exs})
|
33
|
-
product {unit}({w}(\*{w}{unit}|\/{w}{num}))*
|
34
|
-
sum {product}([\s]+[\+\-][\s]+{product})*
|
35
|
-
calc ({vendorprefix})?calc\({w}{sum}{w}\)
|
36
|
-
math {calc}{w}
|
28
|
+
variablename \-\-{name}
|
37
29
|
|
38
30
|
rule
|
39
31
|
|
40
32
|
# [:state] pattern [actions]
|
41
33
|
|
34
|
+
# "Logical queries", such as those used by @supports and @media, include keywords that match our IDENT pattern.
|
35
|
+
# Including these as lexical words makes their use anywhere else (even inside other words!) not possible.
|
36
|
+
# So we define a new state, and list the things that can occur.
|
37
|
+
:LOGICQUERY url\({w}{string}{w}\) { [:URI, st(text)] }
|
38
|
+
:LOGICQUERY url\({w}([!#\$%&*-~]|{nonascii}|{escape})*{w}\) { [:URI, st(text)] }
|
39
|
+
:LOGICQUERY {ident}\(\s* { [:FUNCTION, st(text)] }
|
40
|
+
:LOGICQUERY {w}(and|only|not|or)[\s]+ { [text.upcase.strip.intern, st(text)] }
|
41
|
+
:LOGICQUERY {w}{num}(dpi|dpcm) { [:RESOLUTION, st(text)]}
|
42
|
+
:LOGICQUERY {w}{length}{w} { [:LENGTH, st(text)] }
|
43
|
+
:LOGICQUERY {w}{num}(deg|rad|grad){w} { [:ANGLE, st(text)] }
|
44
|
+
:LOGICQUERY {w}{num}(ms|s){w} { [:TIME, st(text)] }
|
45
|
+
:LOGICQUERY {w}{num}[k]?hz{w} { [:FREQ, st(text)] }
|
46
|
+
:LOGICQUERY {w}{percentage}{w} { [:PERCENTAGE, st(text)] }
|
47
|
+
:LOGICQUERY {w}{num}{w} { [:NUMBER, st(text)] }
|
48
|
+
:LOGICQUERY {ident} { [:IDENT, st(text)] }
|
49
|
+
:LOGICQUERY {w},{w} { [:COMMA, st(',')] }
|
50
|
+
:LOGICQUERY {w}\) { [:RPAREN, st(text)] }
|
51
|
+
:LOGICQUERY {w}\( { [:LPAREN, st(text)] }
|
52
|
+
:LOGICQUERY \: { [:COLON, st(text)] }
|
53
|
+
:LOGICQUERY {w}!({w}|{w}{comment}{w})important{w} { [:IMPORTANT_SYM, st(text)] }
|
54
|
+
# this marks the end of our logical query
|
55
|
+
:LOGICQUERY {w}\{{w} { @state = nil; [:LBRACE, st(text)] }
|
56
|
+
:LOGICQUERY [\s]+ { [:S, st(text)] }
|
57
|
+
|
42
58
|
url\({w}{string}{w}\) { [:URI, st(text)] }
|
43
59
|
url\({w}([!#\$%&*-~]|{nonascii}|{escape})*{w}\) { [:URI, st(text)] }
|
44
60
|
U\+[0-9a-fA-F?]{1,6}(-[0-9a-fA-F]{1,6})? {[:UNICODE_RANGE, st(text)] }
|
45
61
|
{w}{comment}{w} { next_token }
|
46
62
|
|
47
63
|
# this one takes a selector as a parameter
|
48
|
-
not\(
|
64
|
+
not\({w} { [:NOT_PSEUDO_CLASS, st(text)] }
|
49
65
|
|
50
66
|
# this one takes an "nth" value
|
51
67
|
(nth\-child|nth\-last\-child|nth\-of\-type|nth\-last\-of\-type)\({w}{nth}{w}\) { [:NTH_PSEUDO_CLASS, st(text)] }
|
@@ -57,17 +73,24 @@ rule
|
|
57
73
|
(domain|url\-prefix)\({w}{string}{w}\) { [:FUNCTION_NO_QUOTE, st(text)] }
|
58
74
|
(domain|url\-prefix)\({w}([!#\$%&*-~]|{nonascii}|{escape})*{w}\) { [:FUNCTION_NO_QUOTE, st(text)] }
|
59
75
|
|
60
|
-
|
76
|
+
# mozilla-specific pseudo-elements that can be used with one or two colons and can have multiple parameters
|
77
|
+
(\-moz\-non\-element|\-moz\-anonymous\-block|\-moz\-anonymous\-positioned\-block|\-moz\-mathml\-anonymous\-block|\-moz\-xul\-anonymous\-block|\-moz\-hframeset\-border|\-moz\-vframeset\-border|\-moz\-line\-frame|\-moz\-button\-content|\-moz\-buttonlabel|\-moz\-cell\-content|\-moz\-dropdown\-list|\-moz\-fieldset\-content|\-moz\-frameset\-blank|\-moz\-display\-comboboxcontrol\-frame|\-moz\-html\-canvas\-content|\-moz\-inline\-table|\-moz\-table|\-moz\-table\-cell|\-moz\-table\-column\-group|\-moz\-table\-column|\-moz\-table\-outer|\-moz\-table\-row\-group|\-moz\-table\-row|\-moz\-canvas|\-moz\-pagebreak|\-moz\-page|\-moz\-pagecontent|\-moz\-page\-sequence|\-moz\-scrolled\-content|\-moz\-scrolled\-canvas|\-moz\-scrolled\-page\-sequence|\-moz\-column\-content|\-moz\-viewport|\-moz\-viewport\-scroll|\-moz\-anonymous\-flex\-item|\-moz\-tree\-column|\-moz\-tree\-row|\-moz\-tree\-separator|\-moz\-tree\-cell|\-moz\-tree\-indentation|\-moz\-tree\-line|\-moz\-tree\-twisty|\-moz\-tree\-image|\-moz\-tree\-cell\-text|\-moz\-tree\-checkbox|\-moz\-tree\-progressmeter|\-moz\-tree\-drop\-feedback|\-moz\-svg\-outer\-svg\-anon\-child|\-moz\-svg\-foreign\-content|\-moz\-svg\-text)\( { [:MOZ_PSEUDO_ELEMENT, st(text)] }
|
78
|
+
|
79
|
+
{w}@media{w} { @state = :LOGICQUERY; [:MEDIA_SYM, st(text)] }
|
80
|
+
{w}@supports{w} { @state = :LOGICQUERY; [:SUPPORTS_SYM, st(text)] }
|
81
|
+
|
82
|
+
calc\(\s* { [:CALC_SYM, st(text)] }
|
83
|
+
{ident}\(\s* { [:FUNCTION, st(text)] }
|
61
84
|
|
62
|
-
{func}\(\s* { [:FUNCTION, st(text)] }
|
63
85
|
{w}@import{w} { [:IMPORT_SYM, st(text)] }
|
64
86
|
{w}@page{w} { [:PAGE_SYM, st(text)] }
|
65
87
|
{w}@charset{w} { [:CHARSET_SYM, st(text)] }
|
66
|
-
{w}@media{w} { [:MEDIA_SYM, st(text)] }
|
67
88
|
{w}@({vendorprefix})?document{w} { [:DOCUMENT_QUERY_SYM, st(text)] }
|
68
89
|
{w}@namespace{w} { [:NAMESPACE_SYM, st(text)] }
|
90
|
+
{w}@font\-face{w} { [:FONTFACE_SYM, st(text)] }
|
69
91
|
{w}@({vendorprefix})?keyframes{w} { [:KEYFRAMES_SYM, st(text)] }
|
70
92
|
{w}!({w}|{w}{comment}{w})important{w} { [:IMPORTANT_SYM, st(text)] }
|
93
|
+
{variablename} { [:VARIABLE_NAME, st(text)] }
|
71
94
|
{ident} { [:IDENT, st(text)] }
|
72
95
|
\#{name} { [:HASH, st(text)] }
|
73
96
|
{w}~={w} { [:INCLUDES, st(text)] }
|
@@ -78,6 +101,7 @@ rule
|
|
78
101
|
{w}!={w} { [:NOT_EQUAL, st(text)] }
|
79
102
|
{w}={w} { [:EQUAL, st(text)] }
|
80
103
|
{w}\) { [:RPAREN, st(text)] }
|
104
|
+
{w}\( { [:LPAREN, st(text)] }
|
81
105
|
\[{w} { [:LSQUARE, st(text)] }
|
82
106
|
{w}\] { [:RSQUARE, st(text)] }
|
83
107
|
{w}\+{w} { [:PLUS, st(text)] }
|
@@ -86,10 +110,9 @@ rule
|
|
86
110
|
{w}>{w} { [:GREATER, st(text)] }
|
87
111
|
{w},{w} { [:COMMA, st(',')] }
|
88
112
|
{w};{w} { [:SEMI, st(';')] }
|
113
|
+
\: { [:COLON, st(text)] }
|
89
114
|
\* { [:STAR, st(text)] }
|
90
115
|
{w}~{w} { [:TILDE, st(text)] }
|
91
|
-
{w}{ems}{w} { [:EMS, st(text)] }
|
92
|
-
{w}{exs}{w} { [:EXS, st(text)] }
|
93
116
|
|
94
117
|
{w}{length}{w} { [:LENGTH, st(text)] }
|
95
118
|
{w}{num}(deg|rad|grad){w} { [:ANGLE, st(text)] }
|
@@ -104,8 +127,8 @@ rule
|
|
104
127
|
--> { [:CDC, st(text)] }
|
105
128
|
{w}\-(?!{ident}){w} { [:MINUS, st(text)] }
|
106
129
|
{w}\+{w} { [:PLUS, st(text)] }
|
107
|
-
|
108
|
-
|
130
|
+
|
131
|
+
|
109
132
|
[\s]+ { [:S, st(text)] }
|
110
133
|
{string} { [:STRING, st(text)] }
|
111
134
|
{invalid} { [:INVALID, st(text)] }
|
data/lib/csspool/css.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
require 'csspool/css/tokenizer'
|
2
2
|
require 'csspool/css/charset'
|
3
|
+
require 'csspool/css/fontface_rule'
|
3
4
|
require 'csspool/css/import_rule'
|
4
|
-
require 'csspool/css/
|
5
|
+
require 'csspool/css/media_query'
|
6
|
+
require 'csspool/css/media_query_list'
|
7
|
+
require 'csspool/css/media_feature'
|
8
|
+
require 'csspool/css/media_type'
|
5
9
|
require 'csspool/css/rule_set'
|
6
10
|
require 'csspool/css/declaration'
|
7
11
|
require 'csspool/css/document'
|
8
12
|
require 'csspool/css/document_handler'
|
9
13
|
require 'csspool/css/document_query'
|
14
|
+
require 'csspool/css/supports_rule'
|
10
15
|
require 'csspool/css/namespace_rule'
|
11
16
|
require 'csspool/css/keyframes_rule'
|
12
17
|
require 'csspool/css/keyframes_block'
|
data/lib/csspool/node.rb
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
module CSSPool
|
2
2
|
class Node
|
3
3
|
include Enumerable
|
4
|
+
|
5
|
+
# These give the position of this node in the source CSS. Not all
|
6
|
+
# node types are supported. "Outer" represents the start/end of
|
7
|
+
# this node, "inner" represents the start/end of this node's
|
8
|
+
# children. Outer will not contain leading or trailing comments or
|
9
|
+
# whitespace, while inner will. For example, in this code:
|
10
|
+
#
|
11
|
+
# ---
|
12
|
+
# 1 /* test1 */
|
13
|
+
# 2 @document domain('example.com') {
|
14
|
+
# 3 /* test2 */
|
15
|
+
# 4 * { color: blue; }
|
16
|
+
# 5 /* test3 */
|
17
|
+
# 6 } /* test4 */
|
18
|
+
# ---
|
19
|
+
#
|
20
|
+
# The index will represent the position:
|
21
|
+
# outer_start_pos: 2:0
|
22
|
+
# inner_start_pos: 2:33
|
23
|
+
# inner_end_pos: 6:0
|
24
|
+
# outer_end_pos: 6:1
|
25
|
+
attr_accessor :outer_start_pos, :inner_start_pos, :inner_end_pos, :outer_end_pos
|
4
26
|
|
5
27
|
def accept target
|
6
28
|
target.accept self
|
data/lib/csspool/selector.rb
CHANGED
@@ -17,15 +17,16 @@ module CSSPool
|
|
17
17
|
def specificity
|
18
18
|
a = b = c = 0
|
19
19
|
simple_selectors.each do |s|
|
20
|
-
if
|
20
|
+
if s.is_a?(Selectors::Type) && s.name && s.name != ''
|
21
21
|
c += 1
|
22
22
|
end
|
23
23
|
s.additional_selectors.each do |additional_selector|
|
24
|
-
|
24
|
+
case additional_selector
|
25
|
+
when Selectors::Id
|
25
26
|
a += 1
|
26
|
-
|
27
|
+
when Selectors::PseudoElement
|
27
28
|
c += 1
|
28
|
-
|
29
|
+
when Selectors::Class, Selectors::PseudoClass, Selectors::Attribute
|
29
30
|
b += 1
|
30
31
|
end
|
31
32
|
end
|
@@ -3,10 +3,10 @@ require 'csspool/selectors/pseudo_element'
|
|
3
3
|
|
4
4
|
module CSSPool
|
5
5
|
module Selectors
|
6
|
-
def
|
6
|
+
def self.pseudo name
|
7
7
|
# FIXME: This is a bit of an ugly solution. Should be able to handle it
|
8
8
|
# more elegantly, and without calling out css2
|
9
|
-
css2_pseudo_elements =
|
9
|
+
css2_pseudo_elements =
|
10
10
|
if %w{after before first-letter first-line}.include? name
|
11
11
|
PseudoElement.new name, true
|
12
12
|
else
|
@@ -6,7 +6,7 @@ module CSSPool
|
|
6
6
|
attr_accessor :parse_location
|
7
7
|
attr_accessor :operator
|
8
8
|
|
9
|
-
def initialize name, params, operator = nil, parse_location = nil
|
9
|
+
def initialize name, params = [], operator = nil, parse_location = nil
|
10
10
|
@name = name
|
11
11
|
@params = params
|
12
12
|
@operator = operator
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module CSSPool
|
2
|
+
module Terms
|
3
|
+
class Resolution < CSSPool::Node
|
4
|
+
attr_accessor :number, :unit, :parse_location
|
5
|
+
|
6
|
+
def initialize(number, unit, parse_location = {})
|
7
|
+
@number = number
|
8
|
+
@unit = unit
|
9
|
+
@parse_location = parse_location
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/csspool/terms.rb
CHANGED
@@ -13,7 +13,7 @@ module CSSPool
|
|
13
13
|
target.media_list
|
14
14
|
end
|
15
15
|
|
16
|
-
visitor_for CSS::
|
16
|
+
visitor_for CSS::MediaType,
|
17
17
|
CSS::Charset,
|
18
18
|
Selectors::Id,
|
19
19
|
Selectors::Class,
|
@@ -25,7 +25,8 @@ module CSSPool
|
|
25
25
|
Terms::Number,
|
26
26
|
Terms::Hash,
|
27
27
|
Terms::Function,
|
28
|
-
Terms::Rgb
|
28
|
+
Terms::Rgb,
|
29
|
+
Terms::Resolution do |target|
|
29
30
|
[]
|
30
31
|
end
|
31
32
|
|
@@ -37,6 +38,10 @@ module CSSPool
|
|
37
38
|
target.selectors + target.declarations
|
38
39
|
end
|
39
40
|
|
41
|
+
visitor_for CSS::FontfaceRule do |target|
|
42
|
+
target.declarations
|
43
|
+
end
|
44
|
+
|
40
45
|
visitor_for Selector do |target|
|
41
46
|
target.simple_selectors
|
42
47
|
end
|
@@ -44,6 +49,15 @@ module CSSPool
|
|
44
49
|
visitor_for Selectors::Type, Selectors::Universal, Selectors::Simple do |target|
|
45
50
|
target.additional_selectors
|
46
51
|
end
|
52
|
+
|
53
|
+
visitor_for CSS::MediaQuery do |target|
|
54
|
+
[target.media_expr].concat(target.and_exprs).compact
|
55
|
+
end
|
56
|
+
|
57
|
+
visitor_for CSS::MediaQueryList do |target|
|
58
|
+
target.media_queries
|
59
|
+
end
|
60
|
+
|
47
61
|
end
|
48
62
|
end
|
49
63
|
end
|
@@ -17,7 +17,7 @@ module CSSPool
|
|
17
17
|
end
|
18
18
|
|
19
19
|
visitor_for CSS::RuleSet do |target|
|
20
|
-
[:selectors, :declarations, :
|
20
|
+
[:selectors, :declarations, :parent_rule].all? { |m|
|
21
21
|
target.send(m) == @other.send(m)
|
22
22
|
}
|
23
23
|
end
|
@@ -38,13 +38,7 @@ module CSSPool
|
|
38
38
|
}
|
39
39
|
end
|
40
40
|
|
41
|
-
visitor_for CSS::
|
42
|
-
[:media_list].all? { |m|
|
43
|
-
target.send(m) == @other.send(m)
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
visitor_for Selectors::Id, Selectors::Class do |target|
|
41
|
+
visitor_for CSS::MediaType, Selectors::Id, Selectors::Class do |target|
|
48
42
|
target.name == @other.name
|
49
43
|
end
|
50
44
|
|
@@ -61,6 +55,24 @@ module CSSPool
|
|
61
55
|
}
|
62
56
|
end
|
63
57
|
|
58
|
+
visitor_for CSS::MediaFeature do |target|
|
59
|
+
[:property, :value].all? { |m|
|
60
|
+
target.send(m) == @other.send(m)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
visitor_for CSS::MediaQuery do |target|
|
65
|
+
[:only_or_not, :media_expr, :and_exprs].all? { |m|
|
66
|
+
target.send(m) == @other.send(m)
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
visitor_for CSS::MediaQueryList do |target|
|
71
|
+
[:media_queries].all? { |m|
|
72
|
+
target.send(m) == @other.send(m)
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
64
76
|
visitor_for Terms::Function do |target|
|
65
77
|
[:name, :params, :operator].all? { |m|
|
66
78
|
target.send(m) == @other.send(m)
|
@@ -85,6 +97,13 @@ module CSSPool
|
|
85
97
|
:operator
|
86
98
|
].all? { |m| target.send(m) == @other.send(m) }
|
87
99
|
end
|
100
|
+
|
101
|
+
visitor_for Terms::Resolution do |target|
|
102
|
+
[
|
103
|
+
:number,
|
104
|
+
:unit
|
105
|
+
].all? { |m| target.send(m) == @other.send(m) }
|
106
|
+
end
|
88
107
|
end
|
89
108
|
end
|
90
109
|
end
|
@@ -14,7 +14,7 @@ module CSSPool
|
|
14
14
|
@block.call target
|
15
15
|
end
|
16
16
|
|
17
|
-
visitor_for Selectors::Universal, Selectors::Simple do |target|
|
17
|
+
visitor_for Selectors::Universal, Selectors::Simple, CSS::MediaQuery, CSS::MediaQueryList do |target|
|
18
18
|
target.children.each do |node|
|
19
19
|
node.accept self
|
20
20
|
end
|
@@ -32,7 +32,7 @@ module CSSPool
|
|
32
32
|
@block.call target
|
33
33
|
end
|
34
34
|
|
35
|
-
visitor_for CSS::
|
35
|
+
visitor_for CSS::MediaType,
|
36
36
|
Selectors::Id,
|
37
37
|
Selectors::Class,
|
38
38
|
Selectors::PseudoClass,
|
@@ -43,7 +43,9 @@ module CSSPool
|
|
43
43
|
Terms::Number,
|
44
44
|
Terms::Hash,
|
45
45
|
Terms::Function,
|
46
|
-
Terms::Rgb
|
46
|
+
Terms::Rgb,
|
47
|
+
Terms::Resolution,
|
48
|
+
CSS::MediaFeature do |target|
|
47
49
|
@block.call target
|
48
50
|
end
|
49
51
|
|
@@ -18,8 +18,13 @@ module CSSPool
|
|
18
18
|
end
|
19
19
|
|
20
20
|
visitor_for CSS::Document do |target|
|
21
|
-
#
|
22
|
-
|
21
|
+
# FIXME - this does not handle nested parent rules, like
|
22
|
+
# @document domain(example.com) {
|
23
|
+
# @media screen {
|
24
|
+
# a { color: blue; }
|
25
|
+
# }
|
26
|
+
# }
|
27
|
+
current_parent_rule = []
|
23
28
|
|
24
29
|
tokens = []
|
25
30
|
|
@@ -31,34 +36,72 @@ module CSSPool
|
|
31
36
|
tokens << ir.accept(self)
|
32
37
|
end
|
33
38
|
|
39
|
+
target.fontface_rules.each do |ffr|
|
40
|
+
tokens << ffr.accept(self)
|
41
|
+
end
|
42
|
+
|
34
43
|
target.rule_sets.each { |rs|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
tokens << "#{indent}@media#{media} {"
|
44
|
+
# FIXME - handle other kinds of parents
|
45
|
+
if !rs.parent_rule.nil? and rs.parent_rule != current_parent_rule
|
46
|
+
media = rs.parent_rule
|
47
|
+
tokens << "#{indent}@media #{media} {"
|
40
48
|
@indent_level += 1
|
41
49
|
end
|
42
50
|
|
43
51
|
tokens << rs.accept(self)
|
44
52
|
|
45
|
-
if rs.
|
46
|
-
|
47
|
-
|
48
|
-
|
53
|
+
if rs.parent_rule != current_parent_rule
|
54
|
+
current_parent_rule = rs.parent_rule
|
55
|
+
if !rs.parent_rule.nil?
|
56
|
+
@indent_level -= 1
|
57
|
+
tokens << "#{indent}}"
|
58
|
+
end
|
49
59
|
end
|
50
60
|
}
|
51
61
|
tokens.join(line_break)
|
52
62
|
end
|
53
63
|
|
64
|
+
visitor_for CSS::MediaType do |target|
|
65
|
+
escape_css_identifier(target.name)
|
66
|
+
end
|
67
|
+
|
68
|
+
visitor_for CSS::MediaFeature do |target|
|
69
|
+
"(#{escape_css_identifier(target.property)}:#{target.value})"
|
70
|
+
end
|
71
|
+
|
72
|
+
visitor_for CSS::MediaQuery do |target|
|
73
|
+
ret = ''
|
74
|
+
if target.only_or_not
|
75
|
+
ret << target.only_or_not.to_s + ' '
|
76
|
+
end
|
77
|
+
ret << target.media_expr.accept(self)
|
78
|
+
if target.and_exprs.any?
|
79
|
+
ret << ' and '
|
80
|
+
end
|
81
|
+
ret << target.and_exprs.map { |expr| expr.accept(self) }.join(' and ')
|
82
|
+
end
|
83
|
+
|
84
|
+
visitor_for CSS::MediaQueryList do |target|
|
85
|
+
target.media_queries.map do |m|
|
86
|
+
m.accept(self)
|
87
|
+
end.join(', ')
|
88
|
+
end
|
89
|
+
|
54
90
|
visitor_for CSS::Charset do |target|
|
55
91
|
"@charset \"#{escape_css_string target.name}\";"
|
56
92
|
end
|
57
93
|
|
94
|
+
visitor_for CSS::FontfaceRule do |target|
|
95
|
+
"@font-face {#{line_break}" +
|
96
|
+
"#{indent}" +
|
97
|
+
target.declarations.map { |decl| decl.accept self }.join(line_break) +
|
98
|
+
"#{line_break}#{indent}}"
|
99
|
+
end
|
100
|
+
|
58
101
|
visitor_for CSS::ImportRule do |target|
|
59
102
|
media = ''
|
60
103
|
media = " " + target.media_list.map do |medium|
|
61
|
-
escape_css_identifier medium.
|
104
|
+
escape_css_identifier medium.name
|
62
105
|
end.join(', ') if target.media_list.length > 0
|
63
106
|
|
64
107
|
"#{indent}@import #{target.uri.accept(self)}#{media};"
|
@@ -77,17 +120,22 @@ module CSSPool
|
|
77
120
|
end
|
78
121
|
|
79
122
|
visitor_for CSS::RuleSet do |target|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
123
|
+
if target.selectors.any?
|
124
|
+
"#{indent}" +
|
125
|
+
target.selectors.map { |sel| sel.accept self }.join(", ") + " {#{line_break}" +
|
126
|
+
target.declarations.map { |decl| decl.accept self }.join(line_break) +
|
127
|
+
"#{line_break}#{indent}}"
|
128
|
+
else
|
129
|
+
''
|
130
|
+
end
|
84
131
|
end
|
85
132
|
|
86
133
|
visitor_for CSS::Declaration do |target|
|
87
134
|
important = target.important? ? ' !important' : ''
|
88
135
|
|
136
|
+
# only output indents and semicolons if this is in a ruleset
|
89
137
|
indent {
|
90
|
-
"#{indent}#{escape_css_identifier target.property}: " + target.expressions.map { |exp|
|
138
|
+
"#{target.rule_set.nil? ? '' : indent}#{escape_css_identifier target.property}: " + target.expressions.map { |exp|
|
91
139
|
|
92
140
|
op = '/' == exp.operator ? ' /' : exp.operator
|
93
141
|
|
@@ -95,7 +143,7 @@ module CSSPool
|
|
95
143
|
op,
|
96
144
|
exp.accept(self),
|
97
145
|
].join ' '
|
98
|
-
}.join.strip +
|
146
|
+
}.join.strip + important + (target.rule_set.nil? ? '' : ';')
|
99
147
|
}
|
100
148
|
end
|
101
149
|
|
@@ -114,7 +162,7 @@ module CSSPool
|
|
114
162
|
visitor_for Terms::Function do |target|
|
115
163
|
"#{escape_css_identifier target.name}(" +
|
116
164
|
target.params.map { |x|
|
117
|
-
[
|
165
|
+
x.is_a?(String) ? x : [
|
118
166
|
x.operator,
|
119
167
|
x.accept(self)
|
120
168
|
].compact.join(' ')
|
@@ -145,6 +193,10 @@ module CSSPool
|
|
145
193
|
].compact.join
|
146
194
|
end
|
147
195
|
|
196
|
+
visitor_for Terms::Resolution do |target|
|
197
|
+
"#{target.number}#{target.unit}"
|
198
|
+
end
|
199
|
+
|
148
200
|
visitor_for Selector do |target|
|
149
201
|
target.simple_selectors.map { |ss| ss.accept self }.join
|
150
202
|
end
|
@@ -153,7 +205,8 @@ module CSSPool
|
|
153
205
|
combo = {
|
154
206
|
:s => ' ',
|
155
207
|
:+ => ' + ',
|
156
|
-
:> => ' > '
|
208
|
+
:> => ' > ',
|
209
|
+
:~ => ' ~ '
|
157
210
|
}[target.combinator]
|
158
211
|
|
159
212
|
name = [nil, '*'].include?(target.name) ? target.name : escape_css_identifier(target.name)
|
@@ -6,71 +6,70 @@ module CSSPool
|
|
6
6
|
|
7
7
|
def test_function
|
8
8
|
doc = CSSPool.CSS <<-eocss
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
@document domain("example.com") {
|
10
|
+
* { color: blue; }
|
11
|
+
}
|
12
12
|
eocss
|
13
13
|
assert_equal 1, doc.document_queries.size
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
assert_equal 1, doc.document_queries[0].url_functions.size
|
15
|
+
assert_equal CSSPool::Terms::Function, doc.document_queries[0].url_functions[0].class
|
16
|
+
assert_equal 'domain', doc.document_queries[0].url_functions[0].name
|
17
|
+
assert_equal 'example.com', doc.document_queries[0].url_functions[0].params[0].value
|
18
|
+
assert_equal 1, doc.document_queries[0].rule_sets.size
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_function_no_quote
|
22
22
|
doc = CSSPool.CSS <<-eocss
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
@document domain(example.com) {
|
24
|
+
* { color: blue; }
|
25
|
+
}
|
26
26
|
eocss
|
27
27
|
assert_equal 1, doc.document_queries.size
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
assert_equal 1, doc.document_queries[0].url_functions.size
|
29
|
+
assert_equal CSSPool::Terms::Function, doc.document_queries[0].url_functions[0].class
|
30
|
+
assert_equal 'domain', doc.document_queries[0].url_functions[0].name
|
31
|
+
assert_equal 'example.com', doc.document_queries[0].url_functions[0].params[0].value
|
32
|
+
assert_equal 1, doc.document_queries[0].rule_sets.size
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_multiple
|
36
36
|
doc = CSSPool.CSS <<-eocss
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
@document domain(example.com), url-prefix(http://example.com) {
|
38
|
+
* { color: blue; }
|
39
|
+
}
|
40
40
|
eocss
|
41
41
|
assert_equal 1, doc.document_queries.size
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
42
|
+
assert_equal 2, doc.document_queries[0].url_functions.size
|
43
|
+
assert_equal CSSPool::Terms::Function, doc.document_queries[0].url_functions[0].class
|
44
|
+
assert_equal 'domain', doc.document_queries[0].url_functions[0].name
|
45
|
+
assert_equal 'example.com', doc.document_queries[0].url_functions[0].params[0].value
|
46
|
+
assert_equal CSSPool::Terms::Function, doc.document_queries[0].url_functions[1].class
|
47
|
+
assert_equal 'url-prefix', doc.document_queries[0].url_functions[1].name
|
48
|
+
assert_equal 'http://example.com', doc.document_queries[0].url_functions[1].params[0].value
|
49
|
+
assert_equal 1, doc.document_queries[0].rule_sets.size
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_url
|
53
53
|
doc = CSSPool.CSS <<-eocss
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
@document url("http://www.example.com") {
|
55
|
+
* { color: blue; }
|
56
|
+
}
|
57
57
|
eocss
|
58
58
|
assert_equal 1, doc.document_queries.size
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
assert_equal 1, doc.document_queries[0].url_functions.size
|
60
|
+
assert_equal CSSPool::Terms::URI, doc.document_queries[0].url_functions[0].class
|
61
|
+
assert_equal 'http://www.example.com', doc.document_queries[0].url_functions[0].value
|
62
|
+
assert_equal 1, doc.document_queries[0].rule_sets.size
|
63
63
|
end
|
64
64
|
|
65
65
|
def test_empty
|
66
66
|
doc = CSSPool.CSS <<-eocss
|
67
|
-
|
68
|
-
|
67
|
+
@document url("http://www.example.com") {
|
68
|
+
}
|
69
69
|
eocss
|
70
70
|
assert_equal 1, doc.document_queries.size
|
71
|
-
|
71
|
+
assert_equal true, doc.document_queries[0].rule_sets.empty?
|
72
72
|
end
|
73
|
-
|
74
73
|
end
|
75
74
|
end
|
76
75
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module CSSPool
|
4
|
+
module CSS
|
5
|
+
class TestFontFace < CSSPool::TestCase
|
6
|
+
|
7
|
+
def test_basic
|
8
|
+
doc = CSSPool.CSS <<-eocss
|
9
|
+
@font-face { font-weight: normal; }
|
10
|
+
eocss
|
11
|
+
assert_equal "@font-face {\n font-weight: normal;\n}", doc.to_css
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|