css_parser 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.