excession 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/excession/css_regex_hue_mod.rb +281 -0
- data/lib/excession/parser.rb +47 -0
- data/lib/excession/parser/css_parser.rb +391 -0
- data/lib/excession/version.rb +3 -0
- metadata +72 -0
@@ -0,0 +1,281 @@
|
|
1
|
+
module Excession
|
2
|
+
class CssRegexHueMod
|
3
|
+
|
4
|
+
HASHSIX = /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})(\s|;|})/i
|
5
|
+
HASHTHREE = /#([a-f0-9])([a-f0-9])([a-f0-9])(\s|;|})/i
|
6
|
+
RGBCALL = /rgb\(\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\)/i
|
7
|
+
RGBPCTCALL = /rgb\(\s*(-?[0-9]+)\s*%\s*,\s*(-?[0-9]+)\s*%\s*,\s*(-?[0-9]+)\s*%\s*\)/i
|
8
|
+
|
9
|
+
|
10
|
+
# Use this entry point.
|
11
|
+
def rotate_hue(angle_degrees, str)
|
12
|
+
replace_colours(str) do |hsl|
|
13
|
+
h,s,l = *hsl
|
14
|
+
[h+angle_degrees, s, l]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
def rgb_to_hsl(rgbint)
|
21
|
+
rgb = rgbint.map{|i| i/255.0}
|
22
|
+
maxcolour = rgb.max
|
23
|
+
mincolour = rgb.min
|
24
|
+
|
25
|
+
#puts "maxcolour: #{maxcolour}"
|
26
|
+
#puts "mincolour: #{mincolour}"
|
27
|
+
|
28
|
+
cdiff = (maxcolour-mincolour)
|
29
|
+
csum = (maxcolour + mincolour)
|
30
|
+
|
31
|
+
#puts "cdiff: #{cdiff}"
|
32
|
+
#puts "csum: #{csum}"
|
33
|
+
|
34
|
+
l = csum/2
|
35
|
+
#puts "l: #{l}"
|
36
|
+
h,s = 0,0
|
37
|
+
if maxcolour != mincolour
|
38
|
+
s = l < 0.5 ? cdiff/csum : cdiff/(2.0-csum)
|
39
|
+
#puts "s: #{s}"
|
40
|
+
r,g,b = rgb
|
41
|
+
h = if r == maxcolour
|
42
|
+
(g-b)/cdiff
|
43
|
+
elsif g == maxcolour
|
44
|
+
2.0+(b-r)/cdiff
|
45
|
+
else
|
46
|
+
4.0+(r-g)/cdiff
|
47
|
+
end
|
48
|
+
#puts "h: #{h}"
|
49
|
+
end
|
50
|
+
|
51
|
+
return [(h*360)%360,s,l]
|
52
|
+
end
|
53
|
+
|
54
|
+
def hsl_to_rgb(hsl)
|
55
|
+
hdeg,s,l = *hsl
|
56
|
+
|
57
|
+
rgb = nil
|
58
|
+
if s == 0
|
59
|
+
rgb = [l,l,l]
|
60
|
+
else
|
61
|
+
temp2 = l < 0.5 ? l*(1+s) : l+s-l*s
|
62
|
+
temp1 = 2.0*l-temp2
|
63
|
+
h = hdeg/360.0
|
64
|
+
temp3 = [h+1.0/3.0, h, h-1.0/3.0].map{|i| i < 0 ? i+1 : (i > 1 ? i-1 : i)}
|
65
|
+
rgb = temp3.map do |i|
|
66
|
+
if 6.0*i < 1
|
67
|
+
temp1 + (temp2-temp1)*6.0*i
|
68
|
+
elsif 2*i < 1
|
69
|
+
temp2
|
70
|
+
elsif 3*i < 2
|
71
|
+
temp1 + (temp2-temp1)*((2.0/3.0)-i)*6.0
|
72
|
+
else
|
73
|
+
temp1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
rgb.map{|i| (i*255).floor}
|
78
|
+
end
|
79
|
+
|
80
|
+
def rgb_to_hashsix(rgb)
|
81
|
+
"#" + rgb.map do |i|
|
82
|
+
str = i.to_s(16)
|
83
|
+
raise "Invalid colour (#{rgb.inspect})!" if str.length > 2
|
84
|
+
str="0"+str if str.length<2
|
85
|
+
str
|
86
|
+
end.join
|
87
|
+
end
|
88
|
+
|
89
|
+
def modify_hsl(hsl, &blk)
|
90
|
+
newhsl = blk.call(hsl)
|
91
|
+
newhsl[0] %= 360
|
92
|
+
newhsl
|
93
|
+
end
|
94
|
+
|
95
|
+
def modify_rgb(rgb, &blk)
|
96
|
+
rgb_to_hashsix(hsl_to_rgb(modify_hsl(rgb_to_hsl(rgb), &blk)))
|
97
|
+
end
|
98
|
+
|
99
|
+
def replace_colours(str, &blk)
|
100
|
+
|
101
|
+
str.gsub!(HASHSIX) do
|
102
|
+
hexr, hexg, hexb, tend = $1, $2, $3, $4
|
103
|
+
modify_rgb([hexr, hexg, hexb].map(&:hex), &blk)+tend
|
104
|
+
end
|
105
|
+
|
106
|
+
str.gsub!(HASHTHREE) do
|
107
|
+
hexr, hexg, hexb, tend = $1, $2, $3, $4
|
108
|
+
modify_rgb([hexr*2,hexg*2,hexb*2].map(&:hex), &blk)+tend
|
109
|
+
end
|
110
|
+
|
111
|
+
str.gsub!(RGBCALL) do
|
112
|
+
strr, strg, strb = $1, $2, $3
|
113
|
+
modify_rgb([strr, strg, strb].map{|s|Integer(s)}, &blk)
|
114
|
+
end
|
115
|
+
|
116
|
+
str.gsub!(RGBPCTCALL) do
|
117
|
+
strr, strg, strb = $1, $2, $3
|
118
|
+
modify_rgb([strr, strg, strb].
|
119
|
+
map{|s|Integer(s)}.
|
120
|
+
map{|i| (i/100.0) * 255 }, # Don't just multiply by 2.55, it's not accurate
|
121
|
+
&blk)
|
122
|
+
end
|
123
|
+
|
124
|
+
str
|
125
|
+
end
|
126
|
+
|
127
|
+
end # class CssRegexHueMod
|
128
|
+
end # module Excession
|
129
|
+
|
130
|
+
|
131
|
+
=begin
|
132
|
+
COLOUR_LOOKUP = {
|
133
|
+
"AliceBlue" =>"#F0F8FF",
|
134
|
+
"AntiqueWhite" =>"#FAEBD7",
|
135
|
+
"Aqua" =>"#00FFFF",
|
136
|
+
"Aquamarine" =>"#7FFFD4",
|
137
|
+
"Azure" =>"#F0FFFF",
|
138
|
+
"Beige" =>"#F5F5DC",
|
139
|
+
"Bisque" =>"#FFE4C4",
|
140
|
+
"Black" =>"#000000",
|
141
|
+
"BlanchedAlmond" =>"#FFEBCD",
|
142
|
+
"Blue" =>"#0000FF",
|
143
|
+
"BlueViolet" =>"#8A2BE2",
|
144
|
+
"Brown" =>"#A52A2A",
|
145
|
+
"BurlyWood" =>"#DEB887",
|
146
|
+
"CadetBlue" =>"#5F9EA0",
|
147
|
+
"Chartreuse" =>"#7FFF00",
|
148
|
+
"Chocolate" =>"#D2691E",
|
149
|
+
"Coral" =>"#FF7F50",
|
150
|
+
"CornflowerBlue" =>"#6495ED",
|
151
|
+
"Cornsilk" =>"#FFF8DC",
|
152
|
+
"Crimson" =>"#DC143C",
|
153
|
+
"Cyan" =>"#00FFFF",
|
154
|
+
"DarkBlue" =>"#00008B",
|
155
|
+
"DarkCyan" =>"#008B8B",
|
156
|
+
"DarkGoldenRod" =>"#B8860B",
|
157
|
+
"DarkGray" =>"#A9A9A9",
|
158
|
+
"DarkGrey" =>"#A9A9A9",
|
159
|
+
"DarkGreen" =>"#006400",
|
160
|
+
"DarkKhaki" =>"#BDB76B",
|
161
|
+
"DarkMagenta" =>"#8B008B",
|
162
|
+
"DarkOliveGreen" =>"#556B2F",
|
163
|
+
"Darkorange" =>"#FF8C00",
|
164
|
+
"DarkOrchid" =>"#9932CC",
|
165
|
+
"DarkRed" =>"#8B0000",
|
166
|
+
"DarkSalmon" =>"#E9967A",
|
167
|
+
"DarkSeaGreen" =>"#8FBC8F",
|
168
|
+
"DarkSlateBlue" =>"#483D8B",
|
169
|
+
"DarkSlateGray" =>"#2F4F4F",
|
170
|
+
"DarkSlateGrey" =>"#2F4F4F",
|
171
|
+
"DarkTurquoise" =>"#00CED1",
|
172
|
+
"DarkViolet" =>"#9400D3",
|
173
|
+
"DeepPink" =>"#FF1493",
|
174
|
+
"DeepSkyBlue" =>"#00BFFF",
|
175
|
+
"DimGray" =>"#696969",
|
176
|
+
"DimGrey" =>"#696969",
|
177
|
+
"DodgerBlue" =>"#1E90FF",
|
178
|
+
"FireBrick" =>"#B22222",
|
179
|
+
"FloralWhite" =>"#FFFAF0",
|
180
|
+
"ForestGreen" =>"#228B22",
|
181
|
+
"Fuchsia" =>"#FF00FF",
|
182
|
+
"Gainsboro" =>"#DCDCDC",
|
183
|
+
"GhostWhite" =>"#F8F8FF",
|
184
|
+
"Gold" =>"#FFD700",
|
185
|
+
"GoldenRod" =>"#DAA520",
|
186
|
+
"Gray" =>"#808080",
|
187
|
+
"Grey" =>"#808080",
|
188
|
+
"Green" =>"#008000",
|
189
|
+
"GreenYellow" =>"#ADFF2F",
|
190
|
+
"HoneyDew" =>"#F0FFF0",
|
191
|
+
"HotPink" =>"#FF69B4",
|
192
|
+
"IndianRed" =>"#CD5C5C",
|
193
|
+
"Indigo" =>"#4B0082",
|
194
|
+
"Ivory" =>"#FFFFF0",
|
195
|
+
"Khaki" =>"#F0E68C",
|
196
|
+
"Lavender" =>"#E6E6FA",
|
197
|
+
"LavenderBlush" =>"#FFF0F5",
|
198
|
+
"LawnGreen" =>"#7CFC00",
|
199
|
+
"LemonChiffon" =>"#FFFACD",
|
200
|
+
"LightBlue" =>"#ADD8E6",
|
201
|
+
"LightCoral" =>"#F08080",
|
202
|
+
"LightCyan" =>"#E0FFFF",
|
203
|
+
"LightGoldenRodYellow" =>"#FAFAD2",
|
204
|
+
"LightGray" =>"#D3D3D3",
|
205
|
+
"LightGrey" =>"#D3D3D3",
|
206
|
+
"LightGreen" =>"#90EE90",
|
207
|
+
"LightPink" =>"#FFB6C1",
|
208
|
+
"LightSalmon" =>"#FFA07A",
|
209
|
+
"LightSeaGreen" =>"#20B2AA",
|
210
|
+
"LightSkyBlue" =>"#87CEFA",
|
211
|
+
"LightSlateGray" =>"#778899",
|
212
|
+
"LightSlateGrey" =>"#778899",
|
213
|
+
"LightSteelBlue" =>"#B0C4DE",
|
214
|
+
"LightYellow" =>"#FFFFE0",
|
215
|
+
"Lime" =>"#00FF00",
|
216
|
+
"LimeGreen" =>"#32CD32",
|
217
|
+
"Linen" =>"#FAF0E6",
|
218
|
+
"Magenta" =>"#FF00FF",
|
219
|
+
"Maroon" =>"#800000",
|
220
|
+
"MediumAquaMarine" =>"#66CDAA",
|
221
|
+
"MediumBlue" =>"#0000CD",
|
222
|
+
"MediumOrchid" =>"#BA55D3",
|
223
|
+
"MediumPurple" =>"#9370D8",
|
224
|
+
"MediumSeaGreen" =>"#3CB371",
|
225
|
+
"MediumSlateBlue" =>"#7B68EE",
|
226
|
+
"MediumSpringGreen" =>"#00FA9A",
|
227
|
+
"MediumTurquoise" =>"#48D1CC",
|
228
|
+
"MediumVioletRed" =>"#C71585",
|
229
|
+
"MidnightBlue" =>"#191970",
|
230
|
+
"MintCream" =>"#F5FFFA",
|
231
|
+
"MistyRose" =>"#FFE4E1",
|
232
|
+
"Moccasin" =>"#FFE4B5",
|
233
|
+
"NavajoWhite" =>"#FFDEAD",
|
234
|
+
"Navy" =>"#000080",
|
235
|
+
"OldLace" =>"#FDF5E6",
|
236
|
+
"Olive" =>"#808000",
|
237
|
+
"OliveDrab" =>"#6B8E23",
|
238
|
+
"Orange" =>"#FFA500",
|
239
|
+
"OrangeRed" =>"#FF4500",
|
240
|
+
"Orchid" =>"#DA70D6",
|
241
|
+
"PaleGoldenRod" =>"#EEE8AA",
|
242
|
+
"PaleGreen" =>"#98FB98",
|
243
|
+
"PaleTurquoise" =>"#AFEEEE",
|
244
|
+
"PaleVioletRed" =>"#D87093",
|
245
|
+
"PapayaWhip" =>"#FFEFD5",
|
246
|
+
"PeachPuff" =>"#FFDAB9",
|
247
|
+
"Peru" =>"#CD853F",
|
248
|
+
"Pink" =>"#FFC0CB",
|
249
|
+
"Plum" =>"#DDA0DD",
|
250
|
+
"PowderBlue" =>"#B0E0E6",
|
251
|
+
"Purple" =>"#800080",
|
252
|
+
"Red" =>"#FF0000",
|
253
|
+
"RosyBrown" =>"#BC8F8F",
|
254
|
+
"RoyalBlue" =>"#4169E1",
|
255
|
+
"SaddleBrown" =>"#8B4513",
|
256
|
+
"Salmon" =>"#FA8072",
|
257
|
+
"SandyBrown" =>"#F4A460",
|
258
|
+
"SeaGreen" =>"#2E8B57",
|
259
|
+
"SeaShell" =>"#FFF5EE",
|
260
|
+
"Sienna" =>"#A0522D",
|
261
|
+
"Silver" =>"#C0C0C0",
|
262
|
+
"SkyBlue" =>"#87CEEB",
|
263
|
+
"SlateBlue" =>"#6A5ACD",
|
264
|
+
"SlateGray" =>"#708090",
|
265
|
+
"SlateGrey" =>"#708090",
|
266
|
+
"Snow" =>"#FFFAFA",
|
267
|
+
"SpringGreen" =>"#00FF7F",
|
268
|
+
"SteelBlue" =>"#4682B4",
|
269
|
+
"Tan" =>"#D2B48C",
|
270
|
+
"Teal" =>"#008080",
|
271
|
+
"Thistle" =>"#D8BFD8",
|
272
|
+
"Tomato" =>"#FF6347",
|
273
|
+
"Turquoise" =>"#40E0D0",
|
274
|
+
"Violet" =>"#EE82EE",
|
275
|
+
"Wheat" =>"#F5DEB3",
|
276
|
+
"White" =>"#FFFFFF",
|
277
|
+
"WhiteSmoke" =>"#F5F5F5",
|
278
|
+
"Yellow" =>"#FFFF00",
|
279
|
+
"YellowGreen" =>"#9ACD32"
|
280
|
+
}
|
281
|
+
=end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Excession
|
4
|
+
|
5
|
+
module Parser
|
6
|
+
|
7
|
+
def self.parse(str)
|
8
|
+
CssTransform.new.apply(CssParser.new.parse(str))
|
9
|
+
end # def self.parse
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class CssTransform < Parslet::Transform
|
14
|
+
rule(:comment => simple(:content)){CommentNode.new(content)}
|
15
|
+
rule(:whitespace => simple(:content)){WhitespaceNode.new(content)}
|
16
|
+
rule(:selectors => simple(:selectors)){SelectorsNode.new(selectors)}
|
17
|
+
rule(:ruleset => simple(:ruleset)){RulesetNode.new(ruleset)}
|
18
|
+
rule(:children => sequence(:children)){CssFile.new(children)}
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class CssFile < Struct.new(:children)
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
class CommentNode < Struct.new(:content)
|
28
|
+
end
|
29
|
+
|
30
|
+
class WhitespaceNode < Struct.new(:content)
|
31
|
+
end
|
32
|
+
|
33
|
+
class SelectorsNode < Struct.new(:selectors)
|
34
|
+
end
|
35
|
+
|
36
|
+
class RulesetNode < Struct.new(:selectors, :children)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end # class Parser
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
end # module Excession
|
@@ -0,0 +1,391 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'parslet'
|
3
|
+
|
4
|
+
module Excession
|
5
|
+
|
6
|
+
module Parser
|
7
|
+
|
8
|
+
|
9
|
+
class CssParser < Parslet::Parser
|
10
|
+
def initialize(strict=false)
|
11
|
+
@strict=strict
|
12
|
+
super()
|
13
|
+
end
|
14
|
+
|
15
|
+
rule(:h){ match["0-9a-fA-F"] }
|
16
|
+
rule(:nonascii){ match["\200-\377".force_encoding("BINARY")] }
|
17
|
+
rule(:unicode){
|
18
|
+
str("\\") >> h.repeat(1,6) >> (str("\r\n") | match[" \t\r\n\f"]).maybe
|
19
|
+
}
|
20
|
+
rule(:escape){
|
21
|
+
(unicode | (str("\\\\") >> match["\r\n\f0-9a-fA-F"].absnt? >> any))
|
22
|
+
}
|
23
|
+
rule(:nmstart){ match["_a-zA-Z"] | nonascii | escape }
|
24
|
+
rule(:nmchar){ match["_a-zA-Z0-9-"] | nonascii | escape }
|
25
|
+
rule(:nl){ str("\n") | str("\r\n") | str("\r") | str("\f") }
|
26
|
+
rule(:dqstring_contents){
|
27
|
+
(
|
28
|
+
(match["\n\r\f\""].absnt? >> any) |
|
29
|
+
(str("\\\\") >> nl) |
|
30
|
+
escape
|
31
|
+
).repeat
|
32
|
+
}
|
33
|
+
rule(:sqstring_contents){
|
34
|
+
(
|
35
|
+
(match["\n\r\f'"].absnt? >> any) |
|
36
|
+
(str("\\\\") >> nl) |
|
37
|
+
escape
|
38
|
+
).repeat
|
39
|
+
}
|
40
|
+
rule(:string1){ str('"') >> dqstring_contents >> str('"') }
|
41
|
+
rule(:string2){ str("'") >> sqstring_contents >> str("'") }
|
42
|
+
rule(:badstring1){ str('"') >> dqstring_contents >> str("\\").maybe }
|
43
|
+
rule(:badstring2){ str("'") >> sqstring_contents >> str("\\").maybe }
|
44
|
+
rule(:badcomment1){
|
45
|
+
str("/*") >>
|
46
|
+
(match["*"].absnt? >> any).repeat >>
|
47
|
+
str("*").repeat(1) >>
|
48
|
+
(
|
49
|
+
match["/*"].absnt? >> any >>
|
50
|
+
(match["*"].absnt? >> any).repeat >>
|
51
|
+
str("*").repeat(1)
|
52
|
+
).repeat
|
53
|
+
}
|
54
|
+
rule(:badcomment2){
|
55
|
+
str("/*") >>
|
56
|
+
(match["*"].absnt? >> any).repeat >>
|
57
|
+
(
|
58
|
+
str("*").repeat(1) >>
|
59
|
+
match["/*"].absnt? >> any >>
|
60
|
+
(match["*"].absnt? >> any).repeat
|
61
|
+
).repeat
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
# These are used to get the JS in an expression() declaration.
|
66
|
+
# Since we don't care much for parsing JS here, I'm just using
|
67
|
+
# the rule that valid JS must have balanced parens, and ignore
|
68
|
+
# unbalanced parens in comments and strings. For now.
|
69
|
+
# Thanks, IE.
|
70
|
+
rule(:js_chars){ (match["()"].absnt? >> any).repeat(1) }
|
71
|
+
rule(:js_bracketed){ str("(") >> js_balancing.maybe >> str(")") }
|
72
|
+
rule(:js_balancing){ ( js_chars | js_bracketed ).repeat(1) }
|
73
|
+
|
74
|
+
|
75
|
+
rule(:s){ match[" \t\r\n\f"].repeat(1) }
|
76
|
+
rule(:w){ s.repeat(0) }
|
77
|
+
|
78
|
+
rule(:baduri1){
|
79
|
+
str("url(") >> w >>
|
80
|
+
(
|
81
|
+
match['!#$%&*-\[\]-~'] |
|
82
|
+
nonascii |
|
83
|
+
escape
|
84
|
+
).repeat >>
|
85
|
+
w
|
86
|
+
}
|
87
|
+
|
88
|
+
rule(:string){ string1 | string2 }
|
89
|
+
|
90
|
+
rule(:baduri2){ str("url(") >> w >> string >> w }
|
91
|
+
|
92
|
+
rule(:badstring){ badstring1 | badstring2 }
|
93
|
+
rule(:baduri3){ str("url(") >> w >> badstring }
|
94
|
+
|
95
|
+
|
96
|
+
rule(:comment){
|
97
|
+
str("/*") >>
|
98
|
+
(
|
99
|
+
match["*"].absnt? >> any
|
100
|
+
).repeat >>
|
101
|
+
str("*").repeat(1) >>
|
102
|
+
(
|
103
|
+
match["*/"].absnt? >> any >>
|
104
|
+
(match["*"].absnt? >> any).repeat >>
|
105
|
+
str("*").repeat(1)
|
106
|
+
).repeat >>
|
107
|
+
str("/")
|
108
|
+
}
|
109
|
+
|
110
|
+
|
111
|
+
rule(:ident){
|
112
|
+
str("-").maybe >> nmstart >> nmchar.repeat
|
113
|
+
}
|
114
|
+
|
115
|
+
rule(:name){ nmchar.repeat(1) }
|
116
|
+
rule(:num){
|
117
|
+
(
|
118
|
+
match["0-9"].repeat(0) >>
|
119
|
+
str(".") >>
|
120
|
+
match["0-9"].repeat(1)
|
121
|
+
) |
|
122
|
+
match["0-9"].repeat(1)
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
rule(:badcomment){ badcomment1 | badcomment2 }
|
127
|
+
|
128
|
+
rule(:baduri){ baduri2 | baduri3 | baduri1 }
|
129
|
+
|
130
|
+
rule(:url){
|
131
|
+
(
|
132
|
+
match['!#$%&*-~'] |
|
133
|
+
nonascii |
|
134
|
+
escape
|
135
|
+
).repeat
|
136
|
+
}
|
137
|
+
|
138
|
+
# Case insensitive character match with escaped character codes.
|
139
|
+
#
|
140
|
+
# TODO: The CSS2.1 spec allows all letters apart from
|
141
|
+
# a,c,d and e to be specified like "\g". This is not supported
|
142
|
+
# here.
|
143
|
+
def cimatch(char)
|
144
|
+
upper = char.upcase.force_encoding("BINARY")
|
145
|
+
lower = char.downcase.force_encoding("BINARY")
|
146
|
+
return match["#{upper}#{lower}"] |
|
147
|
+
( str("\\") >>
|
148
|
+
str("0").repeat(0,4) >>
|
149
|
+
(str(upper[0].ord.to_s(16)) | str(lower[0].ord.to_s(16))) >>
|
150
|
+
(str("\r\n") | match[" \t\r\n\f"]).maybe )
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
def cimatch_word(word)
|
155
|
+
word.each_char.map{|c| self.cimatch(c)}.reduce(:>>)
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
rule(:sym_comment){ comment.as(:comment) }
|
160
|
+
# NB: Deviation. Comments are ignored in the 2.1 grammar, but I
|
161
|
+
# want to preserve them. I've made the S symbol cover comments
|
162
|
+
# as well.
|
163
|
+
rule(:sym_s){ sym_comment | s.as(:s) }
|
164
|
+
rule(:sym_s_star){ sym_s.repeat }
|
165
|
+
rule(:sym_badcomment){ badcomment.as(:badcomment) }
|
166
|
+
rule(:sym_cdo){ str("<!--").as(:cdo) }
|
167
|
+
rule(:sym_cdc){ str("-->").as(:cdc) }
|
168
|
+
rule(:sym_includes){ str("~=").as(:includes) }
|
169
|
+
rule(:sym_dashmatch){ str("|=").as(:dashmatch) }
|
170
|
+
rule(:sym_string){ string.as(:string) }
|
171
|
+
rule(:sym_badstring){ badstring.as(:badstring) }
|
172
|
+
rule(:sym_ident){ ident.as(:ident) }
|
173
|
+
|
174
|
+
rule(:sym_hash){ (str("#") >> name).as(:hash) }
|
175
|
+
|
176
|
+
rule(:sym_import_sym){
|
177
|
+
(str("@") >> cimatch_word("import")).as(:import_sym)
|
178
|
+
}
|
179
|
+
rule(:sym_page_sym){
|
180
|
+
(str("@") >> cimatch_word("page")).as(:page_sym)
|
181
|
+
}
|
182
|
+
rule(:sym_media_sym){
|
183
|
+
(str("@") >> cimatch_word("media")).as(:media_sym)
|
184
|
+
}
|
185
|
+
|
186
|
+
rule(:sym_charset_sym){
|
187
|
+
str("@charset ").as(:charset_sym)
|
188
|
+
}
|
189
|
+
|
190
|
+
rule(:sym_important_sym){
|
191
|
+
# NB deviation: s is a w in the spec
|
192
|
+
(str("!") >>
|
193
|
+
(comment | s).repeat >>
|
194
|
+
cimatch_word("important")
|
195
|
+
).as(:important_sym)
|
196
|
+
}
|
197
|
+
|
198
|
+
|
199
|
+
rule(:sym_ems){
|
200
|
+
(num.as(:num) >> cimatch_word("em").as(:unit)).as(:ems)
|
201
|
+
}
|
202
|
+
rule(:sym_exs){
|
203
|
+
(num.as(:num) >> cimatch_word("ex").as(:unit)).as(:exs)
|
204
|
+
}
|
205
|
+
rule(:sym_length){
|
206
|
+
units = %w{px cm mm in pt pc}
|
207
|
+
unit_matcher = units.map{|u| cimatch_word(u)}.reduce(:|)
|
208
|
+
|
209
|
+
(num.as(:num) >> unit_matcher.as(:unit)).as(:length)
|
210
|
+
}
|
211
|
+
rule(:sym_angle){
|
212
|
+
units = %w{deg rad grad}
|
213
|
+
unit_matcher = units.map{|u| cimatch_word(u)}.reduce(:|)
|
214
|
+
|
215
|
+
(num.as(:num) >> unit_matcher.as(:unit)).as(:angle)
|
216
|
+
}
|
217
|
+
rule(:sym_time){
|
218
|
+
units = %w{ms s}
|
219
|
+
unit_matcher = units.map{|u| cimatch_word(u)}.reduce(:|)
|
220
|
+
|
221
|
+
(num.as(:num) >> unit_matcher.as(:unit)).as(:time)
|
222
|
+
}
|
223
|
+
rule(:sym_freq){
|
224
|
+
units = %w{hz khz}
|
225
|
+
unit_matcher = units.map{|u| cimatch_word(u)}.reduce(:|)
|
226
|
+
|
227
|
+
(num.as(:num) >> unit_matcher.as(:unit)).as(:freq)
|
228
|
+
}
|
229
|
+
rule(:sym_dimension){
|
230
|
+
(num.as(:num) >> ident.as(:unit)).as(:dimension)
|
231
|
+
}
|
232
|
+
rule(:sym_percentage){
|
233
|
+
(num.as(:num) >> str("%").as(:unit)).as(:percentage)
|
234
|
+
}
|
235
|
+
rule(:sym_number){ (num.as(:num)).as(:number) }
|
236
|
+
rule(:sym_uri){
|
237
|
+
(cimatch_word("url") >> str("(") >>
|
238
|
+
(string.as(:string) | url.as(:url)) >>
|
239
|
+
str(")")).as(:uri)
|
240
|
+
}
|
241
|
+
rule(:sym_js){ js_balancing }
|
242
|
+
|
243
|
+
rule(:sym_baduri){ baduri.as(:baduri) }
|
244
|
+
|
245
|
+
rule(:sym_function){ (ident.as(:name) >> str("(")).as(:function) }
|
246
|
+
rule(:sym_expression){ cimatch_word("expression") >> str("(") }
|
247
|
+
|
248
|
+
|
249
|
+
|
250
|
+
rule(:prod_hexcolor){ sym_hash >> sym_s_star }
|
251
|
+
|
252
|
+
rule(:prod_operator){ match["/,"] >> sym_s_star }
|
253
|
+
rule(:prod_unary_operator){ match["-+"] }
|
254
|
+
rule(:prod_combinator){ match["+>"] >> sym_s_star }
|
255
|
+
|
256
|
+
rule(:prod_function){
|
257
|
+
sym_function >> sym_s_star >> prod_function_arglist >> str(")") >> sym_s_star
|
258
|
+
}
|
259
|
+
|
260
|
+
rule(:prod_expression){
|
261
|
+
sym_expression >> sym_s_star >> sym_js.as(:js) >> str(")") >> sym_s_star
|
262
|
+
}
|
263
|
+
|
264
|
+
rule(:prod_maybe_named_arg){
|
265
|
+
((sym_ident >> str("=")).maybe >> prod_term)
|
266
|
+
}
|
267
|
+
|
268
|
+
rule(:prod_function_arglist){
|
269
|
+
if @strict
|
270
|
+
prod_expr
|
271
|
+
else
|
272
|
+
# This works around IE, which allows things like:
|
273
|
+
# filter: alpha(opacity=90)
|
274
|
+
prod_maybe_named_arg >>
|
275
|
+
( prod_operator.maybe >>
|
276
|
+
prod_maybe_named_arg ).repeat
|
277
|
+
end
|
278
|
+
}
|
279
|
+
|
280
|
+
rule(:prod_term){
|
281
|
+
# expression() is a deprecated IE extension which allows arbitrary
|
282
|
+
# JS expressions (!) to be used for property values. This was a STUPID
|
283
|
+
# IDEA, but it's still used to give IE6 approximately modern functionality.
|
284
|
+
( @strict ? prod_function : (prod_expression | prod_function) ) |
|
285
|
+
( prod_unary_operator.maybe >>
|
286
|
+
( sym_percentage | sym_length | sym_ems | sym_exs | sym_angle |
|
287
|
+
sym_time | sym_freq | sym_number ) >>
|
288
|
+
sym_s_star ) |
|
289
|
+
( ( sym_string | sym_uri | sym_ident ) >> sym_s_star ) |
|
290
|
+
prod_hexcolor
|
291
|
+
}
|
292
|
+
|
293
|
+
|
294
|
+
rule(:prod_expr){
|
295
|
+
prod_term >> ( prod_operator.maybe >> prod_term ).repeat
|
296
|
+
}
|
297
|
+
|
298
|
+
rule(:prod_prio){ sym_important_sym >> sym_s_star }
|
299
|
+
rule(:prod_property){ sym_ident >> sym_s_star }
|
300
|
+
rule(:prod_declaration){
|
301
|
+
base = ( prod_property.as(:property) >> str(":") >>
|
302
|
+
sym_s_star >> prod_expr.as(:value) >> prod_prio.maybe )
|
303
|
+
# Only old IE versions need to pay attention to this. Or rather,
|
304
|
+
# this is used to flag declarations as only applying to old IE versions,
|
305
|
+
# because they don't correctly ignore the declaration.
|
306
|
+
@strict ? base : ( str("*").maybe.as(:star) >> base )
|
307
|
+
}
|
308
|
+
|
309
|
+
rule(:prod_pseudo){
|
310
|
+
str(":") >> ( ( sym_function >> sym_s_star >>
|
311
|
+
( sym_ident >> sym_s_star ).maybe >> str(")") ) |
|
312
|
+
sym_ident)
|
313
|
+
}
|
314
|
+
|
315
|
+
rule(:prod_attrib){
|
316
|
+
str("[") >> sym_s_star >> sym_ident.as(:key) >> sym_s_star >>
|
317
|
+
(
|
318
|
+
( sym_includes | sym_dashmatch | str("=") ) >> sym_s_star >>
|
319
|
+
( sym_string | sym_ident ).as(:value) >> sym_s_star
|
320
|
+
).maybe >>
|
321
|
+
str("]")
|
322
|
+
}
|
323
|
+
|
324
|
+
rule(:prod_element_name){ sym_ident | str("*") }
|
325
|
+
rule(:prod_class){ str(".") >> sym_ident }
|
326
|
+
|
327
|
+
rule(:prod_simple_selector){
|
328
|
+
( prod_element_name >>
|
329
|
+
( sym_hash | prod_class | prod_attrib | prod_pseudo ).repeat ) |
|
330
|
+
( sym_hash | prod_class | prod_attrib | prod_pseudo ).repeat(1)
|
331
|
+
}
|
332
|
+
|
333
|
+
rule(:prod_selector){
|
334
|
+
prod_simple_selector.as(:parent) >>
|
335
|
+
( ( prod_combinator >> prod_selector.as(:child) ) |
|
336
|
+
( sym_s.repeat(1) >> ( prod_combinator.maybe >> prod_selector.as(:child) ).maybe )
|
337
|
+
).maybe
|
338
|
+
}
|
339
|
+
|
340
|
+
rule(:prod_ruleset){
|
341
|
+
prod_selector >>
|
342
|
+
( str(",") >> sym_s_star >> prod_selector ).repeat >>
|
343
|
+
str("{") >> sym_s_star >>
|
344
|
+
prod_declaration.maybe >>
|
345
|
+
( str(";") >> sym_s_star >> prod_declaration.maybe
|
346
|
+
).repeat >>
|
347
|
+
str("}") >> sym_s_star
|
348
|
+
}
|
349
|
+
|
350
|
+
rule(:prod_pseudo_page){ str(":") >> sym_ident >> sym_s_star }
|
351
|
+
rule(:prod_page){ sym_page_sym >> sym_s_star >>
|
352
|
+
prod_pseudo_page.maybe >> str("{") >> sym_s_star >>
|
353
|
+
prod_declaration.maybe >>
|
354
|
+
( str(";") >> sym_s_star >> prod_declaration.maybe
|
355
|
+
).repeat >>
|
356
|
+
str("}") >> sym_s_star
|
357
|
+
}
|
358
|
+
|
359
|
+
rule(:prod_medium){ sym_ident >> sym_s_star }
|
360
|
+
rule(:prod_media_list){
|
361
|
+
# NB str(",") is for the missing COMMA symbol from the grammar
|
362
|
+
prod_medium >> (str(",") >> sym_s_star >> prod_medium).repeat
|
363
|
+
}
|
364
|
+
rule(:prod_media){
|
365
|
+
sym_media_sym >> sym_s_star >> prod_media_list >> str("{") >> sym_s_star >>
|
366
|
+
prod_ruleset.repeat >>
|
367
|
+
str("}") >> sym_s_star
|
368
|
+
}
|
369
|
+
|
370
|
+
rule(:prod_import){
|
371
|
+
sym_import_sym >> sym_s_star >> (sym_string | sym_uri) >> sym_s_star >>
|
372
|
+
prod_media_list.maybe >>
|
373
|
+
str(";") >> sym_s_star
|
374
|
+
}
|
375
|
+
|
376
|
+
rule(:prod_stylesheet){
|
377
|
+
( sym_charset_sym >> sym_string >> str(";") ).maybe >>
|
378
|
+
( sym_s | sym_cdo | sym_cdc ).repeat >>
|
379
|
+
( prod_import >>
|
380
|
+
( ( sym_cdo >> sym_s_star ) | (sym_cdc >> sym_s_star ) ).repeat ).repeat >>
|
381
|
+
( ( prod_ruleset | prod_media | prod_page ) >>
|
382
|
+
( ( sym_cdo >> sym_s_star ) | (sym_cdc >> sym_s_star ) ).repeat ).repeat
|
383
|
+
}
|
384
|
+
|
385
|
+
|
386
|
+
root(:prod_stylesheet)
|
387
|
+
end # class CssLexer
|
388
|
+
|
389
|
+
|
390
|
+
end # module Parser
|
391
|
+
end # module Excession
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: excession
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Alex Young
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-20 00:00:00 +00:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: |
|
22
|
+
Excession is a collection of code for parsing and otherwise
|
23
|
+
munging CSS files. It has sprung from a need to be able to
|
24
|
+
programmatically modify colours in a site's style sheets.
|
25
|
+
|
26
|
+
email:
|
27
|
+
- alex@blackkettle.org
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- lib/excession/parser.rb
|
36
|
+
- lib/excession/version.rb
|
37
|
+
- lib/excession/css_regex_hue_mod.rb
|
38
|
+
- lib/excession/parser/css_parser.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/regularfry/excession
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 1
|
61
|
+
- 3
|
62
|
+
- 6
|
63
|
+
version: 1.3.6
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.6
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Useful code for handling CSS files
|
71
|
+
test_files: []
|
72
|
+
|