deadweight 0.2.1 → 0.2.2

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.
@@ -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; }