class CssParser::Parser
Parser
class¶ ↑
All CSS is converted to UTF-8.
When calling Parser#new there are some configuaration options:
absolute_paths
-
Convert relative paths to absolute paths (
href
,src
andurl('')
. Boolean, default isfalse
. import
-
Follow
@import
rules. Boolean, default istrue
. io_exceptions
-
Throw an exception if a link can not be found. Boolean, default is
true
.
Constants
- MAX_REDIRECTS
- RE_AT_IMPORT_RULE
Initial parsing
- STRIP_CSS_COMMENTS_RX
- STRIP_HTML_COMMENTS_RX
- USER_AGENT
Attributes
Array of CSS files that have been loaded.
Public Class Methods
# File lib/css_parser/parser.rb, line 36 def initialize(options = {}) @options = {:absolute_paths => false, :import => true, :io_exceptions => true}.merge(options) # array of RuleSets @rules = [] @redirect_count = nil @loaded_uris = [] # unprocessed blocks of CSS @blocks = [] reset! end
Public Instance Methods
Add a raw block of CSS.
In order to follow +@import+ rules you must supply either a :base_dir
or :base_uri
option.
Use the :media_types
option to set the media type(s) for this block. Takes an array of symbols.
Use the :only_media_types
option to selectively follow +@import+ rules. Takes an array of symbols.
Example¶ ↑
css = <<-EOT body { font-size: 10pt } p { margin: 0px; } @media screen, print { body { line-height: 1.2 } } EOT parser = CssParser::Parser.new parser.add_block!(css)
# File lib/css_parser/parser.rb, line 115 def add_block!(block, options = {}) options = {:base_uri => nil, :base_dir => nil, :charset => nil, :media_types => :all, :only_media_types => :all}.merge(options) options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)} options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)} block = cleanup_block(block) if options[:base_uri] and @options[:absolute_paths] block = CssParser.convert_uris(block, options[:base_uri]) end # Load @imported CSS if @options[:import] 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
.
media_types
can be a symbol or an array of symbols.
# File lib/css_parser/parser.rb, line 160 def add_rule!(selectors, declarations, media_types = :all) rule_set = RuleSet.new(selectors, declarations) add_rule_set!(rule_set, media_types) end
Add a CssParser
RuleSet
object.
media_types
can be a symbol or an array of symbols.
# File lib/css_parser/parser.rb, line 168 def add_rule_set!(ruleset, media_types = :all) raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet) media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)} @rules << {:media_types => media_types, :rules => ruleset} end
Iterate through RuleSet
objects.
media_types
can be a symbol or an array of symbols.
# File lib/css_parser/parser.rb, line 179 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], 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
.
# File lib/css_parser/parser.rb, line 194 def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types each_rule_set(all_media_types) do |rule_set, media_types| rule_set.each_selector(options) do |selectors, declarations, specificity| yield selectors, declarations, specificity, media_types end end end
Get declarations by selector.
media_types
are optional, and can be a symbol or an array of symbols. The default value is :all
.
Examples¶ ↑
find_by_selector('#content') => 'font-size: 13px; line-height: 1.2;' find_by_selector('#content', [:screen, :handheld]) => 'font-size: 13px; line-height: 1.2;' find_by_selector('#content', :print) => 'font-size: 11pt; line-height: 1.2;'
Returns an array of declarations.
# File lib/css_parser/parser.rb, line 69 def find_by_selector(selector, media_types = :all) out = [] each_selector(media_types) do |sel, dec, spec| out << dec if sel.strip == selector.strip end out end
Finds the rule sets that match the given selectors
# File lib/css_parser/parser.rb, line 79 def find_rule_sets(selectors, media_types = :all) rule_sets = [] selectors.each do |selector| selector.gsub!(/\s+/, ' ') selector.strip! 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 rule_sets end
Load a local CSS file.
# File lib/css_parser/parser.rb, line 388 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) 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.
# File lib/css_parser/parser.rb, line 400 def load_string!(src, base_dir = nil, media_types = :all) add_block!(src, {:media_types => media_types, :base_dir => base_dir}) end
Load a remote CSS file.
You can also pass in file://test.css
See add_block! for options.
Deprecated: originally accepted three params: `uri`, `base_uri` and `media_types`
# File lib/css_parser/parser.rb, line 361 def load_uri!(uri, options = {}, deprecated = nil) uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme #base_uri = nil, media_types = :all, options = {} opts = {:base_uri => nil, :media_types => :all} if options.is_a? Hash 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 opts[:base_uri] = uri if opts[:base_uri].nil? src, charset = read_remote_file(uri) if src add_block!(src, opts) end end
A hash of { :media_query => rule_sets }
# File lib/css_parser/parser.rb, line 232 def rules_by_media_query rules_by_media = {} @rules.each do |block| block[:media_types].each do |mt| unless rules_by_media.has_key?(mt) rules_by_media[mt] = [] end rules_by_media[mt] << block[:rules] end end rules_by_media end
Output all CSS rules as a single stylesheet.
# File lib/css_parser/parser.rb, line 203 def to_s(media_types = :all) out = '' 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
Protected Instance Methods
Check that a path hasn't been loaded already
Raises a CircularReferenceError
exception if io_exceptions are on, otherwise returns true/false.
# File lib/css_parser/parser.rb, line 411 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] return false else @loaded_uris << path return true end end