[DRE-commits] [ruby-css-parser] 01/11: New upstream version 1.5.0.pre2
Lucas Kanashiro
kanashiro at moszumanska.debian.org
Sun Aug 27 23:40:23 UTC 2017
This is an automated email from the git hooks/post-receive script.
kanashiro pushed a commit to branch master
in repository ruby-css-parser.
commit 4d9fe0be43d651e5b946f8df47c01e42d5c6097c
Author: Lucas Kanashiro <kanashiro at debian.org>
Date: Fri Aug 11 12:58:52 2017 -0300
New upstream version 1.5.0.pre2
---
.editorconfig | 10 +
.gitignore | 2 +
.jrubyrc | 1 +
.travis.yml | 13 +-
CHANGELOG.md | 63 ++++
Gemfile | 5 +-
Gemfile.lock | 23 +-
Readme.md => README.md | 29 +-
Rakefile | 26 ++
css_parser.gemspec | 7 +-
lib/css_parser/parser.rb | 224 ++++++++++---
lib/css_parser/regexps.rb | 166 +++++++++-
lib/css_parser/rule_set.rb | 103 ++++--
lib/css_parser/version.rb | 2 +-
test/fixtures/complex.css | 505 ++++++++++++++++++++++++++++++
test/fixtures/import-malformed.css | 35 +++
test/test_css_parser_basic.rb | 17 +-
test/test_css_parser_loading.rb | 75 ++++-
test/test_css_parser_media_types.rb | 8 +-
test/test_css_parser_misc.rb | 24 +-
test/test_css_parser_offset_capture.rb | 109 +++++++
test/test_css_parser_regexps.rb | 23 +-
test/test_helper.rb | 5 +-
test/test_merging.rb | 4 +-
test/test_rule_set.rb | 12 +-
test/test_rule_set_creating_shorthand.rb | 40 ++-
test/test_rule_set_expanding_shorthand.rb | 46 ++-
27 files changed, 1432 insertions(+), 145 deletions(-)
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..0f09989
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,10 @@
+# editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..417d5f1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/pkg/
+
diff --git a/.jrubyrc b/.jrubyrc
new file mode 100644
index 0000000..250bfe2
--- /dev/null
+++ b/.jrubyrc
@@ -0,0 +1 @@
+cext.enabled=true
diff --git a/.travis.yml b/.travis.yml
index b7ec9c1..d110dec 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,11 @@
-notifications:
- disabled: true
+language: ruby
+sudo: false
+cache: bundler
+before_install: rm Gemfile.lock
rvm:
- - 1.9.2
- 1.9.3
- - 2.0.0
- - 2.1.0
+ - 2.0.0-p647
+ - 2.1.7
+ - 2.2.3
+ - 2.3.1
- jruby
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f229f6c..9df8c5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,68 @@
## Ruby CSS Parser CHANGELOG
+## Version 1.5.0.pre2
+
+ * Extended color keywords support (https://www.w3.org/TR/css3-color/).
+ * remove_rule_set! method.
+
+### Version 1.5.0.pre
+
+ * capture_offsets feature.
+
+### Version 1.4.10
+
+ * Include uri in RemoteFileError message.
+ * Prevent to convert single declarations to their respective shorthand.
+ * Fix Ruby warnings.
+
+### Version 1.4.9
+
+ * Support for vrem, vh, vw, vmin, vmax and vm box model units.
+ * Replace obsolete calls with actual ones.
+ * Fix some Ruby warnings.
+
+### Version 1.4.8
+
+ * Allow to get CSS rules as Hash using `to_hash` method.
+ * Updates to support Ruby 1.9 and JRuby.
+ * utf-8 related update.
+
+### Version 1.4.7
+
+ * background-position shorthand fix.
+
+### Version 1.4.6
+
+ * Normalize whitespace in selectors and queries.
+ * Strip spaces from keys.
+ * More checks on ordering.
+
+### Version 1.4.5
+
+ * Maintenance release.
+
+### Version 1.4.4
+
+ * More robust redirection handling, refs #47.
+
+### Version 1.4.3
+
+ * Look for redirects, MAX_REDIRECTS set to 3, refs #36.
+ * Fix border style expanding, refs #58.
+ * load_string! described, refs #70.
+
+### Version 1.4.2
+
+ * Ship license with package, refs #69.
+
+### Version 1.4.1
+
+ * Fix background shorthands, refs #66.
+
+### Version 1.4.0
+
+ * Add support for background-size in the shorthand property @mitio
+
### Version 1.3.6
* Fix bug not setting general rules after media query @jievans.
diff --git a/Gemfile b/Gemfile
index bdbf92c..e033093 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,10 +1,13 @@
+# Keep Gemfile.lock in repo. Reason: https://grosser.it/2015/08/14/check-in-your-gemfile-lock/
+
source "https://rubygems.org"
gemspec
gem 'rake'
gem 'bump'
-gem 'test-unit', '>= 2.5.3'
+gem 'maxitest'
+gem 'public_suffix', '~> 1.4.0', platform: [:ruby_19, :jruby]
platforms :jruby do
gem 'jruby-openssl'
diff --git a/Gemfile.lock b/Gemfile.lock
index 2d6d15a..40169e1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,27 +1,30 @@
PATH
remote: .
specs:
- css_parser (1.3.6)
+ css_parser (1.5.0.pre2)
addressable
GEM
remote: https://rubygems.org/
specs:
- addressable (2.3.6)
- bouncy-castle-java (1.5.0146.1)
- bump (0.3.12)
- jruby-openssl (0.7.6.1)
- bouncy-castle-java (>= 1.5.0146.1)
- rake (0.9.2.2)
- test-unit (2.5.4)
+ addressable (2.4.0)
+ bump (0.5.3)
+ maxitest (2.4.0)
+ minitest (>= 5.0.0, < 5.11.0)
+ minitest (5.10.1)
+ public_suffix (1.4.6)
+ rake (12.0.0)
PLATFORMS
- java
ruby
DEPENDENCIES
bump
css_parser!
jruby-openssl
+ maxitest
+ public_suffix (~> 1.4.0)
rake
- test-unit (>= 2.5.3)
+
+BUNDLED WITH
+ 1.14.6
diff --git a/Readme.md b/README.md
similarity index 53%
rename from Readme.md
rename to README.md
index 2c74612..5078793 100644
--- a/Readme.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Ruby CSS Parser
+# Ruby CSS Parser [](https://travis-ci.org/premailer/css_parser) [](https://badge.fury.io/rb/css_parser)
Load, parse and cascade CSS rule sets in Ruby.
@@ -21,11 +21,15 @@ parser = CssParser::Parser.new
parser.load_uri!('file://home/user/styles/style.css')
# load a remote file, setting the base_uri and media_types
-parser.load_uri!('../style.css', {:base_uri => 'http://example.com/styles/inc/', :media_types => [:screen, :handheld])
+parser.load_uri!('../style.css', {base_uri: 'http://example.com/styles/inc/', media_types: [:screen, :handheld]})
# load a local file, setting the base_dir and media_types
parser.load_file!('print.css', '~/styles/', :print)
+# load a string
+parser = CssParser::Parser.new
+parser.load_string! 'a { color: hotpink; }'
+
# lookup a rule by a selector
parser.find_by_selector('#content')
#=> 'font-size: 13px; line-height: 1.2;'
@@ -49,6 +53,22 @@ parser.add_block!(css)
parser.to_s
=> #content { font-size: 13px; line-height: 1.2; }
body { margin: 0 1em; }
+
+# capturing byte offsets within a file
+parser.load_uri!('../style.css', {base_uri: 'http://example.com/styles/inc/', capture_offsets: true)
+content_rule = parser.find_rule_sets(['#content']).first
+content_rule.filename
+#=> 'http://example.com/styles/styles.css'
+content_rule.offset
+#=> 10703..10752
+
+# capturing byte offsets within a string
+parser.load_string!('a { color: hotpink; }', {filename: 'index.html', capture_offsets: true)
+content_rule = parser.find_rule_sets(['a']).first
+content_rule.filename
+#=> 'index.html'
+content_rule.offset
+#=> 0..21
```
# Testing
@@ -66,9 +86,6 @@ By Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2007-11.
License: MIT
-Thanks to [all the wonderful contributors](http://github.com/alexdunae/css_parser/contributors) for their updates.
+Thanks to [all the wonderful contributors](http://github.com/premailer/css_parser/contributors) for their updates.
Made on Vancouver Island.
-
-[](https://travis-ci.org/alexdunae/css_parser)
-
diff --git a/Rakefile b/Rakefile
index c9d7b1b..0db3ed6 100644
--- a/Rakefile
+++ b/Rakefile
@@ -7,3 +7,29 @@ desc 'Run the unit tests.'
Rake::TestTask.new(:default) do |test|
test.verbose = true
end
+
+desc 'Run a performance evaluation.'
+task :benchmark do
+ require 'benchmark'
+ require 'css_parser'
+
+ base_dir = File.dirname(__FILE__) + '/test/fixtures'
+
+ # parse the import1 file to benchmark file loading
+ time = Benchmark.measure do
+ 10000.times do
+ parser = CssParser::Parser.new
+ parser.load_file!('import1.css', base_dir)
+ end
+ end
+ puts "Parsing 'import1.css' 10 000 times took #{time.real.round(4)} seconds"
+
+ # parse the import1 file to benchmark rule parsing
+ time = Benchmark.measure do
+ 1000.times do
+ parser = CssParser::Parser.new
+ parser.load_file!('complex.css', base_dir)
+ end
+ end
+ puts "Parsing 'complex.css' 1 000 times took #{time.real.round(4)} seconds"
+end
diff --git a/css_parser.gemspec b/css_parser.gemspec
index eb2067d..32927fa 100644
--- a/css_parser.gemspec
+++ b/css_parser.gemspec
@@ -1,14 +1,13 @@
-$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
name = "css_parser"
-require "#{name}/version"
+require "./lib/#{name}/version"
-Gem::Specification.new name, CssParser::VERSION.dup do |s|
+Gem::Specification.new name, CssParser::VERSION do |s|
s.summary = "Ruby CSS parser."
s.description = "A set of classes for parsing CSS in Ruby."
s.email = "code at dunae.ca"
s.homepage = "https://github.com/premailer/#{name}"
s.author = "Alex Dunae"
s.add_runtime_dependency 'addressable'
- s.files = Dir.glob("lib/**/*")
+ s.files = Dir.glob("lib/**/*") + ["MIT-LICENSE"]
s.license = "MIT"
end
diff --git a/lib/css_parser/parser.rb b/lib/css_parser/parser.rb
index d6207a0..4312c87 100644
--- a/lib/css_parser/parser.rb
+++ b/lib/css_parser/parser.rb
@@ -14,15 +14,17 @@ module CssParser
# [<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/#{CssParser::VERSION} (http://github.com/alexdunae/css_parser)"
+ USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
# Initial parsing
- RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\]\(\))]*)\)?[;\n]?/
+ RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\]\(\)]*)\)?[;\n]?/
- # Array of CSS files that have been loaded.
+ MAX_REDIRECTS = 3
+
+ # Array of CSS files that have been loaded.
attr_reader :loaded_uris
#--
@@ -34,11 +36,13 @@ module CssParser
def initialize(options = {})
@options = {:absolute_paths => false,
:import => true,
- :io_exceptions => true}.merge(options)
+ :io_exceptions => true,
+ :capture_offsets => false}.merge(options)
# array of RuleSets
@rules = []
+ @redirect_count = nil
@loaded_uris = []
@@ -77,6 +81,8 @@ module CssParser
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
@@ -112,7 +118,7 @@ module CssParser
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)
+ block = cleanup_block(block, options)
if options[:base_uri] and @options[:absolute_paths]
block = CssParser.convert_uris(block, options[:base_uri])
@@ -134,17 +140,22 @@ module CssParser
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)
- load_uri!(import_uri, options[:base_uri], media_types)
+ import_options[:base_uri] = options[:base_uri]
+ load_uri!(import_uri, import_options)
elsif options[:base_dir]
- load_file!(import_path, options[:base_dir], media_types)
+ import_options[:base_dir] = options[:base_dir]
+ load_file!(import_path, import_options)
end
end
end
# Remove @import declarations
- block.gsub!(RE_AT_IMPORT_RULE, '')
+ block = ignore_pattern(block, RE_AT_IMPORT_RULE, options)
parse_block_into_rule_sets!(block, options)
end
@@ -157,6 +168,16 @@ module CssParser
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.
+ 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 CssParser RuleSet object.
#
# +media_types+ can be a symbol or an array of symbols.
@@ -168,6 +189,19 @@ module CssParser
@rules << {:media_types => media_types, :rules => ruleset}
end
+ # Remove a CssParser RuleSet object.
+ #
+ # +media_types+ can be a symbol or an array of symbols.
+ def remove_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.reject! do |rule|
+ rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
+ end
+ end
+
# Iterate through RuleSet objects.
#
# +media_types+ can be a symbol or an array of symbols.
@@ -182,12 +216,33 @@ module CssParser
end
end
+ # Output all CSS rules as a Hash
+ 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
+
# 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, media_types
- each_rule_set(media_types) do |rule_set, media_types|
+ 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
@@ -195,10 +250,10 @@ module CssParser
end
# Output all CSS rules as a single stylesheet.
- def to_s(media_types = :all)
+ def to_s(which_media = :all)
out = ''
styles_by_media_types = {}
- each_selector(media_types) do |selectors, declarations, specificity, 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]
@@ -263,9 +318,16 @@ module CssParser
current_media_query = ''
current_declarations = ''
- block.scan(/([\\]?[{}\s"]|(.[^\s"{}\\]*))/).each do |matches|
+ # once we are in a rule, we will use this to store where we started if we are capturing offsets
+ rule_start = nil
+ offset = nil
+
+ block.scan(/(([\\]{2,})|([\\]?[{}\s"])|(.[^\s"{}\\]*))/) do |matches|
token = matches[0]
+ # save the regex offset so that we know where in the file we are
+ offset = Regexp.last_match.offset(0) if options[:capture_offsets]
+
if token =~ /\A"/ # found un-escaped double quote
in_string = !in_string
end
@@ -277,7 +339,7 @@ module CssParser
next
end
- if token =~ /\{/
+ if token =~ /\{/ and not in_string
in_declarations += 1
next
end
@@ -290,11 +352,18 @@ module CssParser
in_declarations -= 1
unless current_declarations.strip.empty?
- add_rule!(current_selectors, current_declarations, current_media_queries)
+ if options[:capture_offsets]
+ add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
+ else
+ add_rule!(current_selectors, current_declarations, current_media_queries)
+ end
end
current_selectors = ''
current_declarations = ''
+
+ # restart our search for selectors and declarations
+ rule_start = nil if options[:capture_offsets]
end
elsif token =~ /@media/i
# found '@media', reset current media_types
@@ -330,11 +399,14 @@ module CssParser
end
else
if token =~ /\{/ and not in_string
- current_selectors.gsub!(/^[\s]*/, '')
- current_selectors.gsub!(/[\s]*$/, '')
+ current_selectors.strip!
in_declarations += 1
else
+ # if we are in a selector, add the token to the current selectors
current_selectors += token
+
+ # mark this as the beginning of the selector unless we have already marked it
+ rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
end
end
end
@@ -342,7 +414,11 @@ module CssParser
# check for unclosed braces
if in_declarations > 0
- add_rule!(current_selectors, current_declarations, current_media_queries)
+ if options[:capture_offsets]
+ add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
+ else
+ add_rule!(current_selectors, current_declarations, current_media_queries)
+ end
end
end
@@ -355,7 +431,6 @@ module CssParser
# Deprecated: originally accepted three params: `uri`, `base_uri` and `media_types`
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}
@@ -373,27 +448,50 @@ module CssParser
opts[:base_uri] = uri if opts[:base_uri].nil?
- src, charset = read_remote_file(uri)
+ # 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
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)
+ 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 = IO.read(file_name)
- base_dir = File.dirname(file_name)
- add_block!(src, {:media_types => media_types, :base_dir => base_dir})
+ opts[:filename] = file_name if opts[:capture_offsets]
+ opts[:base_dir] = File.dirname(file_name)
+
+ add_block!(src, opts)
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})
+ 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
@@ -414,21 +512,33 @@ module CssParser
end
end
+ # Remove a pattern from a given string
+ #
+ # Returns a string.
+ 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
+
# Strip comments and clean up blank lines from a block of CSS.
#
# Returns a string.
- def cleanup_block(block) # :nodoc:
+ def cleanup_block(block, options = {}) # :nodoc:
# Strip CSS comments
- block.gsub!(STRIP_CSS_COMMENTS_RX, '')
+ utf8_block = block.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ' ')
+ utf8_block = ignore_pattern(utf8_block, STRIP_CSS_COMMENTS_RX, options)
# Strip HTML comments - they shouldn't really be in here but
# some people are just crazy...
- block.gsub!(STRIP_HTML_COMMENTS_RX, '')
+ utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
# Strip lines containing just whitespace
- block.gsub!(/^\s+$/, "")
+ utf8_block.gsub!(/^\s+$/, "") unless options[:capture_offsets]
- block
+ utf8_block
end
# Download a file into a string.
@@ -438,7 +548,21 @@ module CssParser
# 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)
+ if @redirect_count.nil?
+ @redirect_count = 0
+ else
+ @redirect_count += 1
+ end
+
+ unless circular_reference_check(uri.to_s)
+ @redirect_count = nil
+ return nil, nil
+ end
+
+ if @redirect_count > MAX_REDIRECTS
+ @redirect_count = nil
+ return nil, nil
+ end
src = '', charset = nil
@@ -447,8 +571,11 @@ module CssParser
if uri.scheme == 'file'
# local file
- fh = open(uri.path, 'rb')
+ path = uri.path
+ path.gsub!(/^\//, '') if Gem.win_platform?
+ fh = open(path, 'rb')
src = fh.read
+ charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
fh.close
else
# remote file
@@ -463,11 +590,16 @@ module CssParser
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'
+ charset = res.respond_to?(:charset) ? res.encoding : 'utf-8'
if res.code.to_i >= 400
- raise RemoteFileError if @options[:io_exceptions]
+ @redirect_count = nil
+ raise RemoteFileError.new(uri.to_s) if @options[:io_exceptions]
return '', nil
+ elsif res.code.to_i >= 300 and res.code.to_i < 400
+ if res['Location'] != nil
+ return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
+ end
end
case res['content-encoding']
@@ -489,10 +621,12 @@ module CssParser
end
end
rescue
- raise RemoteFileError if @options[:io_exceptions]
+ @redirect_count = nil
+ raise RemoteFileError.new(uri.to_s)if @options[:io_exceptions]
return nil, nil
end
+ @redirect_count = nil
return src, charset
end
@@ -513,5 +647,23 @@ module CssParser
@css_rules = []
@css_warnings = []
end
+
+ # recurse through nested nodes and return them as Hashes nested in
+ # passed hash
+ 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
end
end
diff --git a/lib/css_parser/regexps.rb b/lib/css_parser/regexps.rb
index 68668db..1986c39 100644
--- a/lib/css_parser/regexps.rb
+++ b/lib/css_parser/regexps.rb
@@ -23,7 +23,7 @@ module CssParser
RE_GRADIENT = /[-a-z]*gradient\([-a-z0-9 .,#%()]*\)/im
# Initial parsing
- RE_AT_IMPORT_RULE = /\@import[\s]+(url\()?["''"]?(.[^'"\s"']*)["''"]?\)?([\w\s\,^\])]*)\)?;?/
+ RE_AT_IMPORT_RULE = /\@import\s+(url\()?["']?(.[^'"\s]*)["']?\)?([\w\s\,^\])]*)\)?;?/
#--
#RE_AT_MEDIA_RULE = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
@@ -44,9 +44,10 @@ module CssParser
STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
# Special units
- BOX_MODEL_UNITS_RX = /(auto|inherit|0|([\-]*([0-9]+|[0-9]*\.[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%)))([\s;]|\Z)/imx
+ BOX_MODEL_UNITS_RX = /(auto|inherit|0|([\-]*([0-9]+|[0-9]*\.[0-9]+)(rem|vw|vh|vm|vmin|vmax|e[mx]+|px|[cm]+m|p[tc+]|in|\%)))([\s;]|\Z)/imx
RE_LENGTH_OR_PERCENTAGE = Regexp.new('([\-]*(([0-9]*\.[0-9]+)|[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%))', Regexp::IGNORECASE)
RE_BACKGROUND_POSITION = Regexp.new("((((#{RE_LENGTH_OR_PERCENTAGE})|left|center|right|top|bottom)[\s]*){1,2})", Regexp::IGNORECASE | Regexp::EXTENDED)
+ RE_BACKGROUND_SIZE = Regexp.new("\\s*/\\s*((((#{RE_LENGTH_OR_PERCENTAGE})|auto|cover|contain|initial|inherit)[\\s]*){1,2})", Regexp::IGNORECASE | Regexp::EXTENDED)
FONT_UNITS_RX = /(([x]+\-)*small|medium|large[r]*|auto|inherit|([0-9]+|[0-9]*\.[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%)*)/i
RE_BORDER_STYLE = /([\s]*^)?(none|hidden|dotted|dashed|solid|double|dot-dash|dot-dot-dash|wave|groove|ridge|inset|outset)([\s]*$)?/imx
RE_BORDER_UNITS = Regexp.union(BOX_MODEL_UNITS_RX, /(thin|medium|thick)/i)
@@ -82,10 +83,163 @@ module CssParser
)/ix
# Colours
- RE_COLOUR_NUMERIC = Regexp.new('((hsl|rgb)[\s]*\([\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*\))', Regexp::IGNORECASE)
- RE_COLOUR_NUMERIC_ALPHA = Regexp.new('((hsla|rgba)[\s]*\([\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*,[\s-]*[\d]+(\.[\d]+)?[%\s]*\))', Regexp::IGNORECASE)
- RE_COLOUR_HEX = /(#([0-9a-f]{6}|[0-9a-f]{3})([\s;]|$))/i
- RE_COLOUR_NAMED = /([\s]*^)?(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|transparent)([\s]*$)?/i
+ NAMED_COLOURS = %w[
+ aliceblue
+ antiquewhite
+ aqua
+ aquamarine
+ azure
+ beige
+ bisque
+ black
+ blanchedalmond
+ blue
+ blueviolet
+ brown
+ burlywood
+ cadetblue
+ chartreuse
+ chocolate
+ coral
+ cornflowerblue
+ cornsilk
+ crimson
+ cyan
+ darkblue
+ darkcyan
+ darkgoldenrod
+ darkgray
+ darkgreen
+ darkgrey
+ darkkhaki
+ darkmagenta
+ darkolivegreen
+ darkorange
+ darkorchid
+ darkred
+ darksalmon
+ darkseagreen
+ darkslateblue
+ darkslategray
+ darkslategrey
+ darkturquoise
+ darkviolet
+ deeppink
+ deepskyblue
+ dimgray
+ dimgrey
+ dodgerblue
+ firebrick
+ floralwhite
+ forestgreen
+ fuchsia
+ gainsboro
+ ghostwhite
+ gold
+ goldenrod
+ gray
+ green
+ greenyellow
+ grey
+ honeydew
+ hotpink
+ indianred
+ indigo
+ ivory
+ khaki
+ lavender
+ lavenderblush
+ lawngreen
+ lemonchiffon
+ lightblue
+ lightcoral
+ lightcyan
+ lightgoldenrodyellow
+ lightgray
+ lightgreen
+ lightgrey
+ lightpink
+ lightsalmon
+ lightseagreen
+ lightskyblue
+ lightslategray
+ lightslategrey
+ lightsteelblue
+ lightyellow
+ lime
+ limegreen
+ linen
+ magenta
+ maroon
+ mediumaquamarine
+ mediumblue
+ mediumorchid
+ mediumpurple
+ mediumseagreen
+ mediumslateblue
+ mediumspringgreen
+ mediumturquoise
+ mediumvioletred
+ midnightblue
+ mintcream
+ mistyrose
+ moccasin
+ navajowhite
+ navy
+ oldlace
+ olive
+ olivedrab
+ orange
+ orangered
+ orchid
+ palegoldenrod
+ palegreen
+ paleturquoise
+ palevioletred
+ papayawhip
+ peachpuff
+ peru
+ pink
+ plum
+ powderblue
+ purple
+ red
+ rosybrown
+ royalblue
+ saddlebrown
+ salmon
+ sandybrown
+ seagreen
+ seashell
+ sienna
+ silver
+ skyblue
+ slateblue
+ slategray
+ slategrey
+ snow
+ springgreen
+ steelblue
+ tan
+ teal
+ thistle
+ tomato
+ turquoise
+ violet
+ wheat
+ white
+ whitesmoke
+ yellow
+ yellowgreen
+
+ transparent
+ inherit
+ currentColor
+ ]
+ RE_COLOUR_NUMERIC = /\b(hsl|rgb)\s*\(-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?\)/i
+ RE_COLOUR_NUMERIC_ALPHA = /\b(hsla|rgba)\s*\(-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?\)/i
+ RE_COLOUR_HEX = /\s*#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/
+ RE_COLOUR_NAMED = /\s*\b(#{NAMED_COLOURS.join('|')})\b/i
RE_COLOUR = Regexp.union(RE_COLOUR_NUMERIC, RE_COLOUR_NUMERIC_ALPHA, RE_COLOUR_HEX, RE_COLOUR_NAMED)
# :startdoc:
end
diff --git a/lib/css_parser/rule_set.rb b/lib/css_parser/rule_set.rb
index 199cd2a..e06c46a 100644
--- a/lib/css_parser/rule_set.rb
+++ b/lib/css_parser/rule_set.rb
@@ -4,12 +4,12 @@ module CssParser
RE_ELEMENTS_AND_PSEUDO_ELEMENTS = /((^|[\s\+\>]+)[\w]+|\:(first\-line|first\-letter|before|after))/i
RE_NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES = /(\.[\w]+)|(\[[\w]+)|(\:(link|first\-child|lang))/i
- BACKGROUND_PROPERTIES = ['background-color', 'background-image', 'background-repeat', 'background-position', 'background-attachment']
+ BACKGROUND_PROPERTIES = ['background-color', 'background-image', 'background-repeat', 'background-position', 'background-size', 'background-attachment']
LIST_STYLE_PROPERTIES = ['list-style-type', 'list-style-position', 'list-style-image']
# Array of selector strings.
attr_reader :selectors
-
+
# Integer with the specificity to use for this RuleSet.
attr_accessor :specificity
@@ -55,7 +55,7 @@ module CssParser
@declarations.delete(property)
return
end
-
+
value.gsub!(/;\Z/, '')
is_important = !value.gsub!(CssParser::IMPORTANT_IN_PROPERTY_RX, '').nil?
property = property.downcase.strip
@@ -149,11 +149,18 @@ module CssParser
split_declaration('background', 'background-attachment', value.slice!(CssParser::RE_SCROLL_FIXED))
split_declaration('background', 'background-repeat', value.slice!(CssParser::RE_REPEAT))
split_declaration('background', 'background-color', value.slice!(CssParser::RE_COLOUR))
+ split_declaration('background', 'background-size', extract_background_size_from(value))
split_declaration('background', 'background-position', value.slice(CssParser::RE_BACKGROUND_POSITION))
@declarations.delete('background')
end
+ def extract_background_size_from(value)
+ size = value.slice!(CssParser::RE_BACKGROUND_SIZE)
+
+ size.sub(/^\s*\/\s*/, '') if size
+ end
+
# Split shorthand border declarations (e.g. <tt>border: 1px red;</tt>)
# Additional splitting happens in expand_dimensions_shorthand!
def expand_border_shorthand! # :nodoc:
@@ -166,7 +173,7 @@ module CssParser
split_declaration(k, "#{k}-color", value.slice!(CssParser::RE_COLOUR))
split_declaration(k, "#{k}-style", value.slice!(CssParser::RE_BORDER_STYLE))
- @declarations.delete(k)
+ @declarations.delete(k)
end
end
@@ -175,22 +182,22 @@ module CssParser
def expand_dimensions_shorthand! # :nodoc:
{'margin' => 'margin-%s',
'padding' => 'padding-%s',
- 'border-color' => 'border-%s-color',
- 'border-style' => 'border-%s-style',
+ 'border-color' => 'border-%s-color',
+ 'border-style' => 'border-%s-style',
'border-width' => 'border-%s-width'}.each do |property, expanded|
next unless @declarations.has_key?(property)
-
+
value = @declarations[property][:value]
# RGB and HSL values in borders are the only units that can have spaces (within params).
- # We cheat a bit here by stripping spaces after commas in RGB and HSL values so that we
+ # We cheat a bit here by stripping spaces after commas in RGB and HSL values so that we
# can split easily on spaces.
#
# TODO: rgba, hsl, hsla
- value.gsub!(RE_COLOUR) { |c| c.gsub(/[\s]+/, '') }
+ value.gsub!(RE_COLOUR) { |c| c.gsub(/(\s*\,\s*)/, ',') }
- matches = value.strip.split(/[\s]+/)
+ matches = value.strip.split(/\s+/)
t, r, b, l = nil
@@ -310,14 +317,19 @@ module CssParser
# Combine several properties into a shorthand one
def create_shorthand_properties! properties, shorthand_property # :nodoc:
values = []
+ properties_to_delete = []
properties.each do |property|
- if @declarations.has_key?(property) and not @declarations[property][:is_important]
+ if @declarations.has_key?(property) and not @declarations[property][:is_important]
values << @declarations[property][:value]
- @declarations.delete(property)
- end
- end
+ properties_to_delete << property
+ end
+ end
+
+ if values.length > 1
+ properties_to_delete.each do |property|
+ @declarations.delete(property)
+ end
- unless values.empty?
@declarations[shorthand_property] = {:value => values.join(' ')}
end
end
@@ -327,16 +339,28 @@ module CssParser
#
# Leaves properties declared !important alone.
def create_background_shorthand! # :nodoc:
+ # When we have a background-size property we must separate it and distinguish it from
+ # background-position by preceeding it with a backslash. In this case we also need to
+ # have a background-position property, so we set it if it's missing.
+ # http://www.w3schools.com/cssref/css3_pr_background.asp
+ if @declarations.has_key?('background-size') and not @declarations['background-size'][:is_important]
+ unless @declarations.has_key?('background-position')
+ @declarations['background-position'] = {:value => '0% 0%'}
+ end
+
+ @declarations['background-size'][:value] = "/ #{@declarations['background-size'][:value]}"
+ end
+
create_shorthand_properties! BACKGROUND_PROPERTIES, 'background'
end
-
+
# Combine border-color, border-style and border-width into border
# Should be run after create_dimensions_shorthand!
#
# TODO: this is extremely similar to create_background_shorthand! and should be combined
def create_border_shorthand! # :nodoc:
values = []
-
+
['border-width', 'border-style', 'border-color'].each do |property|
if @declarations.has_key?(property) and not @declarations[property][:is_important]
# can't merge if any value contains a space (i.e. has multiple values)
@@ -354,16 +378,16 @@ module CssParser
@declarations['border'] = {:value => values.join(' ')}
end
end
-
- # Looks for long format CSS dimensional properties (margin, padding, border-color, border-style and border-width)
+
+ # Looks for long format CSS dimensional properties (margin, padding, border-color, border-style and border-width)
# and converts them into shorthand CSS properties.
def create_dimensions_shorthand! # :nodoc:
directions = ['top', 'right', 'bottom', 'left']
{'margin' => 'margin-%s',
'padding' => 'padding-%s',
- 'border-color' => 'border-%s-color',
- 'border-style' => 'border-%s-style',
+ 'border-color' => 'border-%s-color',
+ 'border-style' => 'border-%s-style',
'border-width' => 'border-%s-width'}.each do |property, expanded|
top, right, bottom, left = ['top', 'right', 'bottom', 'left'].map { |side| expanded % side }
@@ -377,7 +401,7 @@ module CssParser
directions.each { |d| values[d.to_sym] = @declarations[expanded % d][:value].downcase.strip }
if values[:left] == values[:right]
- if values[:top] == values[:bottom]
+ if values[:top] == values[:bottom]
if values[:top] == values[:left] # All four sides are equal
new_value = values[:top]
else # Top and bottom are equal, left and right are equal
@@ -400,8 +424,8 @@ module CssParser
end
- # Looks for long format CSS font properties (e.g. <tt>font-weight</tt>) and
- # tries to convert them into a shorthand CSS <tt>font</tt> property. All
+ # Looks for long format CSS font properties (e.g. <tt>font-weight</tt>) and
+ # tries to convert them into a shorthand CSS <tt>font</tt> property. All
# font properties must be present in order to create a shorthand declaration.
def create_font_shorthand! # :nodoc:
['font-style', 'font-variant', 'font-weight', 'font-size',
@@ -449,17 +473,17 @@ module CssParser
if @declarations.has_key?(dest)
#puts "dest #{dest} already exists"
-
- if @declarations[dest][:order] > @declarations[src][:order]
- #puts "skipping #{dest}:#{v} due to order "
+
+ if @declarations[src][:order].nil? || (!@declarations[dest][:order].nil? && @declarations[dest][:order] > @declarations[src][:order])
+ #puts "skipping #{dest}:#{v} due to order "
return
else
- @declarations[dest] = {}
+ @declarations[dest] = {}
end
end
- @declarations[dest] = @declarations[src].merge({:value => v.to_s.strip})
+ @declarations[dest] = @declarations[src].merge({:value => v.to_s.strip})
end
-
+
def parse_declarations!(block) # :nodoc:
@declarations = {}
@@ -474,7 +498,7 @@ module CssParser
continuation = decs + ';'
elsif matches = decs.match(/(.[^:]*)\s*:\s*(.+)(;?\s*\Z)/i)
- property, value, end_of_declaration = matches.captures
+ property, value, = matches.captures # skip end_of_declaration
add_declaration!(property, value)
continuation = ''
@@ -486,7 +510,22 @@ module CssParser
# TODO: way too simplistic
#++
def parse_selectors!(selectors) # :nodoc:
- @selectors = selectors.split(',').map { |s| s.strip }
+ @selectors = selectors.split(',').map { |s| s.gsub(/\s+/, ' ').strip }
+ end
+ end
+
+ class OffsetAwareRuleSet < RuleSet
+
+ # File offset range
+ attr_reader :offset
+
+ # the local or remote location
+ attr_accessor :filename
+
+ def initialize(filename, offset, selectors, block, specificity = nil)
+ super(selectors, block, specificity)
+ @offset = offset
+ @filename = filename
end
end
end
diff --git a/lib/css_parser/version.rb b/lib/css_parser/version.rb
index 08e2dbd..3514ca5 100644
--- a/lib/css_parser/version.rb
+++ b/lib/css_parser/version.rb
@@ -1,3 +1,3 @@
module CssParser
- VERSION = "1.3.6".freeze
+ VERSION = "1.5.0.pre2".freeze
end
diff --git a/test/fixtures/complex.css b/test/fixtures/complex.css
new file mode 100644
index 0000000..65764ea
--- /dev/null
+++ b/test/fixtures/complex.css
@@ -0,0 +1,505 @@
+/*
+Fonts:
+ font-family:'Caslon 540 LT W01 Italic';
+ font-family:'Caslon 540 LT W01 Roman';
+ font-family:'Univers LT W01 53 Extended';
+ font-family:'Univers LT W01 57 Condensed';
+ font-family:'Univers LT W01 65 Bold';
+*/
+
+
+/*!
+ http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;}
+/* HTML5 display-role reset for older browsers */
+article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block;}
+body{line-height:1;}
+ol,ul{list-style:none;}
+blockquote,q{quotes:none;}
+blockquote:before,blockquote:after,
+q:before,q:after{content:'';content:none;}
+table{border-collapse:collapse;border-spacing:0;}
+
+/*!
+ * Copyright (c) 2008, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ * version: 2.6.0
+*/
+body{font:13px/1.231 helvetica,arial,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
+
+body {
+ font: normal 11px/20px sans-serif;
+ color: #3C3C46;
+ background: #FFF none;
+
+}
+
+
+.debug #page {background-image:url("grid.png?1");}
+
+.debug img { opacity: .5;}
+.debug .cyclenav, .debug .cycleprev, .debug .services li { background-color: rgba(100,100,100,0.5);}
+
+/** Frame **/
+body:after, #page:after, .features:after, section:after, #study_viewer:after, #study:after,#study .gallerywrap:after, footer:after, #name_case_converter:after, footer div:after, form .field:after{clear:both;display:block;visibility:hidden;overflow:hidden;height:0;content:"\0020";}
+
+#container {
+ margin: 0 auto;
+ background: #FFF none;
+}
+
+#page {
+ width: 940px;
+ margin: 0 auto;
+}
+
+
+
+
+header{width:940px;margin:60px auto;background:#fff url("header_bg.png") 0 50% repeat-x;}
+header li{display:block;float:left;width:180px;height:10px;padding:50px 0;line-height: 10px;}
+#nav{width:590px;height:110px;margin:0 auto;font:normal 10px/10px "Univers LT W01 53 Extended",sans-serif;letter-spacing:4px;text-transform:uppercase;}
+#logo,#logo a{display:block;width:110px;height:110px;margin:0 auto;padding:0;}
+#logo{padding:0 55px;}
+#logo a{background:transparent url("sprites.png") -380px 0;text-indent:-9999em;}
+#logo a:hover,#logo a:focus{background-position:-380px -109px;}
+#logo a:active{background-position:-380px -220px;}
+#nav_work{text-align:right;}
+
+
+
+header, footer, nav, section{clear:both;zoom:1;}
+em, i { font-style: italic;}
+b, strong {font-weight: bold;}
+a { color: #000; text-decoration: none;}
+img {display: block;}
+pre {
+ margin: 20px;
+ line-height: 1.5;
+}
+
+h1 {
+ margin-top: 3px;
+ margin-bottom: 17px;
+ font: normal 70px/80px "Caslon 540 LT W01 Roman", Georgia, serif;
+ text-align: center;
+
+ text-shadow: -2px 2px 1px #FFFFFF, -3px 3px 1px #DCD7D2;
+
+ /*filter: dropshadow(color=#cccccc, offx=3, offy=2);*/
+ /*filter: Shadow(Color=#cccccc,Direction=135,Strenth=1);*/
+}
+
+h2 {
+ height: 20px;
+ margin: 50px 0 23px;
+ font: normal 9px/20px "Univers LT W01 53 Extended", sans-serif;
+ background: #FFF url("dashdots.png") 0 50% repeat-x;
+ text-align: center;
+ text-transform: uppercase;
+ letter-spacing: 5px;
+}
+
+h2 span { padding: 0 30px;background: #FFF none;}
+
+h3 {
+ margin-bottom: 12px;
+ font: normal 16px/20px "Univers LT W01 57 Condensed", sans-serif;
+}
+
+
+ol, ul { margin-bottom: 20px; list-style-position: inside; font: italic 11px/20px Arial, sans-serif;}
+
+blockquote {
+ margin: 20px;
+ font: italic 11px/20px Arial, sans-serif;
+}
+
+ul {
+ list-style-type: disc;
+}
+
+li {
+ margin: 0 0 20px;
+
+}
+
+p {
+ margin-bottom: 20px;
+}
+
+
+.lede {
+ width: 820px;
+ margin: 0 auto 100px;
+ font: normal 18px/30px "Caslon 540 LT W01 Roman", Georgia, serif;
+ text-align: center;
+}
+
+.lede em, .lede i, .lede a {
+ font-family: "Garamond W01 Italic", serif;
+}
+
+a {
+ outline: none;
+ font-style: italic;
+}
+
+a:hover, a:focus {
+ text-decoration: underline;
+}
+
+#nav a, .features a, footer a { font-style: normal;}
+
+
+/* Feature boxes */
+.features {
+ clear: both;
+ display: block;
+ width: 900px;
+ margin-bottom: 50px;
+ padding: 0 20px;
+ list-style: none;
+ font-style: normal;
+ background-color: transparent !important;
+}
+
+.features a:hover, .features a:focus {
+ text-decoration: none;
+}
+
+.features li {
+ float: left;
+ width: 180px;
+ margin: 0 60px 50px 0;
+}
+
+#contact .features h3 {font-size: 16px;}
+
+#contact .features li {text-align: center;}
+
+#studies .features, #contact .features {
+ width: 940px;
+ padding: 0 60px;
+}
+#studies .features li, #contact .features li {
+ width: 220px;
+ margin-right: 80px;
+}
+#studies .lede {margin: 0 auto 50px;
+}
+
+#studies nav {
+ width: 940px;
+ margin: 0;
+}
+
+#studies .fade_l {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 40px;
+ height: 100%;
+ z-index: 1000;
+ background: transparent url("fade_l.png") repeat-y;
+}
+
+
+
+#studies .fade_r {
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 40px;
+ height: 100%;
+ z-index: 1000;
+ background: transparent url("fade_r.png") repeat-y;
+}
+
+.features h3 { margin-bottom: 3px;}
+
+.services h3 {
+ margin-left: -5px;
+ margin-bottom: 12px;
+ padding-left: 40px;
+ background: #FFF url("sprites.png") 0 -250px no-repeat;
+}
+
+
+.services .strategy h3 {
+ background-position: 0 -297px;
+}
+
+.services .id h3 {
+ padding-left: 43px;
+ background-position: 0 -347px;
+}
+
+.services .review h3 {
+ padding-left: 45px;
+ background-position: 0 -397px;
+}
+
+.services p {
+ padding: 0 7px;
+}
+
+.features p { margin-bottom:0;}
+.features .img {
+ display: block;
+ width: 228px;
+ height: 168px;
+ margin: 0 -5px 15px;
+ border: 1px solid #e4e4e4;
+}
+.features .img:active {
+background-color: #e4e4e4;
+}
+
+/*.features .img:hover img {opacity: 1;}*/
+
+.features img { margin: 4px; }
+.js .features img {opacity: 1;}
+.features .first { clear: both;}
+.features .last { margin-right: 0 !important;}
+
+.features .meta {
+ margin-bottom: 0;
+ font: italic 12px/30px "Caslon 540 LT W01 Italic", Georgia, sans-serif;
+}
+
+
+
+#contact .email { font-style: italic;}
+#content .tel a { font-style: normal;}
+#contact a:hover, #contact a:focus { text-decoration: underline;}
+
+
+footer {
+ width: 940px;
+ margin: 0 auto 0;
+ padding-bottom: 100px;
+}
+
+
+footer nav {
+ width: 100%;
+ height: 10px;
+ margin: 0 0 50px;
+ padding: 25px 0;
+ text-align: center;
+ font: normal 8px/10px "Univers LT W01 53 Extended", sans-serif;
+ letter-spacing: 3px;
+ text-transform: uppercase;
+ background: #fff url("header_bg.png") 0 50% repeat-x;
+}
+
+footer nav a {
+ display: inline;
+ padding: 0 32px;
+ text-align: center;
+
+}
+
+footer div {
+ margin: 0 auto;
+ font-size: 10px;
+ text-align: center;
+
+ color: #B3B3B3;
+}
+
+footer a { font-style: normal;}
+
+footer img {
+ display: block;
+ margin: 20px auto;
+}
+
+
+
+
+#study h1, .code h1, .inside h1 {
+ font-size: 50px;
+ line-height: 60px;
+ text-shadow: none;
+}
+
+.focus h2 {
+ height: auto;
+ margin: 0 0 12px;
+ border: 0;
+ font: normal 15px/20px "Univers LT W01 57 Condensed", sans-serif;
+ text-transform: none;
+ text-align: left;
+ letter-spacing: 0;
+ background: none;
+}
+
+.focus ul { list-style: none;}
+
+.focus li {
+ margin: 5px 0 10px;
+ font: normal 12px/15px "Univers LT W01 65 Bold", sans-serif;
+}
+
+#study { margin-bottom: 80px;}
+#study .prose, #study .focus {
+ float: left;
+}
+
+#study .prose {
+ width: 460px;
+ margin: 0 40px 0 120px;
+ font: normal 15px/20px "Caslon 540 LT W01 Roman", Georgia, serif;
+}
+
+#study .prose em, #study .prose i {
+ font-family: "Garamond W01 Italic", serif;
+}
+
+#study .focus {
+ width: 200px;
+}
+
+#study .visit {
+ margin: 20px 0;
+ font: normal 15px/20px "Univers LT W01 57 Condensed", sans-serif;
+ text-transform: lowercase;
+}
+
+#study .visit a { text-decoration:none;font-style: normal;}
+
+
+.prose a { text-decoration: underline;}
+
+
+
+.gallerywrap .gallery,.cycleprev,.cyclenext{float:left;}
+.cycleprev,.cyclenext{display:block;width:26px;height:26px;text-indent:-9999em;cursor:pointer;background:#FFF url("sprites.png") no-repeat;}
+.cycleprev{margin-left:14px;margin-right:80px;background-position:-40px -60px;}
+.cycleprev:hover,.cycleprev:focus{background-position:-70px -60px;}
+.cyclenext{margin-right:14px;margin-left:80px;background-position:-100px -60px;}
+.cyclenext:hover,.cyclenext:focus{background-position:-130px -60px;}
+
+
+.cyclenav{width:700px;height:12px;margin:50px auto 50px;text-align:center;}
+.cyclenav a{display:inline-block;width:12px;height:12px;margin:0 5px;text-indent:-9999em;background:#fff url("sprites.png") 0 -60px no-repeat;}
+.cyclenav a:hover,.cyclenav a:focus,.cyclenav a.activeSlide{background-position:-15px -60px;}
+#study .cyclenav { margin-top: 40px;}
+#studies .studywrap .cycleprev { margin: 25px 40px 35px 34px;}
+#studies .studywrap .cyclenext { margin: 25px 34px 35px 40px;}
+#studies .studywrap .cyclenav { float: left; width: 740px;height: 26px;margin:30px auto 45px;}
+#studies nav ul.features { background-color: #FFF;}
+#studies nav { margin-bottom: 20px; background-color: #FFF;}
+
+#study .cycleprev,#study .cyclenext{margin-top:235px;}
+
+
+
+.gallerywrap {
+ width: 100%;
+}
+
+.gallery {
+ width: 700px;
+ margin: 0 auto 50px;
+}
+
+.js .gallery img {
+ display: none;
+}
+
+.js .gallery img:first-child {
+ display: block;
+}
+
+#study .gallery img {
+ width: 700px;
+ height: 500px;
+ margin: 0 auto;
+}
+
+
+
+
+/* Forms and code */
+form {
+ width: 700px;
+ margin: 45px auto;
+}
+
+form .field {
+ clear: left;
+ margin: 0 0 50px;
+}
+
+label {
+ display: block;
+ float: left;
+ width: 280px;
+ margin: 0 20px 5px 0;
+ font: normal 20px/20px "Univers LT W01 57 Condensed", sans-serif;
+}
+
+label .pull {
+ display: inline-block;
+ width: 25px;
+ margin-left: -30px;
+}
+
+form .sublabel {
+ display: block;
+ margin: 10px 0 5px;
+ font: normal 12px/15px sans-serif;
+}
+
+form .hint {
+ margin: 5px 0;
+ font: italic 11px/15px sans-serif;
+}
+
+
+input.text, textarea {
+ width: 378px;
+ padding: 0 10px;
+ border: 1px solid #E4E4E4;
+}
+
+input.text {
+ height: 38px;
+}
+
+textarea { padding: 10px; line-height: 20px; height: 218px;}
+
+form div.button {
+ padding-left: 300px;
+}
+
+button.submit {
+ display: block;
+ width: 85px;
+ height: 85px;
+ border: 0;
+ text-align: left;
+ text-indent: -9999em;
+ background: #FFF url("sprites.png") 0 -150px;
+ cursor: pointer;
+}
+
+button.submit:hover, button.submit:focus {
+ background-position: -100px -150px;
+}
+
+button.submit:active {
+ background-position: -200px -150px;
+}
+
+
+#name_case_converter textarea {
+ height: 400px;
+}
\ No newline at end of file
diff --git a/test/fixtures/import-malformed.css b/test/fixtures/import-malformed.css
new file mode 100644
index 0000000..3824495
--- /dev/null
+++ b/test/fixtures/import-malformed.css
@@ -0,0 +1,35 @@
+.malformed.one:before {
+ content: "\\";
+ color: "red";
+}
+
+.wellformed.one {
+ color: "green";
+}
+
+.malformed.two:before {
+ content: "\"";
+ color: "red";
+}
+
+.wellformed.two {
+ color: "green";
+}
+
+.malformed.three:before {
+ content: "{";
+ color: "red";
+}
+
+.wellformed.three {
+ color: "green";
+}
+
+.malformed.four:before {
+ content: "}";
+ color: "red";
+}
+
+.wellformed.four {
+ color: "green";
+}
\ No newline at end of file
diff --git a/test/test_css_parser_basic.rb b/test/test_css_parser_basic.rb
index 5e9027d..7f3b7cf 100644
--- a/test/test_css_parser_basic.rb
+++ b/test/test_css_parser_basic.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
# Test cases for reading and generating CSS shorthand properties
-class CssParserBasicTests < Test::Unit::TestCase
+class CssParserBasicTests < Minitest::Test
include CssParser
def setup
@@ -43,6 +43,14 @@ class CssParserBasicTests < Test::Unit::TestCase
assert_equal 'color: blue;', @cp.find_by_selector('div').join(' ')
end
+ def test_removing_a_rule_set
+ rs = CssParser::RuleSet.new('div', 'color: blue;')
+ @cp.add_rule_set!(rs)
+ rs2 = CssParser::RuleSet.new('div', 'color: blue;')
+ @cp.remove_rule_set!(rs2)
+ assert_equal '', @cp.find_by_selector('div').join(' ')
+ end
+
def test_toggling_uri_conversion
# with conversion
cp_with_conversion = Parser.new(:absolute_paths => true)
@@ -60,4 +68,11 @@ class CssParserBasicTests < Test::Unit::TestCase
assert_equal "background: url('../style/yellow.png?abc=123');",
cp_without_conversion['body'].join(' ')
end
+
+ def test_converting_to_hash
+ rs = CssParser::RuleSet.new('div', 'color: blue;')
+ @cp.add_rule_set!(rs)
+ hash = @cp.to_h
+ assert_equal 'blue', hash['all']['div']['color']
+ end
end
diff --git a/test/test_css_parser_loading.rb b/test/test_css_parser_loading.rb
index 20fc87c..5177475 100644
--- a/test/test_css_parser_loading.rb
+++ b/test/test_css_parser_loading.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
# Test cases for the CssParser's loading functions.
-class CssParserLoadingTests < Test::Unit::TestCase
+class CssParserLoadingTests < Minitest::Test
include CssParser
include WEBrick
@@ -15,6 +15,14 @@ class CssParserLoadingTests < Test::Unit::TestCase
@server_thread = Thread.new do
s = WEBrick::HTTPServer.new(:Port => 12000, :DocumentRoot => @www_root, :Logger => Log.new(nil, BasicLog::FATAL), :AccessLog => [])
+ s.mount_proc('/redirect301') do |request, response|
+ response['Location'] = '/simple.css'
+ raise WEBrick::HTTPStatus::MovedPermanently
+ end
+ s.mount_proc('/redirect302') do |request, response|
+ response['Location'] = '/simple.css'
+ raise WEBrick::HTTPStatus::TemporaryRedirect
+ end
@port = s.config[:Port]
begin
s.start
@@ -32,6 +40,16 @@ class CssParserLoadingTests < Test::Unit::TestCase
@server_thread = nil
end
+ def test_loading_301_redirect
+ @cp.load_uri!("#{@uri_base}/redirect301")
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
+ end
+
+ def test_loading_302_redirect
+ @cp.load_uri!("#{@uri_base}/redirect302")
+ assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
+ end
+
def test_loading_a_local_file
file_name = File.dirname(__FILE__) + '/fixtures/simple.css'
@cp.load_file!(file_name)
@@ -49,11 +67,15 @@ class CssParserLoadingTests < Test::Unit::TestCase
assert_equal 'margin: 0px;', @cp.find_by_selector('p').join(' ')
end
- # http://github.com/alexdunae/css_parser/issues#issue/4
+ # http://github.com/premailer/css_parser/issues#issue/4
def test_loading_a_remote_file_over_ssl
# TODO: test SSL locally
- @cp.load_uri!("https://dialect.ca/inc/screen.css")
- assert_match /margin\: 0\;/, @cp.find_by_selector('body').join(' ')
+ if RUBY_PLATFORM == 'java'
+ skip "SSL: does not work on jruby"
+ else
+ @cp.load_uri!("https://dialect.ca/inc/screen.css")
+ assert_includes( @cp.find_by_selector('body').join(' '), "margin: 0;" )
+ end
end
def test_loading_a_string
@@ -102,14 +124,41 @@ class CssParserLoadingTests < Test::Unit::TestCase
assert_equal '', cp.find_by_selector('p').join(' ')
end
+ def test_following_remote_import_rules
+ css_block = '@import "http://example.com/css";'
+
+ assert_raises CssParser::RemoteFileError do
+ @cp.add_block!(css_block, :base_uri => "#{@uri_base}/subdir/")
+ end
+ end
+
def test_following_badly_escaped_import_rules
css_block = '@import "http://example.com/css?family=Droid+Sans:regular,bold|Droid+Serif:regular,italic,bold,bolditalic&subset=latin";'
- assert_nothing_raised do
+ assert_raises CssParser::RemoteFileError do
@cp.add_block!(css_block, :base_uri => "#{@uri_base}/subdir/")
end
end
+ def test_loading_malformed_content_strings
+ file_name = File.dirname(__FILE__) + '/fixtures/import-malformed.css'
+ @cp.load_file!(file_name)
+ @cp.each_selector do |sel, dec, spec|
+ assert_nil dec =~ /wellformed/
+ end
+ end
+
+ def test_loading_malformed_css_brackets
+ file_name = File.dirname(__FILE__) + '/fixtures/import-malformed.css'
+ @cp.load_file!(file_name)
+ selector_count = 0
+ @cp.each_selector do |sel, dec, spec|
+ selector_count += 1
+ end
+
+ assert_equal 8, selector_count
+ end
+
def test_following_at_import_rules_from_add_block
css_block = '@import "../simple.css";'
@@ -128,13 +177,13 @@ class CssParserLoadingTests < Test::Unit::TestCase
end
def test_local_circular_reference_exception
- assert_raise CircularReferenceError do
+ assert_raises CircularReferenceError do
@cp.load_file!(File.dirname(__FILE__) + '/fixtures/import-circular-reference.css')
end
end
def test_remote_circular_reference_exception
- assert_raise CircularReferenceError do
+ assert_raises CircularReferenceError do
@cp.load_uri!("#{@uri_base}/import-circular-reference.css")
end
end
@@ -142,22 +191,20 @@ class CssParserLoadingTests < Test::Unit::TestCase
def test_suppressing_circular_reference_exceptions
cp_without_exceptions = Parser.new(:io_exceptions => false)
- assert_nothing_raised CircularReferenceError do
- cp_without_exceptions.load_uri!("#{@uri_base}/import-circular-reference.css")
- end
+ cp_without_exceptions.load_uri!("#{@uri_base}/import-circular-reference.css")
end
def test_toggling_not_found_exceptions
cp_with_exceptions = Parser.new(:io_exceptions => true)
- assert_raise RemoteFileError do
+ err = assert_raises RemoteFileError do
cp_with_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
end
+ assert_includes err.message, "#{@uri_base}/no-exist.xyz"
+
cp_without_exceptions = Parser.new(:io_exceptions => false)
- assert_nothing_raised RemoteFileError do
- cp_without_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
- end
+ cp_without_exceptions.load_uri!("#{@uri_base}/no-exist.xyz")
end
end
diff --git a/test/test_css_parser_media_types.rb b/test/test_css_parser_media_types.rb
index 7573fe0..43dff16 100644
--- a/test/test_css_parser_media_types.rb
+++ b/test/test_css_parser_media_types.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
# Test cases for the handling of media types
-class CssParserMediaTypesTests < Test::Unit::TestCase
+class CssParserMediaTypesTests < Minitest::Test
include CssParser
def setup
@@ -78,7 +78,7 @@ class CssParserMediaTypesTests < Test::Unit::TestCase
body { color: black; }
CSS
- assert_match 'color: black;', @cp.to_s
+ assert_includes @cp.to_s, 'color: black;'
end
def test_adding_block_and_limiting_media_types1
@@ -100,7 +100,7 @@ class CssParserMediaTypesTests < Test::Unit::TestCase
base_dir = File.dirname(__FILE__) + '/fixtures/'
@cp.add_block!(css, :only_media_types => 'print and (color)', :base_dir => base_dir)
- assert_match 'color: lime', @cp.find_by_selector('div').join(' ')
+ assert_includes @cp.find_by_selector('div').join(' '), 'color: lime'
end
def test_adding_block_and_limiting_media_types
@@ -110,7 +110,7 @@ class CssParserMediaTypesTests < Test::Unit::TestCase
base_dir = File.dirname(__FILE__) + '/fixtures/'
@cp.add_block!(css, :only_media_types => :print, :base_dir => base_dir)
- assert_match '', @cp.find_by_selector('div').join(' ')
+ assert_equal '', @cp.find_by_selector('div').join(' ')
end
def test_adding_rule_set_with_media_type
diff --git a/test/test_css_parser_misc.rb b/test/test_css_parser_misc.rb
index ea3a7fc..7f023ae 100644
--- a/test/test_css_parser_misc.rb
+++ b/test/test_css_parser_misc.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
# Test cases for the CssParser.
-class CssParserTests < Test::Unit::TestCase
+class CssParserTests < Minitest::Test
include CssParser
def setup
@@ -53,14 +53,14 @@ class CssParserTests < Test::Unit::TestCase
# dervived from http://www.w3.org/TR/CSS21/syndata.html#rule-sets
css = <<-CSS
div[name='test'] {
-
+
color:
-
+
red;
-
+
}div:hover{coloR:red;
}div:first-letter{color:red;/*color:blue;}"commented out"*/}
-
+
p[example="public class foo\
{\
private string x;\
@@ -71,7 +71,7 @@ class CssParserTests < Test::Unit::TestCase
}\
\
}"] { color: red }
-
+
p { color:red}
CSS
@@ -106,11 +106,16 @@ class CssParserTests < Test::Unit::TestCase
h1, h2 { color: blue; }
h1 { font-size: 10px; }
h2 { font-size: 5px; }
+ article h3 { color: black; }
+ article
+ h3 { background-color: white; }
CSS
@cp.add_block!(css)
assert_equal 2, @cp.find_rule_sets(["h2"]).size
assert_equal 3, @cp.find_rule_sets(["h1", "h2"]).size
+ assert_equal 2, @cp.find_rule_sets(["article h3"]).size
+ assert_equal 2, @cp.find_rule_sets([" article \t \n h3 \n "]).size
end
def test_calculating_specificity
@@ -152,7 +157,7 @@ class CssParserTests < Test::Unit::TestCase
end
def test_ruleset_with_braces
-=begin
+=begin
parser = Parser.new
parser.add_block!("div { background-color: black !important; }")
parser.add_block!("div { background-color: red; }")
@@ -172,4 +177,9 @@ class CssParserTests < Test::Unit::TestCase
assert_equal 'div { background-color: black !important; }', new_rule.to_s
end
+
+ def test_content_with_data
+ rule = RuleSet.new('div', '{content: url(data:image/png;base64,LOTSOFSTUFF)}')
+ assert_includes rule.to_s, "image/png;base64,LOTSOFSTUFF"
+ end
end
diff --git a/test/test_css_parser_offset_capture.rb b/test/test_css_parser_offset_capture.rb
new file mode 100644
index 0000000..60ae33b
--- /dev/null
+++ b/test/test_css_parser_offset_capture.rb
@@ -0,0 +1,109 @@
+require File.expand_path(File.dirname(__FILE__) + '/test_helper')
+
+# Test cases for the CssParser's loading functions.
+class CssParserOffsetCaptureTests < Minitest::Test
+ include CssParser
+
+ def setup
+ @cp = Parser.new
+ end
+
+ def test_capturing_offsets_for_local_file
+ file_name = File.dirname(__FILE__) + '/fixtures/simple.css'
+ @cp.load_file!(file_name, :capture_offsets => true)
+
+ rules = @cp.find_rule_sets(['body', 'p'])
+
+ # check that we found the body rule where we expected
+ assert_equal 0, rules[0].offset.first
+ assert_equal 43, rules[0].offset.last
+ assert_equal file_name, rules[0].filename
+
+ # and the p rule
+ assert_equal 45, rules[1].offset.first
+ assert_equal 63, rules[1].offset.last
+ assert_equal file_name, rules[1].filename
+ end
+
+ # http://github.com/premailer/css_parser/issues#issue/4
+ def test_capturing_offsets_from_remote_file
+ # TODO: test SSL locally
+ if RUBY_PLATFORM == 'java'
+ skip "SSL: does not work on jruby"
+ else
+ @cp.load_uri!("https://dialect.ca/inc/screen.css", :capture_offsets => true)
+
+ # there are a lot of rules in this file, but check some rule offsets
+ rules = @cp.find_rule_sets(['#container', '#name_case_converter textarea'])
+ assert_equal 2, rules.count
+
+ assert_equal 2172, rules.first.offset.first
+ assert_equal 2227, rules.first.offset.last
+ assert_equal 'https://dialect.ca/inc/screen.css', rules.first.filename
+
+ assert_equal 10703, rules.last.offset.first
+ assert_equal 10752, rules.last.offset.last
+ assert_equal 'https://dialect.ca/inc/screen.css', rules.last.filename
+ end
+ end
+
+ def test_capturing_offsets_from_string
+ css = <<-CSS
+ body { margin: 0px; }
+ p { padding: 0px; }
+ #content { font: 12px/normal sans-serif; }
+ .content { color: red; }
+ CSS
+ @cp.load_string!(css, :capture_offsets => true, :filename => 'index.html')
+
+ rules = @cp.find_rule_sets(['body', 'p', '#content', '.content'])
+ assert_equal 4, rules.count
+
+ assert_equal 6, rules[0].offset.first
+ assert_equal 27, rules[0].offset.last
+ assert_equal 'index.html', rules[0].filename
+
+ assert_equal 34, rules[1].offset.first
+ assert_equal 53, rules[1].offset.last
+ assert_equal 'index.html', rules[1].filename
+
+ assert_equal 60, rules[2].offset.first
+ assert_equal 102, rules[2].offset.last
+ assert_equal 'index.html', rules[2].filename
+
+ assert_equal 109, rules[3].offset.first
+ assert_equal 133, rules[3].offset.last
+ assert_equal 'index.html', rules[3].filename
+ end
+
+ def test_capturing_offsets_with_imports
+ base_dir = File.dirname(__FILE__) + '/fixtures'
+ @cp.load_file!('import1.css', :base_dir => base_dir, :capture_offsets => true)
+
+ rules = @cp.find_rule_sets(['div', 'a', 'body', 'p'])
+
+ # check that we found the div rule where we expected in the primary file
+ assert_equal 'div', rules[0].selectors.join
+ assert_equal 31, rules[0].offset.first
+ assert_equal 51, rules[0].offset.last
+ assert_equal base_dir + '/import1.css', rules[0].filename
+
+ # check that the a rule in the first import is where we expect
+ assert_equal 'a', rules[1].selectors.join
+ assert_equal 26, rules[1].offset.first
+ assert_equal 54, rules[1].offset.last
+ assert_equal base_dir + '/subdir/import2.css', rules[1].filename
+
+ # and the body rule in the second import
+ assert_equal 'body', rules[2].selectors.join
+ assert_equal 0, rules[2].offset.first
+ assert_equal 43, rules[2].offset.last
+ assert_equal base_dir + '/simple.css', rules[2].filename
+
+ # as well as the p rule in the second import
+ assert_equal 'p', rules[3].selectors.join
+ assert_equal 45, rules[3].offset.first
+ assert_equal 63, rules[3].offset.last
+ assert_equal base_dir + '/simple.css', rules[3].filename
+ end
+end
diff --git a/test/test_css_parser_regexps.rb b/test/test_css_parser_regexps.rb
index 99f0008..61a33f4 100644
--- a/test/test_css_parser_regexps.rb
+++ b/test/test_css_parser_regexps.rb
@@ -5,13 +5,13 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
#
# see http://www.w3.org/TR/CSS21/syndata.html and
# http://www.w3.org/TR/CSS21/grammar.html
-class CssParserRegexpTests < Test::Unit::TestCase
+class CssParserRegexpTests < Minitest::Test
def test_strings
# complete matches
[
- '"abcd"', '" A sd s�drcv \'dsf\' asd rfg asd"', '"A\ d??ef 123!"',
+ '"abcd"', '" A sd sédrcv \'dsf\' asd rfg asd"', '"A\ d??ef 123!"',
"\"this is\\\n a test\"", '"back\67round"', '"r\000065 ed"',
- "'abcd'", "' A sd sedrcv \"dsf\" asd rf�&23$%#%$g asd'", "'A\\\n def 123!'",
+ "'abcd'", "' A sd sedrcv \"dsf\" asd rf—&23$%#%$g asd'", "'A\\\n def 123!'",
"'this is\\\n a test'", "'back\\67round'", "'r\\000065 ed'"
].each do |str|
assert_equal str, str.match(CssParser::RE_STRING).to_s
@@ -21,6 +21,12 @@ class CssParserRegexpTests < Test::Unit::TestCase
assert_equal "\"url\\.'p'ng\"", test_string.match(CssParser::RE_STRING).to_s
end
+ def test_box_model_units
+ %w( auto inherit 80px 90pt 80pc 80rem 80vh 70vm 60vw 1vmin 2vmax 0 2em 3ex 1cm 100mm 2in 120% ).each do |str|
+ assert_match(CssParser::BOX_MODEL_UNITS_RX, str)
+ end
+ end
+
def test_unicode
['back\67round', 'r\000065 ed', '\00006C'].each do |str|
assert_match(Regexp.new(CssParser::RE_UNICODE), str)
@@ -31,7 +37,9 @@ class CssParserRegexpTests < Test::Unit::TestCase
[
'color: #fff', 'color:#f0a09c;', 'color: #04A', 'color: #04a9CE',
'color: rgb(100, -10%, 300);', 'color: rgb(10,10,10)', 'color:rgb(12.7253%, -12%,0)',
- 'color: black', 'color:Red;', 'color: AqUa;', 'color: blue ', 'color: transparent'
+ 'color: hsla(-15, -77%, 19%, 5%);',
+ 'color: black', 'color:Red;', 'color: AqUa;', 'color: blue ', 'color: transparent',
+ 'color: darkslategray'
].each do |colour|
assert_match(CssParser::RE_COLOUR, colour)
end
@@ -39,9 +47,10 @@ class CssParserRegexpTests < Test::Unit::TestCase
[
'color: #fa', 'color:#f009c;', 'color: #04G', 'color: #04a9Cq',
'color: rgb 100, -10%, 300;', 'color: rgb 10,10,10', 'color:rgb(12px, -12%,0)',
- 'color:fuscia;', 'color: thick'
+ 'color:fuscia;', 'color: thick',
+ 'color: alice_blue'
].each do |colour|
- assert_no_match(CssParser::RE_COLOUR, colour)
+ refute_match(CssParser::RE_COLOUR, colour)
end
end
@@ -74,7 +83,7 @@ class CssParserRegexpTests < Test::Unit::TestCase
def test_important
assert_match(CssParser::IMPORTANT_IN_PROPERTY_RX, "color: #f00 !important ;")
- assert_no_match(CssParser::IMPORTANT_IN_PROPERTY_RX, "color: #f00 !importantish;")
+ refute_match(CssParser::IMPORTANT_IN_PROPERTY_RX, "color: #f00 !importantish;")
end
protected
diff --git a/test/test_helper.rb b/test/test_helper.rb
index ffe7a2e..05aab4c 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,6 +1,5 @@
-require 'rubygems'
require 'bundler/setup'
-require 'test/unit'
+require 'maxitest/autorun'
require 'net/http'
require 'webrick'
-require File.dirname(__FILE__) + '/../lib/css_parser'
+require 'css_parser'
diff --git a/test/test_merging.rb b/test/test_merging.rb
index 1a6e8b7..3ebbcd0 100644
--- a/test/test_merging.rb
+++ b/test/test_merging.rb
@@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
-class MergingTests < Test::Unit::TestCase
+class MergingTests < Minitest::Test
include CssParser
def setup
@@ -89,7 +89,7 @@ class MergingTests < Test::Unit::TestCase
end
def test_raising_error_on_bad_type
- assert_raise ArgumentError do
+ assert_raises ArgumentError do
CssParser.merge([1,2,3])
end
end
diff --git a/test/test_rule_set.rb b/test/test_rule_set.rb
index 14d13e8..28273b8 100644
--- a/test/test_rule_set.rb
+++ b/test/test_rule_set.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
require "set"
# Test cases for parsing CSS blocks
-class RuleSetTests < Test::Unit::TestCase
+class RuleSetTests < Minitest::Test
include CssParser
def setup
@@ -106,4 +106,14 @@ class RuleSetTests < Test::Unit::TestCase
assert_equal 1000, spec
end
end
+
+ def test_not_raised_issue68
+ ok = true
+ begin
+ RuleSet.new('td', 'border-top: 5px solid; border-color: #fffff0;')
+ rescue
+ ok = false
+ end
+ assert_equal true, ok
+ end
end
diff --git a/test/test_rule_set_creating_shorthand.rb b/test/test_rule_set_creating_shorthand.rb
index 711a59b..c598495 100644
--- a/test/test_rule_set_creating_shorthand.rb
+++ b/test/test_rule_set_creating_shorthand.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
# Test cases for reading and generating CSS shorthand properties
-class RuleSetCreatingShorthandTests < Test::Unit::TestCase
+class RuleSetCreatingShorthandTests < Minitest::Test
include CssParser
def setup
@@ -35,6 +35,10 @@ class RuleSetCreatingShorthandTests < Test::Unit::TestCase
properties = {'border-top-style' => 'none', 'border-right-style' => 'none', 'border-bottom-style' => 'none', 'border-left-style' => 'none'}
combined = create_shorthand(properties)
assert_equal 'none;', combined['border']
+
+ properties = {'border-top-color' => '#bada55', 'border-right-color' => '#000000', 'border-bottom-color' => '#ffffff', 'border-left-color' => '#ff0000'}
+ combined = create_shorthand(properties)
+ assert_equal '#bada55 #000000 #ffffff #ff0000;', combined['border-color']
end
# Dimensions shorthand
@@ -102,6 +106,31 @@ class RuleSetCreatingShorthandTests < Test::Unit::TestCase
assert_properties_are_deleted(combined, properties)
end
+ def test_combining_background_with_size_into_shorthand
+ properties = {'background-image' => 'url(\'chess.png\')', 'background-color' => 'gray',
+ 'background-position' => 'center -10.2%', 'background-attachment' => 'fixed',
+ 'background-repeat' => 'no-repeat', 'background-size' => '50% 100%'}
+
+ combined = create_shorthand(properties)
+
+ assert_equal('gray url(\'chess.png\') no-repeat center -10.2% / 50% 100% fixed;', combined['background'])
+
+ # after creating shorthand, all long-hand properties should be deleted
+ assert_properties_are_deleted(combined, properties)
+ end
+
+ def test_combining_background_with_size_and_no_position_into_shorthand
+ properties = {'background-image' => 'url(\'chess.png\')', 'background-color' => 'gray',
+ 'background-attachment' => 'fixed', 'background-repeat' => 'no-repeat',
+ 'background-size' => '50% 100%'}
+
+ combined = create_shorthand(properties)
+
+ assert_equal('gray url(\'chess.png\') no-repeat 0% 0% / 50% 100% fixed;', combined['background'])
+
+ # after creating shorthand, all long-hand properties should be deleted
+ assert_properties_are_deleted(combined, properties)
+ end
# List-style shorthand
def test_combining_list_style_into_shorthand
@@ -125,6 +154,15 @@ class RuleSetCreatingShorthandTests < Test::Unit::TestCase
assert_equal('url(http://example.com/1528/www/top-logo.jpg) no-repeat top right;', rs['background'])
end
+
+ def test_a_single_property_is_not_shorted
+ properties = {'background-color' => 'gray'}
+ combined = create_shorthand(properties)
+
+ assert_equal('gray;', combined['background-color'])
+ assert_equal('', combined['background'])
+ end
+
protected
def assert_properties_are_deleted(ruleset, properties)
diff --git a/test/test_rule_set_expanding_shorthand.rb b/test/test_rule_set_expanding_shorthand.rb
index af70be7..a0069fa 100644
--- a/test/test_rule_set_expanding_shorthand.rb
+++ b/test/test_rule_set_expanding_shorthand.rb
@@ -1,6 +1,6 @@
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
-class RuleSetExpandingShorthandTests < Test::Unit::TestCase
+class RuleSetExpandingShorthandTests < Minitest::Test
include CssParser
def setup
@@ -21,6 +21,27 @@ class RuleSetExpandingShorthandTests < Test::Unit::TestCase
assert_equal 'rgb(2%,2%,2%)', declarations['border-bottom-color']
assert_equal 'hsla(255,0,0,5)', declarations['border-left-color']
+ declarations = expand_declarations('border-color: #000000 #bada55 #ffffff #ff0000')
+
+ assert_equal '#000000', declarations['border-top-color']
+ assert_equal '#bada55', declarations['border-right-color']
+ assert_equal '#ffffff', declarations['border-bottom-color']
+ assert_equal '#ff0000', declarations['border-left-color']
+
+ declarations = expand_declarations('border-color: #000000 #bada55 #ffffff')
+
+ assert_equal '#000000', declarations['border-top-color']
+ assert_equal '#bada55', declarations['border-right-color']
+ assert_equal '#ffffff', declarations['border-bottom-color']
+ assert_equal '#bada55', declarations['border-left-color']
+
+ declarations = expand_declarations('border-color: #000000 #bada55')
+
+ assert_equal '#000000', declarations['border-top-color']
+ assert_equal '#bada55', declarations['border-right-color']
+ assert_equal '#000000', declarations['border-bottom-color']
+ assert_equal '#bada55', declarations['border-left-color']
+
declarations = expand_declarations('border: thin dot-dot-dash')
assert_equal 'dot-dot-dash', declarations['border-left-style']
assert_equal 'thin', declarations['border-left-width']
@@ -83,10 +104,13 @@ class RuleSetExpandingShorthandTests < Test::Unit::TestCase
shorthand = "font: small-caps italic 12px sans-serif;"
declarations = expand_declarations(shorthand)
assert_equal('small-caps', declarations['font-variant'])
+ end
- # ensure normal is the default state
- ['font: normal italic 12px sans-serif;', 'font: italic 12px sans-serif;',
- 'font: normal 12px sans-serif;', 'font: 12px/16px sans-serif;'].each do |shorthand|
+ def test_getting_font_variant_from_shorthand_ensure_normal_is_the_default_state
+ [
+ 'font: normal italic 12px sans-serif;', 'font: italic 12px sans-serif;',
+ 'font: normal 12px sans-serif;', 'font: 12px/16px sans-serif;'
+ ].each do |shorthand|
declarations = expand_declarations(shorthand)
assert_equal('normal', declarations['font-variant'], shorthand)
end
@@ -147,6 +171,20 @@ class RuleSetExpandingShorthandTests < Test::Unit::TestCase
end
end
+ def test_getting_background_size_from_shorthand
+ ['em', 'ex', 'in', 'px', 'pt', 'pc', '%'].each do |unit|
+ shorthand = "background: url('chess.png') gray 30% -0.20/-0.15#{unit} auto repeat fixed;"
+ declarations = expand_declarations(shorthand)
+ assert_equal("-0.15#{unit} auto", declarations['background-size'])
+ end
+
+ ['cover', 'contain', 'auto', 'initial', 'inherit'].each do |size|
+ shorthand = "background: url('chess.png') #000fff 0% 50% / #{size} no-repeat fixed;"
+ declarations = expand_declarations(shorthand)
+ assert_equal(size, declarations['background-size'])
+ end
+ end
+
def test_getting_background_colour_from_shorthand
['blue', 'lime', 'rgb(10,10,10)', 'rgb ( -10%, 99, 300)', '#ffa0a0', '#03c', 'trAnsparEnt', 'inherit'].each do |colour|
shorthand = "background:#{colour} url('chess.png') center repeat fixed ;"
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-css-parser.git
More information about the Pkg-ruby-extras-commits
mailing list