css_parser 0.9.1 → 1.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.
@@ -1,6 +1,6 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
  require 'uri'
3
- require 'md5'
3
+ require 'digest/md5'
4
4
  require 'zlib'
5
5
  require 'iconv'
6
6
  require 'css_parser/rule_set'
@@ -15,7 +15,7 @@ module CssParser
15
15
  # [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
16
16
  # [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
17
17
  class Parser
18
- USER_AGENT = "Ruby CSS Parser/#{VERSION} (http://code.dunae.ca/css_parser/)"
18
+ USER_AGENT = "Ruby CSS Parser/#{RUBY_VERSION} (http://code.dunae.ca/css_parser/)"
19
19
 
20
20
  STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
21
21
  STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
@@ -2,17 +2,17 @@ module CssParser
2
2
  # :stopdoc:
3
3
  # Base types
4
4
  RE_NL = Regexp.new('(\n|\r\n|\r|\f)')
5
- RE_NON_ASCII = Regexp.new('([\x00-\xFF])', Regexp::IGNORECASE) #[^\0-\177]
6
- RE_UNICODE = Regexp.new('(\\\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])*)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE)
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
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)
8
+ RE_IDENT = Regexp.new("[\-]?([_a-z]|#{RE_NON_ASCII}|#{RE_ESCAPE})([_a-z0-9\-]|#{RE_NON_ASCII}|#{RE_ESCAPE})*", Regexp::IGNORECASE, 'n')
9
9
 
10
10
  # General strings
11
11
  RE_STRING1 = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
12
12
  RE_STRING2 = Regexp.new('(\'(.[^\n\r\f\\\']*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\')')
13
13
  RE_STRING = Regexp.union(RE_STRING1, RE_STRING2)
14
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)
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
16
  URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im
17
17
 
18
18
  # Initial parsing
@@ -14,6 +14,7 @@ module CssParser
14
14
  @selectors = []
15
15
  @specificity = specificity
16
16
  @declarations = {}
17
+ @order = 0
17
18
  parse_selectors!(selectors) if selectors
18
19
  parse_declarations!(block)
19
20
  end
@@ -48,11 +49,18 @@ module CssParser
48
49
  #
49
50
  # If the property already exists its value will be over-written.
50
51
  def add_declaration!(property, value)
52
+ if value.nil? or value.empty?
53
+ @declarations.delete(property)
54
+ return
55
+ end
56
+
51
57
  value.gsub!(/;\Z/, '')
52
58
  is_important = !value.gsub!(CssParser::IMPORTANT_IN_PROPERTY_RX, '').nil?
53
59
  property = property.downcase.strip
54
60
  #puts "SAVING #{property} #{value} #{is_important.inspect}"
55
- @declarations[property] = {:value => value, :is_important => is_important}
61
+ @declarations[property] = {
62
+ :value => value, :is_important => is_important, :order => @order += 1
63
+ }
56
64
  end
57
65
  alias_method :[]=, :add_declaration!
58
66
 
@@ -76,7 +84,8 @@ module CssParser
76
84
 
77
85
  # Iterate through declarations.
78
86
  def each_declaration # :yields: property, value, is_important
79
- @declarations.each do |property, data|
87
+ decs = @declarations.sort { |a,b| a[1][:order] <=> b[1][:order] }
88
+ decs.each do |property, data|
80
89
  value = data[:value]
81
90
  #value += ' !important' if data[:is_important]
82
91
  yield property.downcase.strip, value.strip, data[:is_important]
@@ -84,13 +93,14 @@ module CssParser
84
93
  end
85
94
 
86
95
  # Return all declarations as a string.
96
+ #--
97
+ # TODO: Clean-up regexp doesn't seem to work
98
+ #++
87
99
  def declarations_to_s(options = {})
88
100
  options = {:force_important => false}.merge(options)
89
101
  str = ''
90
- each_declaration do |prop, val, is_important|
91
- importance = (options[:force_important] || is_important) ? ' !important' : ''
92
- str += "#{prop}: #{val}#{importance}; "
93
- end
102
+ importance = options[:force_important] ? ' !important' : ''
103
+ each_declaration { |prop, val| str += "#{prop}: #{val}#{importance}; " }
94
104
  str.gsub(/^[\s]+|[\n\r\f\t]*|[\s]+$/mx, '').strip
95
105
  end
96
106
 
@@ -138,6 +148,7 @@ private
138
148
  @selectors = selectors.split(',')
139
149
  end
140
150
 
151
+ public
141
152
  # Split shorthand dimensional declarations (e.g. <tt>margin: 0px auto;</tt>)
142
153
  # into their constituent parts.
143
154
  def expand_dimensions_shorthand! # :nodoc:
@@ -147,6 +158,7 @@ private
147
158
 
148
159
  value = @declarations[property][:value]
149
160
  is_important = @declarations[property][:is_important]
161
+ order = @declarations[property][:order]
150
162
  t, r, b, l = nil
151
163
 
152
164
  matches = value.scan(CssParser::BOX_MODEL_UNITS_RX)
@@ -168,10 +180,11 @@ private
168
180
  l = matches[3][0]
169
181
  end
170
182
 
171
- @declarations["#{property}-top"] = {:value => t.to_s, :is_important => is_important}
172
- @declarations["#{property}-right"] = {:value => r.to_s, :is_important => is_important}
173
- @declarations["#{property}-bottom"] = {:value => b.to_s, :is_important => is_important}
174
- @declarations["#{property}-left"] = {:value => l.to_s, :is_important => is_important}
183
+ values = { :is_important => is_important, :order => order }
184
+ @declarations["#{property}-top"] = values.merge(:value => t.to_s)
185
+ @declarations["#{property}-right"] = values.merge(:value => r.to_s)
186
+ @declarations["#{property}-bottom"] = values.merge(:value => b.to_s)
187
+ @declarations["#{property}-left"] = values.merge(:value => l.to_s)
175
188
  @declarations.delete(property)
176
189
  end
177
190
  end
@@ -191,6 +204,7 @@ private
191
204
 
192
205
  value = @declarations['font'][:value]
193
206
  is_important = @declarations['font'][:is_important]
207
+ order = @declarations['font'][:order]
194
208
 
195
209
  in_fonts = false
196
210
 
@@ -225,7 +239,7 @@ private
225
239
  end
226
240
  end
227
241
 
228
- font_props.each { |font_prop, font_val| @declarations[font_prop] = {:value => font_val, :is_important => is_important} }
242
+ font_props.each { |font_prop, font_val| @declarations[font_prop] = {:value => font_val, :is_important => is_important, :order => order} }
229
243
 
230
244
  @declarations.delete('font')
231
245
  end
@@ -240,6 +254,7 @@ private
240
254
 
241
255
  value = @declarations['background'][:value]
242
256
  is_important = @declarations['background'][:is_important]
257
+ order = @declarations['background'][:order]
243
258
 
244
259
  bg_props = {}
245
260
 
@@ -276,7 +291,7 @@ private
276
291
  end
277
292
  end
278
293
 
279
- bg_props.each { |bg_prop, bg_val| @declarations[bg_prop] = {:value => bg_val, :is_important => is_important} }
294
+ bg_props.each { |bg_prop, bg_val| @declarations[bg_prop] = {:value => bg_val, :is_important => is_important, :order => order} }
280
295
 
281
296
  @declarations.delete('background')
282
297
  end
@@ -372,9 +387,5 @@ private
372
387
  end
373
388
 
374
389
  end
375
-
376
-
377
-
378
-
379
390
  end
380
- end
391
+ end
@@ -1,3 +1,4 @@
1
+ # coding: iso-8859-1
1
2
  require File.dirname(__FILE__) + '/test_helper'
2
3
 
3
4
  # Test cases for CSS regular expressions
@@ -1,8 +1,8 @@
1
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../'))
2
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../lib/'))
3
- require 'rubygems'
4
- require 'test/unit'
5
- require 'css_parser'
6
- require 'net/http'
7
- require 'open-uri'
8
- require 'WEBrick'
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../'))
2
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../lib/'))
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+ require 'css_parser'
6
+ require 'net/http'
7
+ require 'open-uri'
8
+ require 'WEBrick'
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
+ require "set"
2
3
 
3
4
  # Test cases for parsing CSS blocks
4
5
  class RuleSetTests < Test::Unit::TestCase
@@ -32,7 +33,7 @@ class RuleSetTests < Test::Unit::TestCase
32
33
  expected = [
33
34
  {:selector => "#content p", :declarations => "color: #fff;", :specificity => 101},
34
35
  {:selector => "a", :declarations => "color: #fff;", :specificity => 1}
35
- ]
36
+ ]
36
37
 
37
38
  actual = []
38
39
  rs = RuleSet.new('#content p, a', 'color: #fff;')
@@ -44,13 +45,13 @@ class RuleSetTests < Test::Unit::TestCase
44
45
  end
45
46
 
46
47
  def test_each_declaration
47
- expected = [
48
+ expected = Set.new([
48
49
  {:property => 'margin', :value => '1px -0.25em', :is_important => false},
49
50
  {:property => 'background', :value => 'white none no-repeat', :is_important => true},
50
51
  {:property => 'color', :value => '#fff', :is_important => false}
51
- ]
52
+ ])
52
53
 
53
- actual = []
54
+ actual = Set.new
54
55
  rs = RuleSet.new(nil, 'color: #fff; Background: white none no-repeat !important; margin: 1px -0.25em;')
55
56
  rs.each_declaration do |prop, val, imp|
56
57
  actual << {:property => prop, :value => val, :is_important => imp}
@@ -59,6 +60,15 @@ class RuleSetTests < Test::Unit::TestCase
59
60
  assert_equal(expected, actual)
60
61
  end
61
62
 
63
+ def test_each_declaration_respects_order
64
+ css_fragment = "margin: 0; padding: 20px; margin-bottom: 28px;"
65
+ rs = RuleSet.new(nil, css_fragment)
66
+ expected = %w(margin padding margin-bottom)
67
+ actual = []
68
+ rs.each_declaration { |prop, val, imp| actual << prop }
69
+ assert_equal(expected, actual)
70
+ end
71
+
62
72
  def test_declarations_to_s
63
73
  declarations = 'color: #fff; font-weight: bold;'
64
74
  rs = RuleSet.new('#content p, a', declarations)
metadata CHANGED
@@ -1,44 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.2
3
- specification_version: 1
4
2
  name: css_parser
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.9.1
7
- date: 2008-11-12 00:00:00 -08:00
8
- summary: A set of classes for parsing CSS.
9
- require_paths:
10
- - lib
11
- email:
12
- homepage: http://code.dunae.ca/css_parser
13
- rubyforge_project:
14
- description:
15
- autorequire:
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 1.0.0
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Alex Dunae
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-21 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A set of classes for parsing CSS in Ruby.
17
+ email: code@dunae.ca
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
31
24
  files:
32
25
  - lib/css_parser.rb
33
26
  - lib/css_parser/parser.rb
34
27
  - lib/css_parser/regexps.rb
35
28
  - lib/css_parser/rule_set.rb
36
- - test/fixtures
37
29
  - test/fixtures/import-circular-reference.css
38
30
  - test/fixtures/import-with-media-types.css
39
31
  - test/fixtures/import1.css
40
32
  - test/fixtures/simple.css
41
- - test/fixtures/subdir
42
33
  - test/fixtures/subdir/import2.css
43
34
  - test/test_css_parser_basic.rb
44
35
  - test/test_css_parser_downloading.rb
@@ -50,9 +41,38 @@ files:
50
41
  - test/test_rule_set.rb
51
42
  - test/test_rule_set_creating_shorthand.rb
52
43
  - test/test_rule_set_expanding_shorthand.rb
53
- - README
54
- - CHANGELOG
55
- - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/alexdunae/css_parser
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --all
51
+ - --inline-source
52
+ - --line-numbers
53
+ - --charset
54
+ - utf-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.3.5
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Ruby CSS parser.
56
76
  test_files:
57
77
  - test/test_css_parser_basic.rb
58
78
  - test/test_css_parser_downloading.rb
@@ -64,19 +84,3 @@ test_files:
64
84
  - test/test_rule_set.rb
65
85
  - test/test_rule_set_creating_shorthand.rb
66
86
  - test/test_rule_set_expanding_shorthand.rb
67
- rdoc_options:
68
- - --all
69
- - --inline-source
70
- - --line-numbers
71
- extra_rdoc_files:
72
- - README
73
- - CHANGELOG
74
- - LICENSE
75
- executables: []
76
-
77
- extensions: []
78
-
79
- requirements: []
80
-
81
- dependencies: []
82
-
data/CHANGELOG DELETED
@@ -1,13 +0,0 @@
1
- === Ruby CSS Parser CHANGELOG
2
-
3
- ==== Version 0.9.1
4
- * Fixed RuleSet#declaration_to_s so it would respect <tt>!important</tt>
5
- rules (thanks to Dana - http://github.com/DanaDanger)
6
-
7
- ==== Version 0.9
8
- * Initial version forked from Premailer project
9
-
10
- ==== TODO: Future
11
- * border shorthand/folding support
12
- * re-implement caching on CssParser.merge
13
- * correctly parse http://www.webstandards.org/files/acid2/test.html
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- === Ruby CSS Parser License
2
-
3
- Copyright (c) 2007 Alex Dunae
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
data/README DELETED
@@ -1,58 +0,0 @@
1
- === Ruby CSS Parser
2
-
3
- Load, parse and cascade CSS rule sets in Ruby.
4
-
5
- ==== Setup
6
-
7
- Install the gem from RubyGems.
8
-
9
- gem install css_parser
10
-
11
- Done.
12
-
13
- ==== An example
14
- require 'css_parser'
15
- include CssParser
16
-
17
- parser = CssParser::Parser.new
18
- parser.load_file!('http://example.com/styles/style.css')
19
-
20
- # lookup a rule by a selector
21
- parser.find('#content')
22
- #=> 'font-size: 13px; line-height: 1.2;'
23
-
24
- # lookup a rule by a selector and media type
25
- parser.find('#content', [:screen, :handheld])
26
-
27
- # iterate through selectors by media type
28
- parser.each_selector(:screen) do |selector, declarations, specificity|
29
- ...
30
- end
31
-
32
- # add a block of CSS
33
- css = <<-EOT
34
- body { margin: 0 1em; }
35
- EOT
36
-
37
- parser.add_block!(css)
38
-
39
- # output all CSS rules in a single stylesheet
40
- parser.to_s
41
- => #content { font-size: 13px; line-height: 1.2; }
42
- body { margin: 0 1em; }
43
-
44
- ==== Testing
45
-
46
- You can run the suite of unit tests using <tt>rake test</tt>.
47
-
48
- The download/import tests require that WEBrick is installed. The tests set up
49
- a temporary server on port 12000 and pull down files from the <tt>test/fixtures/</tt>
50
- directory.
51
-
52
- ==== Credits and code
53
-
54
- By Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2007-08.
55
-
56
- Thanks to Dana (http://github.com/DanaDanger) for the 0.9.1 update.
57
-
58
- Made on Vancouver Island.