Skip to content

Commit

Permalink
Merge pull request #42 from mzsanford/media_query_support
Browse files Browse the repository at this point in the history
Media query support
  • Loading branch information
morten committed Aug 5, 2013
2 parents c86396b + 436ab9a commit 886cd14
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 36 deletions.
92 changes: 56 additions & 36 deletions lib/css_parser/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Parser

# 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
#++
Expand All @@ -38,10 +38,10 @@ def initialize(options = {})

# array of RuleSets
@rules = []


@loaded_uris = []

# unprocessed blocks of CSS
@blocks = []
reset!
Expand Down Expand Up @@ -77,7 +77,7 @@ 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
Expand Down Expand Up @@ -117,7 +117,7 @@ def add_block!(block, options = {})
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 = []
Expand All @@ -128,22 +128,22 @@ def add_block!(block, options = {})
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

Expand All @@ -169,13 +169,13 @@ def add_rule_set!(ruleset, media_types = :all)
# 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
Expand All @@ -184,23 +184,43 @@ def each_rule_set(media_types = :all) # :yields: rule_set
#
# +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 = {}
Expand All @@ -212,7 +232,7 @@ def rules_by_media_query
rules_by_media[mt] << block[:rules]
end
end

rules_by_media
end

Expand Down Expand Up @@ -246,25 +266,25 @@ def parse_block_into_rule_sets!(block, options = {}) # :nodoc:

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?
Expand Down Expand Up @@ -318,7 +338,7 @@ def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
end
end

# check for unclosed braces
# check for unclosed braces
if in_declarations > 0
add_rule!(current_selectors, current_declarations, current_media_queries)
end
Expand All @@ -343,7 +363,7 @@ def load_uri!(uri, options = {}, deprecated = nil)
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'
Expand All @@ -356,7 +376,7 @@ def load_uri!(uri, options = {}, deprecated = nil)
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)
Expand All @@ -368,18 +388,18 @@ def load_file!(file_name, base_dir = nil, media_types = :all)

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
Expand All @@ -391,15 +411,15 @@ def circular_reference_check(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, '')

Expand All @@ -416,12 +436,12 @@ def cleanup_block(block) # :nodoc:
# 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
Expand All @@ -433,7 +453,7 @@ def read_remote_file(uri) # :nodoc:
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)
Expand Down Expand Up @@ -471,7 +491,7 @@ def read_remote_file(uri) # :nodoc:
return nil, nil
end

return src, charset
return src, charset
end

private
Expand Down
5 changes: 5 additions & 0 deletions test/test_css_parser_media_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,9 @@ def test_selecting_with_all_media_types
@cp.add_rule!('body', 'color: black;', [:handheld,:tty])
assert_equal 'color: black;', @cp.find_by_selector('body', :all).join(' ')
end

def test_to_s_includes_media_queries
@cp.add_rule!('body', 'color: black;', 'aural and (device-aspect-ratio: 16/9)')
assert_equal "@media aural and (device-aspect-ratio: 16/9) {\n body {\n color: black;\n }\n}\n", @cp.to_s
end
end

0 comments on commit 886cd14

Please sign in to comment.