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,5 +1,3 @@
1
- $LOAD_PATH.concat Dir.glob(File.expand_path('../../vendor/gems/*/lib', __FILE__))
2
-
3
1
  require 'css_parser'
4
2
  require 'nokogiri'
5
3
  require 'open-uri'
@@ -176,7 +174,9 @@ private
176
174
  end
177
175
 
178
176
  def strip(selector)
179
- selector.gsub(/::?[\w\-]+/, '')
177
+ selector = selector.gsub(/^@.*/, '') # @-webkit-keyframes ...
178
+ selector = selector.gsub(/:.*/, '') # input#x:nth-child(2):not(#z.o[type='file'])
179
+ selector
180
180
  end
181
181
 
182
182
  def log
@@ -1,5 +1,7 @@
1
1
  class Deadweight
2
2
  class RakeTask
3
+ include Rake::DSL if defined?(Rake::DSL)
4
+
3
5
  def initialize output=STDOUT, &block
4
6
  desc "run deadweight"
5
7
  task :deadweight do
metadata CHANGED
@@ -1,70 +1,103 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: deadweight
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 1
9
- version: 0.2.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Aanand Prasad
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-04-18 00:00:00 +01:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-09-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
22
23
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: css_parser
32
+ requirement: !ruby/object:Gem::Requirement
24
33
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- version: "0"
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.2.6
31
38
  type: :runtime
32
- version_requirements: *id001
33
- - !ruby/object:Gem::Dependency
34
- name: shoulda
35
39
  prerelease: false
36
- requirement: &id002 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.6
46
+ - !ruby/object:Gem::Dependency
47
+ name: shoulda
48
+ requirement: !ruby/object:Gem::Requirement
37
49
  none: false
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
- version: "0"
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
44
54
  type: :development
45
- version_requirements: *id002
46
- - !ruby/object:Gem::Dependency
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
47
63
  name: mechanize
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.5.1
70
+ type: :development
48
71
  prerelease: false
49
- requirement: &id003 !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
50
73
  none: false
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- segments:
55
- - 0
56
- version: "0"
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.5.1
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
57
86
  type: :development
58
- version_requirements: *id003
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
59
94
  description:
60
95
  email: aanand.prasad@gmail.com
61
- executables:
96
+ executables:
62
97
  - deadweight
63
98
  extensions: []
64
-
65
99
  extra_rdoc_files: []
66
-
67
- files:
100
+ files:
68
101
  - LICENSE
69
102
  - README.md
70
103
  - bin/deadweight
@@ -74,56 +107,29 @@ files:
74
107
  - lib/deadweight/hijack/rails.rb
75
108
  - lib/deadweight/rack/capturing_middleware.rb
76
109
  - lib/deadweight/rake_task.rb
77
- - vendor/gems/css_parser-1.1.5/lib/css_parser.rb
78
- - vendor/gems/css_parser-1.1.5/lib/css_parser/parser.rb
79
- - vendor/gems/css_parser-1.1.5/lib/css_parser/regexps.rb
80
- - vendor/gems/css_parser-1.1.5/lib/css_parser/rule_set.rb
81
- - vendor/gems/css_parser-1.1.5/test/fixtures/import-circular-reference.css
82
- - vendor/gems/css_parser-1.1.5/test/fixtures/import-with-media-types.css
83
- - vendor/gems/css_parser-1.1.5/test/fixtures/import1.css
84
- - vendor/gems/css_parser-1.1.5/test/fixtures/simple.css
85
- - vendor/gems/css_parser-1.1.5/test/fixtures/subdir/import2.css
86
- - vendor/gems/css_parser-1.1.5/test/test_css_parser_basic.rb
87
- - vendor/gems/css_parser-1.1.5/test/test_css_parser_loading.rb
88
- - vendor/gems/css_parser-1.1.5/test/test_css_parser_media_types.rb
89
- - vendor/gems/css_parser-1.1.5/test/test_css_parser_misc.rb
90
- - vendor/gems/css_parser-1.1.5/test/test_css_parser_regexps.rb
91
- - vendor/gems/css_parser-1.1.5/test/test_helper.rb
92
- - vendor/gems/css_parser-1.1.5/test/test_merging.rb
93
- - vendor/gems/css_parser-1.1.5/test/test_rule_set.rb
94
- - vendor/gems/css_parser-1.1.5/test/test_rule_set_creating_shorthand.rb
95
- - vendor/gems/css_parser-1.1.5/test/test_rule_set_expanding_shorthand.rb
96
- has_rdoc: true
97
110
  homepage: http://github.com/aanand/deadweight
98
- licenses:
111
+ licenses:
99
112
  - MIT
100
113
  post_install_message:
101
114
  rdoc_options: []
102
-
103
- require_paths:
115
+ require_paths:
104
116
  - lib
105
- required_ruby_version: !ruby/object:Gem::Requirement
117
+ required_ruby_version: !ruby/object:Gem::Requirement
106
118
  none: false
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- segments:
111
- - 0
112
- version: "0"
113
- required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
124
  none: false
115
- requirements:
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- segments:
119
- - 0
120
- version: "0"
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
121
129
  requirements: []
122
-
123
130
  rubyforge_project:
124
- rubygems_version: 1.3.7
131
+ rubygems_version: 1.8.23
125
132
  signing_key:
126
133
  specification_version: 3
127
134
  summary: A coverage tool for finding unused CSS
128
135
  test_files: []
129
-
@@ -1,162 +0,0 @@
1
- require 'uri'
2
- require 'net/https'
3
- require 'open-uri'
4
- require 'digest/md5'
5
- require 'zlib'
6
- require 'stringio'
7
- require 'iconv'
8
-
9
- module CssParser
10
- VERSION = '1.1.5'
11
-
12
- # Merge multiple CSS RuleSets by cascading according to the CSS 2.1 cascading rules
13
- # (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order).
14
- #
15
- # Takes one or more RuleSet objects.
16
- #
17
- # Returns a RuleSet.
18
- #
19
- # ==== Cascading
20
- # If a RuleSet object has its +specificity+ defined, that specificity is
21
- # used in the cascade calculations.
22
- #
23
- # If no specificity is explicitly set and the RuleSet has *one* selector,
24
- # the specificity is calculated using that selector.
25
- #
26
- # If no selectors or multiple selectors are present, the specificity is
27
- # treated as 0.
28
- #
29
- # ==== Example #1
30
- # rs1 = RuleSet.new(nil, 'color: black;')
31
- # rs2 = RuleSet.new(nil, 'margin: 0px;')
32
- #
33
- # merged = CssParser.merge(rs1, rs2)
34
- #
35
- # puts merged
36
- # => "{ margin: 0px; color: black; }"
37
- #
38
- # ==== Example #2
39
- # rs1 = RuleSet.new(nil, 'background-color: black;')
40
- # rs2 = RuleSet.new(nil, 'background-image: none;')
41
- #
42
- # merged = CssParser.merge(rs1, rs2)
43
- #
44
- # puts merged
45
- # => "{ background: none black; }"
46
- #--
47
- # TODO: declaration_hashes should be able to contain a RuleSet
48
- # this should be a Class method
49
- def CssParser.merge(*rule_sets)
50
- @folded_declaration_cache = {}
51
-
52
- # in case called like CssParser.merge([rule_set, rule_set])
53
- rule_sets.flatten! if rule_sets[0].kind_of?(Array)
54
-
55
- unless rule_sets.all? {|rs| rs.kind_of?(CssParser::RuleSet)}
56
- raise ArgumentError, "all parameters must be CssParser::RuleSets."
57
- end
58
-
59
- return rule_sets[0] if rule_sets.length == 1
60
-
61
- # Internal storage of CSS properties that we will keep
62
- properties = {}
63
-
64
- rule_sets.each do |rule_set|
65
- rule_set.expand_shorthand!
66
-
67
- specificity = rule_set.specificity
68
- unless specificity
69
- if rule_set.selectors.length == 1
70
- specificity = calculate_specificity(rule_set.selectors[0])
71
- else
72
- specificity = 0
73
- end
74
- end
75
-
76
- rule_set.each_declaration do |property, value, is_important|
77
- # Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
78
- if not properties.has_key?(property)
79
- properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
80
- elsif properties[property][:specificity] < specificity or properties[property][:specificity] == specificity
81
- unless properties[property][:is_important]
82
- properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
83
- end
84
- end
85
-
86
- if is_important
87
- properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
88
- end
89
- end
90
- end
91
-
92
- merged = RuleSet.new(nil, nil)
93
-
94
- properties.each do |property, details|
95
- if details[:is_important]
96
- merged[property.strip] = details[:value].strip.gsub(/\;\Z/, '') + '!important'
97
- else
98
- merged[property.strip] = details[:value].strip
99
- end
100
- end
101
-
102
- merged.create_shorthand!
103
- merged
104
- end
105
-
106
- # Calculates the specificity of a CSS selector
107
- # per http://www.w3.org/TR/CSS21/cascade.html#specificity
108
- #
109
- # Returns an integer.
110
- #
111
- # ==== Example
112
- # CssParser.calculate_specificity('#content div p:first-line a:link')
113
- # => 114
114
- #--
115
- # Thanks to Rafael Salazar and Nick Fitzsimons on the css-discuss list for their help.
116
- #++
117
- def CssParser.calculate_specificity(selector)
118
- a = 0
119
- b = selector.scan(/\#/).length
120
- c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX).length
121
- d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX).length
122
-
123
- (a.to_s + b.to_s + c.to_s + d.to_s).to_i
124
- rescue
125
- return 0
126
- end
127
-
128
- # Make <tt>url()</tt> links absolute.
129
- #
130
- # Takes a block of CSS and returns it with all relative URIs converted to absolute URIs.
131
- #
132
- # "For CSS style sheets, the base URI is that of the style sheet, not that of the source document."
133
- # per http://www.w3.org/TR/CSS21/syndata.html#uri
134
- #
135
- # Returns a string.
136
- #
137
- # ==== Example
138
- # CssParser.convert_uris("body { background: url('../style/yellow.png?abc=123') };",
139
- # "http://example.org/style/basic.css").inspect
140
- # => "body { background: url('http://example.org/style/yellow.png?abc=123') };"
141
- def self.convert_uris(css, base_uri)
142
- out = ''
143
- base_uri = URI.parse(base_uri) unless base_uri.kind_of?(URI)
144
-
145
- out = css.gsub(URI_RX) do |s|
146
- uri = $1.to_s
147
- uri.gsub!(/["']+/, '')
148
- # Don't process URLs that are already absolute
149
- unless uri =~ /^[a-z]+\:\/\//i
150
- begin
151
- uri = base_uri.merge(uri)
152
- rescue; end
153
- end
154
- "url('" + uri.to_s + "')"
155
- end
156
- out
157
- end
158
- end
159
-
160
- require File.dirname(__FILE__) + '/css_parser/rule_set'
161
- require File.dirname(__FILE__) + '/css_parser/regexps'
162
- require File.dirname(__FILE__) + '/css_parser/parser'
@@ -1,411 +0,0 @@
1
- module CssParser
2
- # Exception class used for any errors encountered while downloading remote files.
3
- class RemoteFileError < IOError; end
4
-
5
- # Exception class used if a request is made to load a CSS file more than once.
6
- class CircularReferenceError < StandardError; end
7
-
8
-
9
- # == Parser class
10
- #
11
- # All CSS is converted to UTF-8.
12
- #
13
- # When calling Parser#new there are some configuaration options:
14
- # [<tt>absolute_paths</tt>] Convert relative paths to absolute paths (<tt>href</tt>, <tt>src</tt> and <tt>url('')</tt>. Boolean, default is <tt>false</tt>.
15
- # [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
16
- # [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
17
- class Parser
18
- USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (http://github.com/alexdunae/css_parser)"
19
-
20
- STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
21
- STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
22
-
23
- # Initial parsing
24
- RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\])]*)\)?[;\n]?/
25
-
26
- # Array of CSS files that have been loaded.
27
- attr_reader :loaded_uris
28
-
29
- #attr_reader :rules
30
-
31
- #--
32
- # Class variable? see http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html
33
- #++
34
- @folded_declaration_cache = {}
35
- class << self; attr_reader :folded_declaration_cache; end
36
-
37
- def initialize(options = {})
38
- @options = {:absolute_paths => false,
39
- :import => true,
40
- :io_exceptions => true}.merge(options)
41
-
42
- # array of RuleSets
43
- @rules = []
44
-
45
-
46
- @loaded_uris = []
47
-
48
- # unprocessed blocks of CSS
49
- @blocks = []
50
- reset!
51
- end
52
-
53
- # Get declarations by selector.
54
- #
55
- # +media_types+ are optional, and can be a symbol or an array of symbols.
56
- # The default value is <tt>:all</tt>.
57
- #
58
- # ==== Examples
59
- # find_by_selector('#content')
60
- # => 'font-size: 13px; line-height: 1.2;'
61
- #
62
- # find_by_selector('#content', [:screen, :handheld])
63
- # => 'font-size: 13px; line-height: 1.2;'
64
- #
65
- # find_by_selector('#content', :print)
66
- # => 'font-size: 11pt; line-height: 1.2;'
67
- #
68
- # Returns an array of declarations.
69
- def find_by_selector(selector, media_types = :all)
70
- out = []
71
- each_selector(media_types) do |sel, dec, spec|
72
- out << dec if sel.strip == selector.strip
73
- end
74
- out
75
- end
76
- alias_method :[], :find_by_selector
77
-
78
-
79
- # Add a raw block of CSS.
80
- #
81
- # In order to follow +@import+ rules you must supply either a
82
- # +:base_dir+ or +:base_uri+ option.
83
- #
84
- # Use the +:media_types+ option to set the media type(s) for this block. Takes an array of symbols.
85
- #
86
- # Use the +:only_media_types+ option to selectively follow +@import+ rules. Takes an array of symbols.
87
- #
88
- # ==== Example
89
- # css = <<-EOT
90
- # body { font-size: 10pt }
91
- # p { margin: 0px; }
92
- # @media screen, print {
93
- # body { line-height: 1.2 }
94
- # }
95
- # EOT
96
- #
97
- # parser = CssParser::Parser.new
98
- # parser.add_block!(css)
99
- def add_block!(block, options = {})
100
- options = {:base_uri => nil, :base_dir => nil, :charset => nil, :media_types => :all, :only_media_types => :all}.merge(options)
101
- options[:media_types] = [options[:media_types]].flatten
102
- options[:only_media_types] = [options[:only_media_types]].flatten
103
-
104
- block = cleanup_block(block)
105
-
106
- if options[:base_uri] and @options[:absolute_paths]
107
- block = CssParser.convert_uris(block, options[:base_uri])
108
- end
109
-
110
- # Load @imported CSS
111
- block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
112
- media_types = []
113
- if media_string = import_rule[-1]
114
- media_string.split(/\s|\,/).each do |t|
115
- media_types << t.to_sym unless t.empty?
116
- end
117
- end
118
-
119
- next unless options[:only_media_types].include?(:all) or media_types.length < 1 or (media_types & options[:only_media_types]).length > 0
120
-
121
- import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
122
-
123
- if options[:base_uri]
124
- import_uri = URI.parse(options[:base_uri].to_s).merge(import_path)
125
- load_uri!(import_uri, options[:base_uri], media_types)
126
- elsif options[:base_dir]
127
- load_file!(import_path, options[:base_dir], media_types)
128
- end
129
- end
130
-
131
- # Remove @import declarations
132
- block.gsub!(RE_AT_IMPORT_RULE, '')
133
-
134
- parse_block_into_rule_sets!(block, options)
135
- end
136
-
137
- # Add a CSS rule by setting the +selectors+, +declarations+ and +media_types+.
138
- #
139
- # +media_types+ can be a symbol or an array of symbols.
140
- def add_rule!(selectors, declarations, media_types = :all)
141
- rule_set = RuleSet.new(selectors, declarations)
142
- add_rule_set!(rule_set, media_types)
143
- end
144
-
145
- # Add a CssParser RuleSet object.
146
- #
147
- # +media_types+ can be a symbol or an array of symbols.
148
- def add_rule_set!(ruleset, media_types = :all)
149
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
150
-
151
- media_types = [media_types] if media_types.kind_of?(Symbol)
152
-
153
- @rules << {:media_types => media_types, :rules => ruleset}
154
- end
155
-
156
- # Iterate through RuleSet objects.
157
- #
158
- # +media_types+ can be a symbol or an array of symbols.
159
- def each_rule_set(media_types = :all) # :yields: rule_set
160
- media_types = [:all] if media_types.nil?
161
- media_types = [media_types] if media_types.kind_of?(Symbol)
162
-
163
- @rules.each do |block|
164
- if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
165
- yield block[:rules]
166
- end
167
- end
168
- end
169
-
170
- # Iterate through CSS selectors.
171
- #
172
- # +media_types+ can be a symbol or an array of symbols.
173
- # See RuleSet#each_selector for +options+.
174
- def each_selector(media_types = :all, options = {}) # :yields: selectors, declarations, specificity
175
- each_rule_set(media_types) do |rule_set|
176
- #puts rule_set
177
- rule_set.each_selector(options) do |selectors, declarations, specificity|
178
- yield selectors, declarations, specificity
179
- end
180
- end
181
- end
182
-
183
- # Output all CSS rules as a single stylesheet.
184
- def to_s(media_types = :all)
185
- out = ''
186
- each_selector(media_types) do |selectors, declarations, specificity|
187
- out << "#{selectors} {\n#{declarations}\n}\n"
188
- end
189
- out
190
- end
191
-
192
- # Merge declarations with the same selector.
193
- def compact! # :nodoc:
194
- compacted = []
195
-
196
- compacted
197
- end
198
-
199
- def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
200
- options = {:media_types => :all}.merge(options)
201
- media_types = options[:media_types]
202
-
203
- in_declarations = 0
204
-
205
- block_depth = 0
206
-
207
- # @charset is ignored for now
208
- in_charset = false
209
- in_string = false
210
- in_at_media_rule = false
211
-
212
- current_selectors = ''
213
- current_declarations = ''
214
-
215
- block.scan(/([\\]?[{}\s"]|(.[^\s"{}\\]*))/).each do |matches|
216
- #block.scan(/((.[^{}"\n\r\f\s]*)[\s]|(.[^{}"\n\r\f]*)\{|(.[^{}"\n\r\f]*)\}|(.[^{}"\n\r\f]*)\"|(.*)[\s]+)/).each do |matches|
217
- token = matches[0]
218
- #puts "TOKEN: #{token}" unless token =~ /^[\s]*$/
219
- if token =~ /\A"/ # found un-escaped double quote
220
- in_string = !in_string
221
- end
222
-
223
- if in_declarations > 0
224
-
225
- # too deep, malformed declaration block
226
- if in_declarations > 1
227
- in_declarations -= 1 if token =~ /\}/
228
- next
229
- end
230
-
231
- if token =~ /\{/
232
- in_declarations += 1
233
- next
234
- end
235
-
236
- current_declarations += token
237
-
238
- if token =~ /\}/ and not in_string
239
- current_declarations.gsub!(/\}[\s]*$/, '')
240
-
241
- in_declarations -= 1
242
-
243
- unless current_declarations.strip.empty?
244
- #puts "SAVING #{current_selectors} -> #{current_declarations}"
245
- add_rule!(current_selectors, current_declarations, media_types)
246
- end
247
-
248
- current_selectors = ''
249
- current_declarations = ''
250
- end
251
- elsif token =~ /@media/i
252
- # found '@media', reset current media_types
253
- in_at_media_rule = true
254
- media_types = []
255
- elsif in_at_media_rule
256
- if token =~ /\{/
257
- block_depth = block_depth + 1
258
- in_at_media_rule = false
259
- else
260
- token.gsub!(/[,\s]*/, '')
261
- media_types << token.strip.downcase.to_sym unless token.empty?
262
- end
263
- elsif in_charset or token =~ /@charset/i
264
- # iterate until we are out of the charset declaration
265
- in_charset = (token =~ /;/ ? false : true)
266
- else
267
- if token =~ /\}/ and not in_string
268
- block_depth = block_depth - 1
269
- else
270
- if token =~ /\{/ and not in_string
271
- current_selectors.gsub!(/^[\s]*/, '')
272
- current_selectors.gsub!(/[\s]*$/, '')
273
- in_declarations += 1
274
- else
275
- current_selectors += token
276
- end
277
- end
278
- end
279
- end
280
- end
281
-
282
- # Load a remote CSS file.
283
- #
284
- # You can also pass in file://test.css
285
- def load_uri!(uri, base_uri = nil, media_types = :all)
286
- uri = URI.parse(uri) unless uri.respond_to? :scheme
287
- if uri.scheme == 'file' or uri.scheme.nil?
288
- uri.path = File.expand_path(uri.path)
289
- uri.scheme = 'file'
290
- end
291
- base_uri = uri if base_uri.nil?
292
-
293
- src, charset = read_remote_file(uri)
294
-
295
- if src
296
- add_block!(src, {:media_types => media_types, :base_uri => base_uri})
297
- end
298
- end
299
-
300
- # Load a local CSS file.
301
- def load_file!(file_name, base_dir = nil, media_types = :all)
302
- file_name = File.expand_path(file_name, base_dir)
303
- return unless File.readable?(file_name)
304
-
305
- src = IO.read(file_name)
306
- base_dir = File.dirname(file_name)
307
-
308
- add_block!(src, {:media_types => media_types, :base_dir => base_dir})
309
- end
310
-
311
-
312
-
313
- protected
314
- # Strip comments and clean up blank lines from a block of CSS.
315
- #
316
- # Returns a string.
317
- def cleanup_block(block) # :nodoc:
318
- # Strip CSS comments
319
- block.gsub!(STRIP_CSS_COMMENTS_RX, '')
320
-
321
- # Strip HTML comments - they shouldn't really be in here but
322
- # some people are just crazy...
323
- block.gsub!(STRIP_HTML_COMMENTS_RX, '')
324
-
325
- # Strip lines containing just whitespace
326
- block.gsub!(/^\s+$/, "")
327
-
328
- block
329
- end
330
-
331
- # Download a file into a string.
332
- #
333
- # Returns the file's data and character set in an array.
334
- #--
335
- # TODO: add option to fail silently or throw and exception on a 404
336
- #++
337
- def read_remote_file(uri) # :nodoc:
338
- if @loaded_uris.include?(uri.to_s)
339
- raise CircularReferenceError, "can't load #{uri.to_s} more than once" if @options[:io_exceptions]
340
- return '', nil
341
- end
342
-
343
- @loaded_uris << uri.to_s
344
-
345
- src = '', charset = nil
346
-
347
- begin
348
- uri = URI.parse(uri.to_s)
349
- http = Net::HTTP.new(uri.host, uri.port)
350
-
351
- if uri.scheme == 'file'
352
- # local file
353
- fh = open(uri.path, 'rb')
354
- src = fh.read
355
- fh.close
356
- else
357
- # remote file
358
- if uri.scheme == 'https'
359
- http.use_ssl = true
360
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
361
- end
362
-
363
- res, src = http.get(uri.path, {'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip'})
364
- charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
365
-
366
- if res.code.to_i >= 400
367
- raise RemoteFileError if @options[:io_exceptions]
368
- return '', nil
369
- end
370
-
371
- case res['content-encoding']
372
- when 'gzip'
373
- io = Zlib::GzipReader.new(StringIO.new(res.body))
374
- src = io.read
375
- when 'deflate'
376
- io = Zlib::Inflate.new
377
- src = io.inflate(res.body)
378
- end
379
- end
380
-
381
- if charset
382
- ic = Iconv.new('UTF-8//IGNORE', charset)
383
- src = ic.iconv(src)
384
- end
385
- rescue
386
- raise RemoteFileError if @options[:io_exceptions]
387
- return nil, nil
388
- end
389
-
390
- return src, charset
391
- end
392
-
393
- private
394
- # Save a folded declaration block to the internal cache.
395
- def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
396
- @folded_declaration_cache[block_hash] = folded_declaration
397
- end
398
-
399
- # Retrieve a folded declaration block from the internal cache.
400
- def get_folded_declaration(block_hash) # :nodoc:
401
- return @folded_declaration_cache[block_hash] ||= nil
402
- end
403
-
404
- def reset! # :nodoc:
405
- @folded_declaration_cache = {}
406
- @css_source = ''
407
- @css_rules = []
408
- @css_warnings = []
409
- end
410
- end
411
- end