deadweight 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/deadweight.rb +3 -3
- data/lib/deadweight/rake_task.rb +2 -0
- metadata +91 -85
- data/vendor/gems/css_parser-1.1.5/lib/css_parser.rb +0 -162
- data/vendor/gems/css_parser-1.1.5/lib/css_parser/parser.rb +0 -411
- data/vendor/gems/css_parser-1.1.5/lib/css_parser/regexps.rb +0 -46
- data/vendor/gems/css_parser-1.1.5/lib/css_parser/rule_set.rb +0 -394
- data/vendor/gems/css_parser-1.1.5/test/fixtures/import-circular-reference.css +0 -4
- data/vendor/gems/css_parser-1.1.5/test/fixtures/import-with-media-types.css +0 -3
- data/vendor/gems/css_parser-1.1.5/test/fixtures/import1.css +0 -3
- data/vendor/gems/css_parser-1.1.5/test/fixtures/simple.css +0 -6
- data/vendor/gems/css_parser-1.1.5/test/fixtures/subdir/import2.css +0 -3
- data/vendor/gems/css_parser-1.1.5/test/test_css_parser_basic.rb +0 -59
- data/vendor/gems/css_parser-1.1.5/test/test_css_parser_loading.rb +0 -139
- data/vendor/gems/css_parser-1.1.5/test/test_css_parser_media_types.rb +0 -106
- data/vendor/gems/css_parser-1.1.5/test/test_css_parser_misc.rb +0 -142
- data/vendor/gems/css_parser-1.1.5/test/test_css_parser_regexps.rb +0 -69
- data/vendor/gems/css_parser-1.1.5/test/test_helper.rb +0 -6
- data/vendor/gems/css_parser-1.1.5/test/test_merging.rb +0 -103
- data/vendor/gems/css_parser-1.1.5/test/test_rule_set.rb +0 -90
- data/vendor/gems/css_parser-1.1.5/test/test_rule_set_creating_shorthand.rb +0 -90
- data/vendor/gems/css_parser-1.1.5/test/test_rule_set_expanding_shorthand.rb +0 -178
data/lib/deadweight.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
$LOAD_PATH.concat Dir.glob(File.expand_path('../../vendor/gems/*/lib', __FILE__))
|
2
|
-
|
3
1
|
require 'css_parser'
|
4
2
|
require 'nokogiri'
|
5
3
|
require 'open-uri'
|
@@ -176,7 +174,9 @@ private
|
|
176
174
|
end
|
177
175
|
|
178
176
|
def strip(selector)
|
179
|
-
selector.gsub(
|
177
|
+
selector = selector.gsub(/^@.*/, '') # @-webkit-keyframes ...
|
178
|
+
selector = selector.gsub(/:.*/, '') # input#x:nth-child(2):not(#z.o[type='file'])
|
179
|
+
selector
|
180
180
|
end
|
181
181
|
|
182
182
|
def log
|
data/lib/deadweight/rake_task.rb
CHANGED
metadata
CHANGED
@@ -1,70 +1,103 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: deadweight
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 2
|
8
|
-
- 1
|
9
|
-
version: 0.2.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.2
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Aanand Prasad
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-09-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: nokogiri
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
22
23
|
prerelease: false
|
23
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: css_parser
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
24
33
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
- 0
|
30
|
-
version: "0"
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.2.6
|
31
38
|
type: :runtime
|
32
|
-
version_requirements: *id001
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: shoulda
|
35
39
|
prerelease: false
|
36
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.2.6
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: shoulda
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
37
49
|
none: false
|
38
|
-
requirements:
|
39
|
-
- -
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
|
42
|
-
- 0
|
43
|
-
version: "0"
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
44
54
|
type: :development
|
45
|
-
|
46
|
-
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
47
63
|
name: mechanize
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.5.1
|
70
|
+
type: :development
|
48
71
|
prerelease: false
|
49
|
-
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
73
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
55
|
-
|
56
|
-
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.5.1
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
57
86
|
type: :development
|
58
|
-
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
59
94
|
description:
|
60
95
|
email: aanand.prasad@gmail.com
|
61
|
-
executables:
|
96
|
+
executables:
|
62
97
|
- deadweight
|
63
98
|
extensions: []
|
64
|
-
|
65
99
|
extra_rdoc_files: []
|
66
|
-
|
67
|
-
files:
|
100
|
+
files:
|
68
101
|
- LICENSE
|
69
102
|
- README.md
|
70
103
|
- bin/deadweight
|
@@ -74,56 +107,29 @@ files:
|
|
74
107
|
- lib/deadweight/hijack/rails.rb
|
75
108
|
- lib/deadweight/rack/capturing_middleware.rb
|
76
109
|
- lib/deadweight/rake_task.rb
|
77
|
-
- vendor/gems/css_parser-1.1.5/lib/css_parser.rb
|
78
|
-
- vendor/gems/css_parser-1.1.5/lib/css_parser/parser.rb
|
79
|
-
- vendor/gems/css_parser-1.1.5/lib/css_parser/regexps.rb
|
80
|
-
- vendor/gems/css_parser-1.1.5/lib/css_parser/rule_set.rb
|
81
|
-
- vendor/gems/css_parser-1.1.5/test/fixtures/import-circular-reference.css
|
82
|
-
- vendor/gems/css_parser-1.1.5/test/fixtures/import-with-media-types.css
|
83
|
-
- vendor/gems/css_parser-1.1.5/test/fixtures/import1.css
|
84
|
-
- vendor/gems/css_parser-1.1.5/test/fixtures/simple.css
|
85
|
-
- vendor/gems/css_parser-1.1.5/test/fixtures/subdir/import2.css
|
86
|
-
- vendor/gems/css_parser-1.1.5/test/test_css_parser_basic.rb
|
87
|
-
- vendor/gems/css_parser-1.1.5/test/test_css_parser_loading.rb
|
88
|
-
- vendor/gems/css_parser-1.1.5/test/test_css_parser_media_types.rb
|
89
|
-
- vendor/gems/css_parser-1.1.5/test/test_css_parser_misc.rb
|
90
|
-
- vendor/gems/css_parser-1.1.5/test/test_css_parser_regexps.rb
|
91
|
-
- vendor/gems/css_parser-1.1.5/test/test_helper.rb
|
92
|
-
- vendor/gems/css_parser-1.1.5/test/test_merging.rb
|
93
|
-
- vendor/gems/css_parser-1.1.5/test/test_rule_set.rb
|
94
|
-
- vendor/gems/css_parser-1.1.5/test/test_rule_set_creating_shorthand.rb
|
95
|
-
- vendor/gems/css_parser-1.1.5/test/test_rule_set_expanding_shorthand.rb
|
96
|
-
has_rdoc: true
|
97
110
|
homepage: http://github.com/aanand/deadweight
|
98
|
-
licenses:
|
111
|
+
licenses:
|
99
112
|
- MIT
|
100
113
|
post_install_message:
|
101
114
|
rdoc_options: []
|
102
|
-
|
103
|
-
require_paths:
|
115
|
+
require_paths:
|
104
116
|
- lib
|
105
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
118
|
none: false
|
107
|
-
requirements:
|
108
|
-
- -
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
|
111
|
-
|
112
|
-
version: "0"
|
113
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ! '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
124
|
none: false
|
115
|
-
requirements:
|
116
|
-
- -
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
|
119
|
-
- 0
|
120
|
-
version: "0"
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
121
129
|
requirements: []
|
122
|
-
|
123
130
|
rubyforge_project:
|
124
|
-
rubygems_version: 1.
|
131
|
+
rubygems_version: 1.8.23
|
125
132
|
signing_key:
|
126
133
|
specification_version: 3
|
127
134
|
summary: A coverage tool for finding unused CSS
|
128
135
|
test_files: []
|
129
|
-
|
@@ -1,162 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
require 'net/https'
|
3
|
-
require 'open-uri'
|
4
|
-
require 'digest/md5'
|
5
|
-
require 'zlib'
|
6
|
-
require 'stringio'
|
7
|
-
require 'iconv'
|
8
|
-
|
9
|
-
module CssParser
|
10
|
-
VERSION = '1.1.5'
|
11
|
-
|
12
|
-
# Merge multiple CSS RuleSets by cascading according to the CSS 2.1 cascading rules
|
13
|
-
# (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order).
|
14
|
-
#
|
15
|
-
# Takes one or more RuleSet objects.
|
16
|
-
#
|
17
|
-
# Returns a RuleSet.
|
18
|
-
#
|
19
|
-
# ==== Cascading
|
20
|
-
# If a RuleSet object has its +specificity+ defined, that specificity is
|
21
|
-
# used in the cascade calculations.
|
22
|
-
#
|
23
|
-
# If no specificity is explicitly set and the RuleSet has *one* selector,
|
24
|
-
# the specificity is calculated using that selector.
|
25
|
-
#
|
26
|
-
# If no selectors or multiple selectors are present, the specificity is
|
27
|
-
# treated as 0.
|
28
|
-
#
|
29
|
-
# ==== Example #1
|
30
|
-
# rs1 = RuleSet.new(nil, 'color: black;')
|
31
|
-
# rs2 = RuleSet.new(nil, 'margin: 0px;')
|
32
|
-
#
|
33
|
-
# merged = CssParser.merge(rs1, rs2)
|
34
|
-
#
|
35
|
-
# puts merged
|
36
|
-
# => "{ margin: 0px; color: black; }"
|
37
|
-
#
|
38
|
-
# ==== Example #2
|
39
|
-
# rs1 = RuleSet.new(nil, 'background-color: black;')
|
40
|
-
# rs2 = RuleSet.new(nil, 'background-image: none;')
|
41
|
-
#
|
42
|
-
# merged = CssParser.merge(rs1, rs2)
|
43
|
-
#
|
44
|
-
# puts merged
|
45
|
-
# => "{ background: none black; }"
|
46
|
-
#--
|
47
|
-
# TODO: declaration_hashes should be able to contain a RuleSet
|
48
|
-
# this should be a Class method
|
49
|
-
def CssParser.merge(*rule_sets)
|
50
|
-
@folded_declaration_cache = {}
|
51
|
-
|
52
|
-
# in case called like CssParser.merge([rule_set, rule_set])
|
53
|
-
rule_sets.flatten! if rule_sets[0].kind_of?(Array)
|
54
|
-
|
55
|
-
unless rule_sets.all? {|rs| rs.kind_of?(CssParser::RuleSet)}
|
56
|
-
raise ArgumentError, "all parameters must be CssParser::RuleSets."
|
57
|
-
end
|
58
|
-
|
59
|
-
return rule_sets[0] if rule_sets.length == 1
|
60
|
-
|
61
|
-
# Internal storage of CSS properties that we will keep
|
62
|
-
properties = {}
|
63
|
-
|
64
|
-
rule_sets.each do |rule_set|
|
65
|
-
rule_set.expand_shorthand!
|
66
|
-
|
67
|
-
specificity = rule_set.specificity
|
68
|
-
unless specificity
|
69
|
-
if rule_set.selectors.length == 1
|
70
|
-
specificity = calculate_specificity(rule_set.selectors[0])
|
71
|
-
else
|
72
|
-
specificity = 0
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
rule_set.each_declaration do |property, value, is_important|
|
77
|
-
# Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
|
78
|
-
if not properties.has_key?(property)
|
79
|
-
properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
|
80
|
-
elsif properties[property][:specificity] < specificity or properties[property][:specificity] == specificity
|
81
|
-
unless properties[property][:is_important]
|
82
|
-
properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
if is_important
|
87
|
-
properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
merged = RuleSet.new(nil, nil)
|
93
|
-
|
94
|
-
properties.each do |property, details|
|
95
|
-
if details[:is_important]
|
96
|
-
merged[property.strip] = details[:value].strip.gsub(/\;\Z/, '') + '!important'
|
97
|
-
else
|
98
|
-
merged[property.strip] = details[:value].strip
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
merged.create_shorthand!
|
103
|
-
merged
|
104
|
-
end
|
105
|
-
|
106
|
-
# Calculates the specificity of a CSS selector
|
107
|
-
# per http://www.w3.org/TR/CSS21/cascade.html#specificity
|
108
|
-
#
|
109
|
-
# Returns an integer.
|
110
|
-
#
|
111
|
-
# ==== Example
|
112
|
-
# CssParser.calculate_specificity('#content div p:first-line a:link')
|
113
|
-
# => 114
|
114
|
-
#--
|
115
|
-
# Thanks to Rafael Salazar and Nick Fitzsimons on the css-discuss list for their help.
|
116
|
-
#++
|
117
|
-
def CssParser.calculate_specificity(selector)
|
118
|
-
a = 0
|
119
|
-
b = selector.scan(/\#/).length
|
120
|
-
c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX).length
|
121
|
-
d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX).length
|
122
|
-
|
123
|
-
(a.to_s + b.to_s + c.to_s + d.to_s).to_i
|
124
|
-
rescue
|
125
|
-
return 0
|
126
|
-
end
|
127
|
-
|
128
|
-
# Make <tt>url()</tt> links absolute.
|
129
|
-
#
|
130
|
-
# Takes a block of CSS and returns it with all relative URIs converted to absolute URIs.
|
131
|
-
#
|
132
|
-
# "For CSS style sheets, the base URI is that of the style sheet, not that of the source document."
|
133
|
-
# per http://www.w3.org/TR/CSS21/syndata.html#uri
|
134
|
-
#
|
135
|
-
# Returns a string.
|
136
|
-
#
|
137
|
-
# ==== Example
|
138
|
-
# CssParser.convert_uris("body { background: url('../style/yellow.png?abc=123') };",
|
139
|
-
# "http://example.org/style/basic.css").inspect
|
140
|
-
# => "body { background: url('http://example.org/style/yellow.png?abc=123') };"
|
141
|
-
def self.convert_uris(css, base_uri)
|
142
|
-
out = ''
|
143
|
-
base_uri = URI.parse(base_uri) unless base_uri.kind_of?(URI)
|
144
|
-
|
145
|
-
out = css.gsub(URI_RX) do |s|
|
146
|
-
uri = $1.to_s
|
147
|
-
uri.gsub!(/["']+/, '')
|
148
|
-
# Don't process URLs that are already absolute
|
149
|
-
unless uri =~ /^[a-z]+\:\/\//i
|
150
|
-
begin
|
151
|
-
uri = base_uri.merge(uri)
|
152
|
-
rescue; end
|
153
|
-
end
|
154
|
-
"url('" + uri.to_s + "')"
|
155
|
-
end
|
156
|
-
out
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
require File.dirname(__FILE__) + '/css_parser/rule_set'
|
161
|
-
require File.dirname(__FILE__) + '/css_parser/regexps'
|
162
|
-
require File.dirname(__FILE__) + '/css_parser/parser'
|
@@ -1,411 +0,0 @@
|
|
1
|
-
module CssParser
|
2
|
-
# Exception class used for any errors encountered while downloading remote files.
|
3
|
-
class RemoteFileError < IOError; end
|
4
|
-
|
5
|
-
# Exception class used if a request is made to load a CSS file more than once.
|
6
|
-
class CircularReferenceError < StandardError; end
|
7
|
-
|
8
|
-
|
9
|
-
# == Parser class
|
10
|
-
#
|
11
|
-
# All CSS is converted to UTF-8.
|
12
|
-
#
|
13
|
-
# When calling Parser#new there are some configuaration options:
|
14
|
-
# [<tt>absolute_paths</tt>] Convert relative paths to absolute paths (<tt>href</tt>, <tt>src</tt> and <tt>url('')</tt>. Boolean, default is <tt>false</tt>.
|
15
|
-
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
|
16
|
-
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
|
17
|
-
class Parser
|
18
|
-
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (http://github.com/alexdunae/css_parser)"
|
19
|
-
|
20
|
-
STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
|
21
|
-
STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
|
22
|
-
|
23
|
-
# Initial parsing
|
24
|
-
RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\])]*)\)?[;\n]?/
|
25
|
-
|
26
|
-
# Array of CSS files that have been loaded.
|
27
|
-
attr_reader :loaded_uris
|
28
|
-
|
29
|
-
#attr_reader :rules
|
30
|
-
|
31
|
-
#--
|
32
|
-
# Class variable? see http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html
|
33
|
-
#++
|
34
|
-
@folded_declaration_cache = {}
|
35
|
-
class << self; attr_reader :folded_declaration_cache; end
|
36
|
-
|
37
|
-
def initialize(options = {})
|
38
|
-
@options = {:absolute_paths => false,
|
39
|
-
:import => true,
|
40
|
-
:io_exceptions => true}.merge(options)
|
41
|
-
|
42
|
-
# array of RuleSets
|
43
|
-
@rules = []
|
44
|
-
|
45
|
-
|
46
|
-
@loaded_uris = []
|
47
|
-
|
48
|
-
# unprocessed blocks of CSS
|
49
|
-
@blocks = []
|
50
|
-
reset!
|
51
|
-
end
|
52
|
-
|
53
|
-
# Get declarations by selector.
|
54
|
-
#
|
55
|
-
# +media_types+ are optional, and can be a symbol or an array of symbols.
|
56
|
-
# The default value is <tt>:all</tt>.
|
57
|
-
#
|
58
|
-
# ==== Examples
|
59
|
-
# find_by_selector('#content')
|
60
|
-
# => 'font-size: 13px; line-height: 1.2;'
|
61
|
-
#
|
62
|
-
# find_by_selector('#content', [:screen, :handheld])
|
63
|
-
# => 'font-size: 13px; line-height: 1.2;'
|
64
|
-
#
|
65
|
-
# find_by_selector('#content', :print)
|
66
|
-
# => 'font-size: 11pt; line-height: 1.2;'
|
67
|
-
#
|
68
|
-
# Returns an array of declarations.
|
69
|
-
def find_by_selector(selector, media_types = :all)
|
70
|
-
out = []
|
71
|
-
each_selector(media_types) do |sel, dec, spec|
|
72
|
-
out << dec if sel.strip == selector.strip
|
73
|
-
end
|
74
|
-
out
|
75
|
-
end
|
76
|
-
alias_method :[], :find_by_selector
|
77
|
-
|
78
|
-
|
79
|
-
# Add a raw block of CSS.
|
80
|
-
#
|
81
|
-
# In order to follow +@import+ rules you must supply either a
|
82
|
-
# +:base_dir+ or +:base_uri+ option.
|
83
|
-
#
|
84
|
-
# Use the +:media_types+ option to set the media type(s) for this block. Takes an array of symbols.
|
85
|
-
#
|
86
|
-
# Use the +:only_media_types+ option to selectively follow +@import+ rules. Takes an array of symbols.
|
87
|
-
#
|
88
|
-
# ==== Example
|
89
|
-
# css = <<-EOT
|
90
|
-
# body { font-size: 10pt }
|
91
|
-
# p { margin: 0px; }
|
92
|
-
# @media screen, print {
|
93
|
-
# body { line-height: 1.2 }
|
94
|
-
# }
|
95
|
-
# EOT
|
96
|
-
#
|
97
|
-
# parser = CssParser::Parser.new
|
98
|
-
# parser.add_block!(css)
|
99
|
-
def add_block!(block, options = {})
|
100
|
-
options = {:base_uri => nil, :base_dir => nil, :charset => nil, :media_types => :all, :only_media_types => :all}.merge(options)
|
101
|
-
options[:media_types] = [options[:media_types]].flatten
|
102
|
-
options[:only_media_types] = [options[:only_media_types]].flatten
|
103
|
-
|
104
|
-
block = cleanup_block(block)
|
105
|
-
|
106
|
-
if options[:base_uri] and @options[:absolute_paths]
|
107
|
-
block = CssParser.convert_uris(block, options[:base_uri])
|
108
|
-
end
|
109
|
-
|
110
|
-
# Load @imported CSS
|
111
|
-
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
|
112
|
-
media_types = []
|
113
|
-
if media_string = import_rule[-1]
|
114
|
-
media_string.split(/\s|\,/).each do |t|
|
115
|
-
media_types << t.to_sym unless t.empty?
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
next unless options[:only_media_types].include?(:all) or media_types.length < 1 or (media_types & options[:only_media_types]).length > 0
|
120
|
-
|
121
|
-
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
|
122
|
-
|
123
|
-
if options[:base_uri]
|
124
|
-
import_uri = URI.parse(options[:base_uri].to_s).merge(import_path)
|
125
|
-
load_uri!(import_uri, options[:base_uri], media_types)
|
126
|
-
elsif options[:base_dir]
|
127
|
-
load_file!(import_path, options[:base_dir], media_types)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# Remove @import declarations
|
132
|
-
block.gsub!(RE_AT_IMPORT_RULE, '')
|
133
|
-
|
134
|
-
parse_block_into_rule_sets!(block, options)
|
135
|
-
end
|
136
|
-
|
137
|
-
# Add a CSS rule by setting the +selectors+, +declarations+ and +media_types+.
|
138
|
-
#
|
139
|
-
# +media_types+ can be a symbol or an array of symbols.
|
140
|
-
def add_rule!(selectors, declarations, media_types = :all)
|
141
|
-
rule_set = RuleSet.new(selectors, declarations)
|
142
|
-
add_rule_set!(rule_set, media_types)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Add a CssParser RuleSet object.
|
146
|
-
#
|
147
|
-
# +media_types+ can be a symbol or an array of symbols.
|
148
|
-
def add_rule_set!(ruleset, media_types = :all)
|
149
|
-
raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
|
150
|
-
|
151
|
-
media_types = [media_types] if media_types.kind_of?(Symbol)
|
152
|
-
|
153
|
-
@rules << {:media_types => media_types, :rules => ruleset}
|
154
|
-
end
|
155
|
-
|
156
|
-
# Iterate through RuleSet objects.
|
157
|
-
#
|
158
|
-
# +media_types+ can be a symbol or an array of symbols.
|
159
|
-
def each_rule_set(media_types = :all) # :yields: rule_set
|
160
|
-
media_types = [:all] if media_types.nil?
|
161
|
-
media_types = [media_types] if media_types.kind_of?(Symbol)
|
162
|
-
|
163
|
-
@rules.each do |block|
|
164
|
-
if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
|
165
|
-
yield block[:rules]
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# Iterate through CSS selectors.
|
171
|
-
#
|
172
|
-
# +media_types+ can be a symbol or an array of symbols.
|
173
|
-
# See RuleSet#each_selector for +options+.
|
174
|
-
def each_selector(media_types = :all, options = {}) # :yields: selectors, declarations, specificity
|
175
|
-
each_rule_set(media_types) do |rule_set|
|
176
|
-
#puts rule_set
|
177
|
-
rule_set.each_selector(options) do |selectors, declarations, specificity|
|
178
|
-
yield selectors, declarations, specificity
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# Output all CSS rules as a single stylesheet.
|
184
|
-
def to_s(media_types = :all)
|
185
|
-
out = ''
|
186
|
-
each_selector(media_types) do |selectors, declarations, specificity|
|
187
|
-
out << "#{selectors} {\n#{declarations}\n}\n"
|
188
|
-
end
|
189
|
-
out
|
190
|
-
end
|
191
|
-
|
192
|
-
# Merge declarations with the same selector.
|
193
|
-
def compact! # :nodoc:
|
194
|
-
compacted = []
|
195
|
-
|
196
|
-
compacted
|
197
|
-
end
|
198
|
-
|
199
|
-
def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
|
200
|
-
options = {:media_types => :all}.merge(options)
|
201
|
-
media_types = options[:media_types]
|
202
|
-
|
203
|
-
in_declarations = 0
|
204
|
-
|
205
|
-
block_depth = 0
|
206
|
-
|
207
|
-
# @charset is ignored for now
|
208
|
-
in_charset = false
|
209
|
-
in_string = false
|
210
|
-
in_at_media_rule = false
|
211
|
-
|
212
|
-
current_selectors = ''
|
213
|
-
current_declarations = ''
|
214
|
-
|
215
|
-
block.scan(/([\\]?[{}\s"]|(.[^\s"{}\\]*))/).each do |matches|
|
216
|
-
#block.scan(/((.[^{}"\n\r\f\s]*)[\s]|(.[^{}"\n\r\f]*)\{|(.[^{}"\n\r\f]*)\}|(.[^{}"\n\r\f]*)\"|(.*)[\s]+)/).each do |matches|
|
217
|
-
token = matches[0]
|
218
|
-
#puts "TOKEN: #{token}" unless token =~ /^[\s]*$/
|
219
|
-
if token =~ /\A"/ # found un-escaped double quote
|
220
|
-
in_string = !in_string
|
221
|
-
end
|
222
|
-
|
223
|
-
if in_declarations > 0
|
224
|
-
|
225
|
-
# too deep, malformed declaration block
|
226
|
-
if in_declarations > 1
|
227
|
-
in_declarations -= 1 if token =~ /\}/
|
228
|
-
next
|
229
|
-
end
|
230
|
-
|
231
|
-
if token =~ /\{/
|
232
|
-
in_declarations += 1
|
233
|
-
next
|
234
|
-
end
|
235
|
-
|
236
|
-
current_declarations += token
|
237
|
-
|
238
|
-
if token =~ /\}/ and not in_string
|
239
|
-
current_declarations.gsub!(/\}[\s]*$/, '')
|
240
|
-
|
241
|
-
in_declarations -= 1
|
242
|
-
|
243
|
-
unless current_declarations.strip.empty?
|
244
|
-
#puts "SAVING #{current_selectors} -> #{current_declarations}"
|
245
|
-
add_rule!(current_selectors, current_declarations, media_types)
|
246
|
-
end
|
247
|
-
|
248
|
-
current_selectors = ''
|
249
|
-
current_declarations = ''
|
250
|
-
end
|
251
|
-
elsif token =~ /@media/i
|
252
|
-
# found '@media', reset current media_types
|
253
|
-
in_at_media_rule = true
|
254
|
-
media_types = []
|
255
|
-
elsif in_at_media_rule
|
256
|
-
if token =~ /\{/
|
257
|
-
block_depth = block_depth + 1
|
258
|
-
in_at_media_rule = false
|
259
|
-
else
|
260
|
-
token.gsub!(/[,\s]*/, '')
|
261
|
-
media_types << token.strip.downcase.to_sym unless token.empty?
|
262
|
-
end
|
263
|
-
elsif in_charset or token =~ /@charset/i
|
264
|
-
# iterate until we are out of the charset declaration
|
265
|
-
in_charset = (token =~ /;/ ? false : true)
|
266
|
-
else
|
267
|
-
if token =~ /\}/ and not in_string
|
268
|
-
block_depth = block_depth - 1
|
269
|
-
else
|
270
|
-
if token =~ /\{/ and not in_string
|
271
|
-
current_selectors.gsub!(/^[\s]*/, '')
|
272
|
-
current_selectors.gsub!(/[\s]*$/, '')
|
273
|
-
in_declarations += 1
|
274
|
-
else
|
275
|
-
current_selectors += token
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
# Load a remote CSS file.
|
283
|
-
#
|
284
|
-
# You can also pass in file://test.css
|
285
|
-
def load_uri!(uri, base_uri = nil, media_types = :all)
|
286
|
-
uri = URI.parse(uri) unless uri.respond_to? :scheme
|
287
|
-
if uri.scheme == 'file' or uri.scheme.nil?
|
288
|
-
uri.path = File.expand_path(uri.path)
|
289
|
-
uri.scheme = 'file'
|
290
|
-
end
|
291
|
-
base_uri = uri if base_uri.nil?
|
292
|
-
|
293
|
-
src, charset = read_remote_file(uri)
|
294
|
-
|
295
|
-
if src
|
296
|
-
add_block!(src, {:media_types => media_types, :base_uri => base_uri})
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
# Load a local CSS file.
|
301
|
-
def load_file!(file_name, base_dir = nil, media_types = :all)
|
302
|
-
file_name = File.expand_path(file_name, base_dir)
|
303
|
-
return unless File.readable?(file_name)
|
304
|
-
|
305
|
-
src = IO.read(file_name)
|
306
|
-
base_dir = File.dirname(file_name)
|
307
|
-
|
308
|
-
add_block!(src, {:media_types => media_types, :base_dir => base_dir})
|
309
|
-
end
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
protected
|
314
|
-
# Strip comments and clean up blank lines from a block of CSS.
|
315
|
-
#
|
316
|
-
# Returns a string.
|
317
|
-
def cleanup_block(block) # :nodoc:
|
318
|
-
# Strip CSS comments
|
319
|
-
block.gsub!(STRIP_CSS_COMMENTS_RX, '')
|
320
|
-
|
321
|
-
# Strip HTML comments - they shouldn't really be in here but
|
322
|
-
# some people are just crazy...
|
323
|
-
block.gsub!(STRIP_HTML_COMMENTS_RX, '')
|
324
|
-
|
325
|
-
# Strip lines containing just whitespace
|
326
|
-
block.gsub!(/^\s+$/, "")
|
327
|
-
|
328
|
-
block
|
329
|
-
end
|
330
|
-
|
331
|
-
# Download a file into a string.
|
332
|
-
#
|
333
|
-
# Returns the file's data and character set in an array.
|
334
|
-
#--
|
335
|
-
# TODO: add option to fail silently or throw and exception on a 404
|
336
|
-
#++
|
337
|
-
def read_remote_file(uri) # :nodoc:
|
338
|
-
if @loaded_uris.include?(uri.to_s)
|
339
|
-
raise CircularReferenceError, "can't load #{uri.to_s} more than once" if @options[:io_exceptions]
|
340
|
-
return '', nil
|
341
|
-
end
|
342
|
-
|
343
|
-
@loaded_uris << uri.to_s
|
344
|
-
|
345
|
-
src = '', charset = nil
|
346
|
-
|
347
|
-
begin
|
348
|
-
uri = URI.parse(uri.to_s)
|
349
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
350
|
-
|
351
|
-
if uri.scheme == 'file'
|
352
|
-
# local file
|
353
|
-
fh = open(uri.path, 'rb')
|
354
|
-
src = fh.read
|
355
|
-
fh.close
|
356
|
-
else
|
357
|
-
# remote file
|
358
|
-
if uri.scheme == 'https'
|
359
|
-
http.use_ssl = true
|
360
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
361
|
-
end
|
362
|
-
|
363
|
-
res, src = http.get(uri.path, {'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip'})
|
364
|
-
charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
|
365
|
-
|
366
|
-
if res.code.to_i >= 400
|
367
|
-
raise RemoteFileError if @options[:io_exceptions]
|
368
|
-
return '', nil
|
369
|
-
end
|
370
|
-
|
371
|
-
case res['content-encoding']
|
372
|
-
when 'gzip'
|
373
|
-
io = Zlib::GzipReader.new(StringIO.new(res.body))
|
374
|
-
src = io.read
|
375
|
-
when 'deflate'
|
376
|
-
io = Zlib::Inflate.new
|
377
|
-
src = io.inflate(res.body)
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
if charset
|
382
|
-
ic = Iconv.new('UTF-8//IGNORE', charset)
|
383
|
-
src = ic.iconv(src)
|
384
|
-
end
|
385
|
-
rescue
|
386
|
-
raise RemoteFileError if @options[:io_exceptions]
|
387
|
-
return nil, nil
|
388
|
-
end
|
389
|
-
|
390
|
-
return src, charset
|
391
|
-
end
|
392
|
-
|
393
|
-
private
|
394
|
-
# Save a folded declaration block to the internal cache.
|
395
|
-
def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
|
396
|
-
@folded_declaration_cache[block_hash] = folded_declaration
|
397
|
-
end
|
398
|
-
|
399
|
-
# Retrieve a folded declaration block from the internal cache.
|
400
|
-
def get_folded_declaration(block_hash) # :nodoc:
|
401
|
-
return @folded_declaration_cache[block_hash] ||= nil
|
402
|
-
end
|
403
|
-
|
404
|
-
def reset! # :nodoc:
|
405
|
-
@folded_declaration_cache = {}
|
406
|
-
@css_source = ''
|
407
|
-
@css_rules = []
|
408
|
-
@css_warnings = []
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|