csspool 4.0.0.pre → 4.0.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.
- 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
|