deadweight 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|