lib/css_parser/parser.rb in css_parser-1.3.4 vs lib/css_parser/parser.rb in css_parser-1.3.5
- old
+ new
@@ -22,11 +22,11 @@
# Initial parsing
RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\]\(\))]*)\)?[;\n]?/
# Array of CSS files that have been loaded.
attr_reader :loaded_uris
-
+
#--
# Class variable? see http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html
#++
@folded_declaration_cache = {}
class << self; attr_reader :folded_declaration_cache; end
@@ -36,14 +36,14 @@
:import => true,
:io_exceptions => true}.merge(options)
# array of RuleSets
@rules = []
-
-
+
+
@loaded_uris = []
-
+
# unprocessed blocks of CSS
@blocks = []
reset!
end
@@ -75,11 +75,11 @@
# Finds the rule sets that match the given selectors
def find_rule_sets(selectors, media_types = :all)
rule_sets = []
selectors.each do |selector|
- each_rule_set(media_types) do |rule_set|
+ each_rule_set(media_types) do |rule_set, media_type|
if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
rule_sets << rule_set
end
end
end
@@ -115,37 +115,37 @@
block = cleanup_block(block)
if options[:base_uri] and @options[:absolute_paths]
block = CssParser.convert_uris(block, options[:base_uri])
end
-
+
# Load @imported CSS
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
media_types = []
if media_string = import_rule[-1]
media_string.split(/[,]/).each do |t|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
end
else
media_types = [:all]
end
-
+
next unless options[:only_media_types].include?(:all) or media_types.length < 1 or (media_types & options[:only_media_types]).length > 0
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
-
+
if options[:base_uri]
import_uri = Addressable::URI.parse(options[:base_uri].to_s) + Addressable::URI.parse(import_path)
load_uri!(import_uri, options[:base_uri], media_types)
elsif options[:base_dir]
load_file!(import_path, options[:base_dir], media_types)
- end
+ end
end
# Remove @import declarations
block.gsub!(RE_AT_IMPORT_RULE, '')
-
+
parse_block_into_rule_sets!(block, options)
end
# Add a CSS rule by setting the +selectors+, +declarations+ and +media_types+.
#
@@ -167,42 +167,62 @@
end
# Iterate through RuleSet objects.
#
# +media_types+ can be a symbol or an array of symbols.
- def each_rule_set(media_types = :all) # :yields: rule_set
+ def each_rule_set(media_types = :all) # :yields: rule_set, media_types
media_types = [:all] if media_types.nil?
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
@rules.each do |block|
if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
- yield block[:rules]
+ yield(block[:rules], block[:media_types])
end
end
end
# Iterate through CSS selectors.
#
# +media_types+ can be a symbol or an array of symbols.
# See RuleSet#each_selector for +options+.
- def each_selector(media_types = :all, options = {}) # :yields: selectors, declarations, specificity
- each_rule_set(media_types) do |rule_set|
+ def each_selector(media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
+ each_rule_set(media_types) do |rule_set, media_types|
rule_set.each_selector(options) do |selectors, declarations, specificity|
- yield selectors, declarations, specificity
+ yield selectors, declarations, specificity, media_types
end
end
end
# Output all CSS rules as a single stylesheet.
def to_s(media_types = :all)
out = ''
- each_selector(media_types) do |selectors, declarations, specificity|
- out << "#{selectors} {\n#{declarations}\n}\n"
+ styles_by_media_types = {}
+ each_selector(media_types) do |selectors, declarations, specificity, media_types|
+ media_types.each do |media_type|
+ styles_by_media_types[media_type] ||= []
+ styles_by_media_types[media_type] << [selectors, declarations]
+ end
end
+
+ styles_by_media_types.each_pair do |media_type, media_styles|
+ media_block = (media_type != :all)
+ out += "@media #{media_type} {\n" if media_block
+
+ media_styles.each do |media_style|
+ if media_block
+ out += " #{media_style[0]} {\n #{media_style[1]}\n }\n"
+ else
+ out += "#{media_style[0]} {\n#{media_style[1]}\n}\n"
+ end
+ end
+
+ out += "}\n" if media_block
+ end
+
out
end
-
+
# A hash of { :media_query => rule_sets }
def rules_by_media_query
rules_by_media = {}
@rules.each do |block|
block[:media_types].each do |mt|
@@ -210,11 +230,11 @@
rules_by_media[mt] = []
end
rules_by_media[mt] << block[:rules]
end
end
-
+
rules_by_media
end
# Merge declarations with the same selector.
def compact! # :nodoc:
@@ -244,29 +264,29 @@
block.scan(/([\\]?[{}\s"]|(.[^\s"{}\\]*))/).each do |matches|
token = matches[0]
if token =~ /\A"/ # found un-escaped double quote
in_string = !in_string
- end
+ end
if in_declarations > 0
# too deep, malformed declaration block
if in_declarations > 1
in_declarations -= 1 if token =~ /\}/
next
end
-
+
if token =~ /\{/
in_declarations += 1
next
end
-
+
current_declarations += token
if token =~ /\}/ and not in_string
current_declarations.gsub!(/\}[\s]*$/, '')
-
+
in_declarations -= 1
unless current_declarations.strip.empty?
add_rule!(current_selectors, current_declarations, current_media_queries)
end
@@ -316,11 +336,11 @@
end
end
end
end
- # check for unclosed braces
+ # check for unclosed braces
if in_declarations > 0
add_rule!(current_selectors, current_declarations, current_media_queries)
end
end
@@ -341,11 +361,11 @@
opts.merge!(options)
else
opts[:base_uri] = options if options.is_a? String
opts[:media_types] = deprecated if deprecated
end
-
+
if uri.scheme == 'file' or uri.scheme.nil?
uri.path = File.expand_path(uri.path)
uri.scheme = 'file'
end
@@ -354,11 +374,11 @@
src, charset = read_remote_file(uri)
if src
add_block!(src, opts)
end
end
-
+
# Load a local CSS file.
def load_file!(file_name, base_dir = nil, media_types = :all)
file_name = File.expand_path(file_name, base_dir)
return unless File.readable?(file_name)
return unless circular_reference_check(file_name)
@@ -366,22 +386,22 @@
src = IO.read(file_name)
base_dir = File.dirname(file_name)
add_block!(src, {:media_types => media_types, :base_dir => base_dir})
end
-
+
# Load a local CSS string.
def load_string!(src, base_dir = nil, media_types = :all)
add_block!(src, {:media_types => media_types, :base_dir => base_dir})
end
-
-
+
+
protected
# Check that a path hasn't been loaded already
#
- # Raises a CircularReferenceError exception if io_exceptions are on,
+ # Raises a CircularReferenceError exception if io_exceptions are on,
# otherwise returns true/false.
def circular_reference_check(path)
path = path.to_s
if @loaded_uris.include?(path)
raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
@@ -389,19 +409,19 @@
else
@loaded_uris << path
return true
end
end
-
+
# Strip comments and clean up blank lines from a block of CSS.
#
# Returns a string.
def cleanup_block(block) # :nodoc:
# Strip CSS comments
block.gsub!(STRIP_CSS_COMMENTS_RX, '')
- # Strip HTML comments - they shouldn't really be in here but
+ # Strip HTML comments - they shouldn't really be in here but
# some people are just crazy...
block.gsub!(STRIP_HTML_COMMENTS_RX, '')
# Strip lines containing just whitespace
block.gsub!(/^\s+$/, "")
@@ -414,16 +434,16 @@
# Returns the file's data and character set in an array.
#--
# TODO: add option to fail silently or throw and exception on a 404
#++
def read_remote_file(uri) # :nodoc:
- return nil, nil unless circular_reference_check(uri.to_s)
+ return nil, nil unless circular_reference_check(uri.to_s)
src = '', charset = nil
begin
- uri = Addressable::URI.parse(uri.to_s)
+ uri = Addressable::URI.parse(uri.to_s)
if uri.scheme == 'file'
# local file
fh = open(uri.path, 'rb')
src = fh.read
@@ -431,17 +451,17 @@
else
# remote file
if uri.scheme == 'https'
uri.port = 443 unless uri.port
http = Net::HTTP.new(uri.host, uri.port)
- http.use_ssl = true
+ http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
else
http = Net::HTTP.new(uri.host, uri.port)
end
- res = http.get(uri.path, {'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip'})
+ res = http.get(uri.request_uri, {'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip'})
src = res.body
charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
if res.code.to_i >= 400
raise RemoteFileError if @options[:io_exceptions]
@@ -469,10 +489,10 @@
rescue
raise RemoteFileError if @options[:io_exceptions]
return nil, nil
end
- return src, charset
+ return src, charset
end
private
# Save a folded declaration block to the internal cache.
def save_folded_declaration(block_hash, folded_declaration) # :nodoc: