deadweight 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,46 +0,0 @@
1
- module CssParser
2
- # :stopdoc:
3
- # Base types
4
- RE_NL = Regexp.new('(\n|\r\n|\r|\f)')
5
- RE_NON_ASCII = Regexp.new('([\x00-\xFF])', Regexp::IGNORECASE, 'n') #[^\0-\177]
6
- RE_UNICODE = Regexp.new('(\\\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])*)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE, 'n')
7
- RE_ESCAPE = Regexp.union(RE_UNICODE, '|(\\\\[^\n\r\f0-9a-f])')
8
- RE_IDENT = Regexp.new("[\-]?([_a-z]|#{RE_NON_ASCII}|#{RE_ESCAPE})([_a-z0-9\-]|#{RE_NON_ASCII}|#{RE_ESCAPE})*", Regexp::IGNORECASE, 'n')
9
-
10
- # General strings
11
- RE_STRING1 = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
12
- RE_STRING2 = Regexp.new('(\'(.[^\n\r\f\\\']*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\')')
13
- RE_STRING = Regexp.union(RE_STRING1, RE_STRING2)
14
-
15
- 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')
16
- URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im
17
-
18
- # Initial parsing
19
- RE_AT_IMPORT_RULE = /\@import[\s]+(url\()?["''"]?(.[^'"\s"']*)["''"]?\)?([\w\s\,^\])]*)\)?;?/
20
-
21
- #--
22
- #RE_AT_MEDIA_RULE = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
23
-
24
- #RE_AT_IMPORT_RULE = Regexp.new('@import[\s]*(' + RE_STRING.to_s + ')([\w\s\,]*)[;]?', Regexp::IGNORECASE) -- should handle url() even though it is not allowed
25
- #++
26
- IMPORTANT_IN_PROPERTY_RX = /[\s]*\!important[\s]*/i
27
- STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
28
- STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
29
-
30
- # Special units
31
- BOX_MODEL_UNITS_RX = /(auto|inherit|0|([\-]*([0-9]+|[0-9]*\.[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%)))([\s;]|\Z)/imx
32
- RE_LENGTH_OR_PERCENTAGE = Regexp.new('([\-]*(([0-9]*\.[0-9]+)|[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%))', Regexp::IGNORECASE)
33
- RE_BACKGROUND_POSITION = Regexp.new("((#{RE_LENGTH_OR_PERCENTAGE})|left|center|right|top|bottom)", Regexp::IGNORECASE | Regexp::EXTENDED)
34
- FONT_UNITS_RX = /(([x]+\-)*small|medium|large[r]*|auto|inherit|([0-9]+|[0-9]*\.[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%)*)/i
35
-
36
- # Patterns for specificity calculations
37
- ELEMENTS_AND_PSEUDO_ELEMENTS_RX = /((^|[\s\+\>]+)[\w]+|\:(first\-line|first\-letter|before|after))/i
38
- NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = /(\.[\w]+)|(\[[\w]+)|(\:(link|first\-child|lang))/i
39
-
40
- # Colours
41
- RE_COLOUR_RGB = Regexp.new('(rgb[\s]*\([\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*\))', Regexp::IGNORECASE)
42
- RE_COLOUR_HEX = /(#([0-9a-f]{6}|[0-9a-f]{3})([\s;]|$))/i
43
- RE_COLOUR_NAMED = /([\s]*^)?(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|transparent)([\s]*$)?/i
44
- RE_COLOUR = Regexp.union(RE_COLOUR_RGB, RE_COLOUR_HEX, RE_COLOUR_NAMED)
45
- # :startdoc:
46
- end
@@ -1,394 +0,0 @@
1
- module CssParser
2
- class RuleSet
3
- # Patterns for specificity calculations
4
- RE_ELEMENTS_AND_PSEUDO_ELEMENTS = /((^|[\s\+\>]+)[\w]+|\:(first\-line|first\-letter|before|after))/i
5
- RE_NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES = /(\.[\w]+)|(\[[\w]+)|(\:(link|first\-child|lang))/i
6
-
7
- # Array of selector strings.
8
- attr_reader :selectors
9
-
10
- # Integer with the specificity to use for this RuleSet.
11
- attr_accessor :specificity
12
-
13
- def initialize(selectors, block, specificity = nil)
14
- @selectors = []
15
- @specificity = specificity
16
- @declarations = {}
17
- @order = 0
18
- parse_selectors!(selectors) if selectors
19
- parse_declarations!(block)
20
- end
21
-
22
-
23
- # Get the value of a property
24
- def get_value(property)
25
- return '' unless property and not property.empty?
26
-
27
- property = property.downcase.strip
28
- properties = @declarations.inject('') do |val, (key, data)|
29
- #puts "COMPARING #{key} #{key.inspect} against #{property} #{property.inspect}"
30
- importance = data[:is_important] ? ' !important' : ''
31
- val << "#{data[:value]}#{importance}; " if key.downcase.strip == property
32
- val
33
- end
34
- return properties ? properties.strip : ''
35
- end
36
- alias_method :[], :get_value
37
-
38
- # Add a CSS declaration to the current RuleSet.
39
- #
40
- # rule_set.add_declaration!('color', 'blue')
41
- #
42
- # puts rule_set['color']
43
- # => 'blue;'
44
- #
45
- # rule_set.add_declaration!('margin', '0px auto !important')
46
- #
47
- # puts rule_set['margin']
48
- # => '0px auto !important;'
49
- #
50
- # If the property already exists its value will be over-written.
51
- def add_declaration!(property, value)
52
- if value.nil? or value.empty?
53
- @declarations.delete(property)
54
- return
55
- end
56
-
57
- value.gsub!(/;\Z/, '')
58
- is_important = !value.gsub!(CssParser::IMPORTANT_IN_PROPERTY_RX, '').nil?
59
- property = property.downcase.strip
60
- #puts "SAVING #{property} #{value} #{is_important.inspect}"
61
- @declarations[property] = {
62
- :value => value, :is_important => is_important, :order => @order += 1
63
- }
64
- end
65
- alias_method :[]=, :add_declaration!
66
-
67
- # Iterate through selectors.
68
- #
69
- # Options
70
- # - +force_important+ -- boolean
71
- #
72
- # ==== Example
73
- # ruleset.each_selector do |sel, dec, spec|
74
- # ...
75
- # end
76
- def each_selector(options = {}) # :yields: selector, declarations, specificity
77
- declarations = declarations_to_s(options)
78
- if @specificity
79
- @selectors.each { |sel| yield sel.strip, declarations, @specificity }
80
- else
81
- @selectors.each { |sel| yield sel.strip, declarations, CssParser.calculate_specificity(sel) }
82
- end
83
- end
84
-
85
- # Iterate through declarations.
86
- def each_declaration # :yields: property, value, is_important
87
- decs = @declarations.sort { |a,b| a[1][:order].nil? || b[1][:order].nil? ? 0 : a[1][:order] <=> b[1][:order] }
88
- decs.each do |property, data|
89
- value = data[:value]
90
- yield property.downcase.strip, value.strip, data[:is_important]
91
- end
92
- end
93
-
94
- # Return all declarations as a string.
95
- #--
96
- # TODO: Clean-up regexp doesn't seem to work
97
- #++
98
- def declarations_to_s(options = {})
99
- options = {:force_important => false}.merge(options)
100
- str = ''
101
- each_declaration do |prop, val, is_important|
102
- importance = (options[:force_important] || is_important) ? ' !important' : ''
103
- str += "#{prop}: #{val}#{importance}; "
104
- end
105
- str.gsub(/^[\s]+|[\n\r\f\t]*|[\s]+$/mx, '').strip
106
- end
107
-
108
- # Return the CSS rule set as a string.
109
- def to_s
110
- decs = declarations_to_s
111
- "#{@selectors} { #{decs} }"
112
- end
113
-
114
- # Split shorthand declarations (e.g. +margin+ or +font+) into their constituent parts.
115
- def expand_shorthand!
116
- expand_dimensions_shorthand!
117
- expand_font_shorthand!
118
- expand_background_shorthand!
119
- end
120
-
121
- # Create shorthand declarations (e.g. +margin+ or +font+) whenever possible.
122
- def create_shorthand!
123
- create_background_shorthand!
124
- create_dimensions_shorthand!
125
- create_font_shorthand!
126
- end
127
-
128
- private
129
- def parse_declarations!(block) # :nodoc:
130
- @declarations = {}
131
-
132
- return unless block
133
-
134
- block.gsub!(/(^[\s]*)|([\s]*$)/, '')
135
-
136
- block.split(/[\;$]+/m).each do |decs|
137
- if matches = decs.match(/(.[^:]*)\:(.[^;]*)(;|\Z)/i)
138
- property, value, end_of_declaration = matches.captures
139
-
140
- add_declaration!(property, value)
141
- end
142
- end
143
- end
144
-
145
- #--
146
- # TODO: way too simplistic
147
- #++
148
- def parse_selectors!(selectors) # :nodoc:
149
- @selectors = selectors.split(',')
150
- end
151
-
152
- public
153
- # Split shorthand dimensional declarations (e.g. <tt>margin: 0px auto;</tt>)
154
- # into their constituent parts.
155
- def expand_dimensions_shorthand! # :nodoc:
156
- ['margin', 'padding'].each do |property|
157
-
158
- next unless @declarations.has_key?(property)
159
-
160
- value = @declarations[property][:value]
161
- is_important = @declarations[property][:is_important]
162
- order = @declarations[property][:order]
163
- t, r, b, l = nil
164
-
165
- matches = value.scan(CssParser::BOX_MODEL_UNITS_RX)
166
-
167
- case matches.length
168
- when 1
169
- t, r, b, l = matches[0][0], matches[0][0], matches[0][0], matches[0][0]
170
- when 2
171
- t, b = matches[0][0], matches[0][0]
172
- r, l = matches[1][0], matches[1][0]
173
- when 3
174
- t = matches[0][0]
175
- r, l = matches[1][0], matches[1][0]
176
- b = matches[2][0]
177
- when 4
178
- t = matches[0][0]
179
- r = matches[1][0]
180
- b = matches[2][0]
181
- l = matches[3][0]
182
- end
183
-
184
- values = { :is_important => is_important, :order => order }
185
- @declarations["#{property}-top"] = values.merge(:value => t.to_s)
186
- @declarations["#{property}-right"] = values.merge(:value => r.to_s)
187
- @declarations["#{property}-bottom"] = values.merge(:value => b.to_s)
188
- @declarations["#{property}-left"] = values.merge(:value => l.to_s)
189
- @declarations.delete(property)
190
- end
191
- end
192
-
193
- # Convert shorthand font declarations (e.g. <tt>font: 300 italic 11px/14px verdana, helvetica, sans-serif;</tt>)
194
- # into their constituent parts.
195
- def expand_font_shorthand! # :nodoc:
196
- return unless @declarations.has_key?('font')
197
-
198
- font_props = {}
199
-
200
- # reset properties to 'normal' per http://www.w3.org/TR/CSS21/fonts.html#font-shorthand
201
- ['font-style', 'font-variant', 'font-weight', 'font-size',
202
- 'line-height'].each do |prop|
203
- font_props[prop] = 'normal'
204
- end
205
-
206
- value = @declarations['font'][:value]
207
- is_important = @declarations['font'][:is_important]
208
- order = @declarations['font'][:order]
209
-
210
- in_fonts = false
211
-
212
- matches = value.scan(/("(.*[^"])"|'(.*[^'])'|(\w[^ ,]+))/)
213
- matches.each do |match|
214
- m = match[0].to_s.strip
215
- m.gsub!(/[;]$/, '')
216
-
217
- if in_fonts
218
- if font_props.has_key?('font-family')
219
- font_props['font-family'] += ', ' + m
220
- else
221
- font_props['font-family'] = m
222
- end
223
- elsif m =~ /normal|inherit/i
224
- ['font-style', 'font-weight', 'font-variant'].each do |font_prop|
225
- font_props[font_prop] = m unless font_props.has_key?(font_prop)
226
- end
227
- elsif m =~ /italic|oblique/i
228
- font_props['font-style'] = m
229
- elsif m =~ /small\-caps/i
230
- font_props['font-variant'] = m
231
- elsif m =~ /[1-9]00$|bold|bolder|lighter/i
232
- font_props['font-weight'] = m
233
- elsif m =~ CssParser::FONT_UNITS_RX
234
- if m =~ /\//
235
- font_props['font-size'], font_props['line-height'] = m.split('/')
236
- else
237
- font_props['font-size'] = m
238
- end
239
- in_fonts = true
240
- end
241
- end
242
-
243
- font_props.each { |font_prop, font_val| @declarations[font_prop] = {:value => font_val, :is_important => is_important, :order => order} }
244
-
245
- @declarations.delete('font')
246
- end
247
-
248
-
249
- # Convert shorthand background declarations (e.g. <tt>background: url("chess.png") gray 50% repeat fixed;</tt>)
250
- # into their constituent parts.
251
- #
252
- # See http://www.w3.org/TR/CSS21/colors.html#propdef-background
253
- def expand_background_shorthand! # :nodoc:
254
- return unless @declarations.has_key?('background')
255
-
256
- value = @declarations['background'][:value]
257
- is_important = @declarations['background'][:is_important]
258
- order = @declarations['background'][:order]
259
-
260
- bg_props = {}
261
-
262
-
263
- if m = value.match(Regexp.union(CssParser::URI_RX, /none/i)).to_s
264
- bg_props['background-image'] = m.strip unless m.empty?
265
- value.gsub!(Regexp.union(CssParser::URI_RX, /none/i), '')
266
- end
267
-
268
- if m = value.match(/([\s]*^)?(scroll|fixed)([\s]*$)?/i).to_s
269
- bg_props['background-attachment'] = m.strip unless m.empty?
270
- end
271
-
272
- if m = value.match(/([\s]*^)?(repeat(\-x|\-y)*|no\-repeat)([\s]*$)?/i).to_s
273
- bg_props['background-repeat'] = m.strip unless m.empty?
274
- end
275
-
276
- if m = value.match(CssParser::RE_COLOUR).to_s
277
- bg_props['background-color'] = m.strip unless m.empty?
278
- end
279
-
280
- value.scan(CssParser::RE_BACKGROUND_POSITION).each do |m|
281
- if bg_props.has_key?('background-position')
282
- bg_props['background-position'] += ' ' + m[0].to_s.strip unless m.empty?
283
- else
284
- bg_props['background-position'] = m[0].to_s.strip unless m.empty?
285
- end
286
- end
287
-
288
-
289
- if value =~ /([\s]*^)?inherit([\s]*$)?/i
290
- ['background-color', 'background-image', 'background-attachment', 'background-repeat', 'background-position'].each do |prop|
291
- bg_props["#{prop}"] = 'inherit' unless bg_props.has_key?(prop) and not bg_props[prop].empty?
292
- end
293
- end
294
-
295
- bg_props.each { |bg_prop, bg_val| @declarations[bg_prop] = {:value => bg_val, :is_important => is_important, :order => order} }
296
-
297
- @declarations.delete('background')
298
- end
299
-
300
-
301
- # Looks for long format CSS background properties (e.g. <tt>background-color</tt>) and
302
- # converts them into a shorthand CSS <tt>background</tt> property.
303
- #
304
- # Leaves properties declared !important alone.
305
- def create_background_shorthand! # :nodoc:
306
- new_value = ''
307
- ['background-color', 'background-image', 'background-repeat',
308
- 'background-position', 'background-attachment'].each do |property|
309
- if @declarations.has_key?(property) and not @declarations[property][:is_important]
310
- new_value += @declarations[property][:value] + ' '
311
- @declarations.delete(property)
312
- end
313
- end
314
-
315
- unless new_value.strip.empty?
316
- @declarations['background'] = {:value => new_value.gsub(/[\s]+/, ' ').strip}
317
- end
318
- end
319
-
320
- # Looks for long format CSS dimensional properties (i.e. <tt>margin</tt> and <tt>padding</tt>) and
321
- # converts them into shorthand CSS properties.
322
- def create_dimensions_shorthand! # :nodoc:
323
- # geometric
324
- directions = ['top', 'right', 'bottom', 'left']
325
- ['margin', 'padding'].each do |property|
326
- values = {}
327
-
328
- foldable = @declarations.select { |dim, val| dim == "#{property}-top" or dim == "#{property}-right" or dim == "#{property}-bottom" or dim == "#{property}-left" }
329
- # All four dimensions must be present
330
- if foldable.length == 4
331
- values = {}
332
-
333
- directions.each { |d| values[d.to_sym] = @declarations["#{property}-#{d}"][:value].downcase.strip }
334
-
335
- if values[:left] == values[:right]
336
- if values[:top] == values[:bottom]
337
- if values[:top] == values[:left] # All four sides are equal
338
- new_value = values[:top]
339
- else # Top and bottom are equal, left and right are equal
340
- new_value = values[:top] + ' ' + values[:left]
341
- end
342
- else # Only left and right are equal
343
- new_value = values[:top] + ' ' + values[:left] + ' ' + values[:bottom]
344
- end
345
- else # No sides are equal
346
- new_value = values[:top] + ' ' + values[:right] + ' ' + values[:bottom] + ' ' + values[:left]
347
- end # done creating 'new_value'
348
-
349
- # Save the new value
350
- unless new_value.strip.empty?
351
- @declarations[property] = {:value => new_value.gsub(/[\s]+/, ' ').strip}
352
- end
353
-
354
- # Delete the shorthand values
355
- directions.each { |d| @declarations.delete("#{property}-#{d}") }
356
- end
357
- end # done iterating through margin and padding
358
- end
359
-
360
-
361
- # Looks for long format CSS font properties (e.g. <tt>font-weight</tt>) and
362
- # tries to convert them into a shorthand CSS <tt>font</tt> property. All
363
- # font properties must be present in order to create a shorthand declaration.
364
- def create_font_shorthand! # :nodoc:
365
- ['font-style', 'font-variant', 'font-weight', 'font-size',
366
- 'line-height', 'font-family'].each do |prop|
367
- return unless @declarations.has_key?(prop)
368
- end
369
-
370
- new_value = ''
371
- ['font-style', 'font-variant', 'font-weight'].each do |property|
372
- unless @declarations[property][:value] == 'normal'
373
- new_value += @declarations[property][:value] + ' '
374
- end
375
- end
376
-
377
- new_value += @declarations['font-size'][:value]
378
-
379
- unless @declarations['line-height'][:value] == 'normal'
380
- new_value += '/' + @declarations['line-height'][:value]
381
- end
382
-
383
- new_value += ' ' + @declarations['font-family'][:value]
384
-
385
- @declarations['font'] = {:value => new_value.gsub(/[\s]+/, ' ').strip}
386
-
387
- ['font-style', 'font-variant', 'font-weight', 'font-size',
388
- 'line-height', 'font-family'].each do |prop|
389
- @declarations.delete(prop)
390
- end
391
-
392
- end
393
- end
394
- end
@@ -1,4 +0,0 @@
1
- @import "import-circular-reference.css";
2
-
3
- body { color: black; background: white; }
4
- p { margin: 0px; }
@@ -1,3 +0,0 @@
1
- @import "simple.css" print, tv, screen;
2
-
3
- div { color: lime; }