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,srcandurl(''). Boolean, default isfalse. import-
Follow
@importrules. 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
Source
# File lib/css_parser/parser.rb, line 37 def initialize(options = {}) @options = { absolute_paths: false, import: true, io_exceptions: true, rule_set_exceptions: true, capture_offsets: false, user_agent: USER_AGENT }.merge(options) # array of RuleSets @rules = [] @redirect_count = nil @loaded_uris = [] # unprocessed blocks of CSS @blocks = [] reset! end
Public Instance Methods
Source
# File lib/css_parser/parser.rb, line 120 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, options) 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.empty? or !(media_types & options[:only_media_types]).empty? import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip import_options = {media_types: media_types} import_options[:capture_offsets] = true if options[:capture_offsets] if options[:base_uri] import_uri = Addressable::URI.parse(options[:base_uri].to_s) + Addressable::URI.parse(import_path) import_options[:base_uri] = options[:base_uri] load_uri!(import_uri, import_options) elsif options[:base_dir] import_options[:base_dir] = options[:base_dir] load_file!(import_path, import_options) end end end # Remove @import declarations block = ignore_pattern(block, RE_AT_IMPORT_RULE, options) parse_block_into_rule_sets!(block, options) end
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)
Source
# File lib/css_parser/parser.rb, line 170 def add_rule!(selectors, declarations, media_types = :all) rule_set = RuleSet.new(selectors, declarations) add_rule_set!(rule_set, media_types) rescue ArgumentError => e raise e if @options[:rule_set_exceptions] end
Add a CSS rule by setting the selectors, declarations and media_types.
media_types can be a symbol or an array of symbols.
Source
# File lib/css_parser/parser.rb, line 190 def add_rule_set!(ruleset, media_types = :all) raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet) media_types = [media_types] unless media_types.is_a?(Array) media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) } @rules << {media_types: media_types, rules: ruleset} end
Source
# File lib/css_parser/parser.rb, line 182 def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all) rule_set = OffsetAwareRuleSet.new(filename, offset, selectors, declarations) add_rule_set!(rule_set, media_types) end
Add a CSS rule by setting the selectors, declarations, filename, offset and media_types.
filename can be a string or uri pointing to the file or url location. offset should be Range object representing the start and end byte locations where the rule was found in the file. media_types can be a symbol or an array of symbols.
Source
# File lib/css_parser/parser.rb, line 215 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 RuleSet objects.
media_types can be a symbol or an array of symbols.
Source
# File lib/css_parser/parser.rb, line 251 def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types return to_enum(__method__, all_media_types, options) unless block_given? 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
Iterate through CSS selectors.
media_types can be a symbol or an array of symbols. See RuleSet#each_selector for options.
Source
# File lib/css_parser/parser.rb, line 75 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
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.
Source
# File lib/css_parser/parser.rb, line 85 def find_rule_sets(selectors, media_types = :all) rule_sets = [] selectors.each do |selector| selector = selector.gsub(/\s+/, ' ').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
Finds the rule sets that match the given selectors
Source
# File lib/css_parser/parser.rb, line 475 def load_file!(file_name, options = {}, deprecated = nil) opts = {base_dir: nil, media_types: :all} if options.is_a? Hash opts.merge!(options) else opts[:base_dir] = options if options.is_a? String opts[:media_types] = deprecated if deprecated end file_name = File.expand_path(file_name, opts[:base_dir]) return unless File.readable?(file_name) return unless circular_reference_check(file_name) src = File.read(file_name) opts[:filename] = file_name if opts[:capture_offsets] opts[:base_dir] = File.dirname(file_name) add_block!(src, opts) end
Load a local CSS file.
Source
# File lib/css_parser/parser.rb, line 498 def load_string!(src, options = {}, deprecated = nil) opts = {base_dir: nil, media_types: :all} if options.is_a? Hash opts.merge!(options) else opts[:base_dir] = options if options.is_a? String opts[:media_types] = deprecated if deprecated end add_block!(src, opts) end
Load a local CSS string.
Source
# File lib/css_parser/parser.rb, line 447 def load_uri!(uri, options = {}, deprecated = nil) uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme 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? # pass on the uri if we are capturing file offsets opts[:filename] = uri.to_s if opts[:capture_offsets] src, = read_remote_file(uri) # skip charset add_block!(src, opts) if src 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`
Source
# File lib/css_parser/parser.rb, line 202 def remove_rule_set!(ruleset, media_types = :all) raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet) media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) } @rules.reject! do |rule| rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s end end
Source
# File lib/css_parser/parser.rb, line 293 def rules_by_media_query rules_by_media = {} @rules.each do |block| block[:media_types].each do |mt| unless rules_by_media.key?(mt) rules_by_media[mt] = [] end rules_by_media[mt] << block[:rules] end end rules_by_media end
A hash of { :media_query => rule_sets }
Source
# File lib/css_parser/parser.rb, line 227 def to_h(which_media = :all) out = {} styles_by_media_types = {} each_selector(which_media) 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| ms = {} media_styles.each do |media_style| ms = css_node_to_h(ms, media_style[0], media_style[1]) end out[media_type.to_s] = ms end out end
Output all CSS rules as a Hash
Source
# File lib/css_parser/parser.rb, line 262 def to_s(which_media = :all) out = [] styles_by_media_types = {} each_selector(which_media) 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} {" if media_block media_styles.each do |media_style| if media_block out.push(" #{media_style[0]} {\n #{media_style[1]}\n }") else out.push("#{media_style[0]} {\n#{media_style[1]}\n}") end end out << '}' if media_block end out << '' out.join("\n") end
Output all CSS rules as a single stylesheet.
Protected Instance Methods
Source
# File lib/css_parser/parser.rb, line 517 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] false else @loaded_uris << path true end end
Check that a path hasnโt been loaded already
Raises a CircularReferenceError exception if io_exceptions are on, otherwise returns true/false.
Source
# File lib/css_parser/parser.rb, line 532 def ignore_pattern(css, regex, options) # if we are capturing file offsets, replace the characters with spaces to retail the original positions return css.gsub(regex) { |m| ' ' * m.length } if options[:capture_offsets] # otherwise just strip it out css.gsub(regex, '') end
Remove a pattern from a given string
Returns a string.
Private Instance Methods
Source
# File lib/css_parser/parser.rb, line 667 def css_node_to_h(hash, key, val) hash[key.strip] = '' and return hash if val.nil? lines = val.split(';') nodes = {} lines.each do |line| parts = line.split(':', 2) if parts[1] =~ /:/ nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1]) else nodes[parts[0].to_s.strip] = parts[1].to_s.strip end end hash[key.strip] = nodes hash end
recurse through nested nodes and return them as Hashes nested in passed hash