lib/css_parser/parser.rb in css_parser-1.0.1 vs lib/css_parser/parser.rb in css_parser-1.1.1
- old
+ new
@@ -13,17 +13,17 @@
# When calling Parser#new there are some configuaration options:
# [<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>.
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
class Parser
- USER_AGENT = "Ruby CSS Parser/#{RUBY_VERSION} (http://code.dunae.ca/css_parser/)"
+ USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (http://github.com/alexdunae/css_parser)"
STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
# Initial parsing
- RE_AT_IMPORT_RULE = /\@import[\s]+(url\()?["']+(.[^'"]*)["']\)?([\w\s\,]*);?/i
+ RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\])]*)\)?[;\n]?/
#--
# 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
#++
@@ -80,35 +80,59 @@
alias_method :[], :find_by_selector
# Add a raw block of CSS.
#
+ # In order to follow +@import+ rules you must supply either a
+ # +:base_dir+ or +:base_uri+ option.
+ #
# ==== Example
# css = <<-EOT
# body { font-size: 10pt }
# p { margin: 0px; }
# @media screen, print {
# body { line-height: 1.2 }
# }
# EOT
#
# parser = CssParser::Parser.new
- # parser.load_css!(css)
+ # parser.add_block!(css)
#--
# TODO: add media_type
#++
def add_block!(block, options = {})
- options = {:base_uri => nil, :charset => nil, :media_types => :all}.merge(options)
+ options = {:base_uri => nil, :base_dir => nil, :charset => nil, :media_types => :all}.merge(options)
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(/\s|\,/).each do |t|
+ media_types << t.to_sym unless t.empty?
+ end
+ end
+
+ import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
+
+ if options[:base_uri]
+ import_uri = URI.parse(options[:base_uri].to_s).merge(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
+
+ # 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+.
#
# +media_types+ can be a symbol or an array of symbols.
@@ -243,37 +267,29 @@
end
# Load a remote CSS file.
def load_uri!(uri, base_uri = nil, media_types = :all)
base_uri = uri if base_uri.nil?
- src, charset = read_remote_file(uri)
- # Load @imported CSS
- src.scan(RE_AT_IMPORT_RULE).each do |import_rule|
- import_path = import_rule[1].to_s.gsub(/['"]*/, '').strip
- import_uri = URI.parse(base_uri.to_s).merge(import_path)
- #puts import_uri.to_s
-
- media_types = []
- if media_string = import_rule[import_rule.length-1]
- media_string.split(/\s|\,/).each do |t|
- media_types << t.to_sym unless t.empty?
- end
- end
-
- # Recurse
- load_uri!(import_uri, nil, media_types)
+ src, charset = read_remote_file(uri)
+ unless src.empty?
+ add_block!(src, {:media_types => media_types, :base_uri => base_uri})
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)
- # Remove @import declarations
- src.gsub!(RE_AT_IMPORT_RULE, '')
+ src = IO.read(file_name)
+ base_dir = File.dirname(file_name)
- # Relative paths need to be converted here
- src = CssParser.convert_uris(src, base_uri) if base_uri and @options[:absolute_paths]
-
- add_block!(src, {:media_types => media_types})
+ add_block!(src, {:media_types => media_types, :base_dir => base_dir})
end
+
+
protected
# Strip comments and clean up blank lines from a block of CSS.
#
# Returns a string.
@@ -296,11 +312,15 @@
# 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:
- raise CircularReferenceError, "can't load #{uri.to_s} more than once" if @loaded_uris.include?(uri.to_s)
+ if @loaded_uris.include?(uri.to_s)
+ raise CircularReferenceError, "can't load #{uri.to_s} more than once" if @options[:io_exceptions]
+ return '', nil
+ end
+
@loaded_uris << uri.to_s
begin
#fh = open(uri, 'rb')
fh = open(uri, 'rb', 'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip')
@@ -316,11 +336,11 @@
ic = Iconv.new('UTF-8//IGNORE', fh.charset)
src = ic.iconv(remote_src)
fh.close
return src, fh.charset
- rescue
+ rescue Exception => e
raise RemoteFileError if @options[:io_exceptions]
return '', nil
end
end
@@ -340,6 +360,6 @@
@css_source = ''
@css_rules = []
@css_warnings = []
end
end
-end
\ No newline at end of file
+end