color_parser 0.1.0 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +0 -2
- data/Guardfile +2 -2
- data/README.md +9 -18
- data/color_parser.gemspec +3 -3
- data/lib/color_parser.rb +2 -48
- data/lib/color_parser/page.rb +14 -57
- data/lib/color_parser/stylesheet.rb +27 -116
- data/lib/color_parser/version.rb +1 -1
- data/spec/fixtures/css_color/stylesheets/color_styles.css +7 -7
- data/spec/fixtures/css_color/stylesheets/frequency.css +9 -9
- data/spec/page_spec.rb +27 -182
- data/spec/stylesheet_spec.rb +43 -153
- metadata +31 -126
- data/lib/color_parser/color.rb +0 -154
- data/lib/color_parser/image.rb +0 -20
- data/lib/color_parser/request.rb +0 -21
- data/spec/color_parser_spec.rb +0 -88
- data/spec/color_spec.rb +0 -10
- data/spec/fixtures/css/absolute.html +0 -15
- data/spec/fixtures/css/inline.html +0 -34
- data/spec/fixtures/css/inline_import.html +0 -16
- data/spec/fixtures/css/invalid.html +0 -15
- data/spec/fixtures/css/relative.html +0 -15
- data/spec/fixtures/css/relative_root.html +0 -15
- data/spec/fixtures/css/stylesheets/colors.css +0 -0
- data/spec/fixtures/css/stylesheets/fonts.css +0 -0
- data/spec/fixtures/css/stylesheets/print.css +0 -3
- data/spec/fixtures/css/stylesheets/screen.css +0 -16
- data/spec/fixtures/css_images/images/apple.png +0 -0
- data/spec/fixtures/css_images/images/cantaloupe.png +0 -0
- data/spec/fixtures/css_images/images/kiwi.jpg +0 -0
- data/spec/fixtures/css_images/images/mango.png +0 -0
- data/spec/fixtures/css_images/images/pineapple.png +0 -0
- data/spec/fixtures/css_images/paths.html +0 -14
- data/spec/fixtures/css_images/stylesheets/import_paths.css +0 -4
- data/spec/fixtures/css_images/stylesheets/paths.css +0 -17
- data/spec/fixtures/css_images/stylesheets/quotes.css +0 -14
- data/spec/fixtures/css_import/index.html +0 -15
- data/spec/fixtures/css_import/stylesheets/borders.css +0 -0
- data/spec/fixtures/css_import/stylesheets/colors.css +0 -0
- data/spec/fixtures/css_import/stylesheets/fonts.css +0 -3
- data/spec/fixtures/css_import/stylesheets/ie.css +0 -3
- data/spec/fixtures/css_import/stylesheets/images.css +0 -0
- data/spec/fixtures/css_import/stylesheets/master.css +0 -12
- data/spec/fixtures/css_import/stylesheets/print.css +0 -3
- data/spec/fixtures/css_import/stylesheets/screen.css +0 -12
- data/spec/fixtures/inline_images/absolute.html +0 -14
- data/spec/fixtures/inline_images/images/apple.png +0 -0
- data/spec/fixtures/inline_images/images/kiwi.jpg +0 -0
- data/spec/fixtures/inline_images/relative.html +0 -14
- data/spec/fixtures/inline_images/relative_root.html +0 -14
- data/spec/image_spec.rb +0 -33
- data/spec/request_spec.rb +0 -12
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b64cdb42506f55875598011c744435a85be5d61ede4ead26d303349553decb2b
|
4
|
+
data.tar.gz: 609e667ff3db8104fa741b2fc02f35ccde1448f0132c5e681e848d55d65a07fe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 74dc01bbc206a7f27a7b7979491cc1e8f1da23fb315eb5b8005fdf57ce7e760420e286a98b2f0d17600520c9e4c8a489f302fa5242167a994cff1d15668e3d01
|
7
|
+
data.tar.gz: fe6f880219b08e6433d4ee649ca93dfae57db296ac16208487cb606148b41d16d0259397b43899bd8d5ece614dff509b066ab4759b685b1761fea3a20ab5b23a
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
guard 'rspec' do
|
5
5
|
watch(%r{^spec/.+_spec\.rb$})
|
6
|
-
watch(%r{^lib/
|
7
|
-
watch('spec/spec_helper.rb')
|
6
|
+
watch(%r{^lib/color_parser/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
8
|
end
|
9
9
|
|
data/README.md
CHANGED
@@ -11,33 +11,24 @@ Get colors on a given webpage
|
|
11
11
|
```ruby
|
12
12
|
page = ColorParser::Page.new("http://google.com/")
|
13
13
|
colors = page.colors
|
14
|
-
|
15
|
-
|
16
|
-
Get stylesheets on a given webpage
|
17
|
-
|
18
|
-
```ruby
|
19
|
-
page = ColorParser::Page.new("http://google.com/")
|
20
|
-
stylesheets = page.stylesheets
|
21
|
-
```
|
22
|
-
|
23
|
-
Get images on a given webpage
|
14
|
+
=> {"ffffff"=>5, "c9d7f1"=>1, "0000cc"=>2, "dd8e27"=>1, "990000"=>1,
|
15
|
+
"3366cc"=>3, "000000"=>2, "1111cc"=>5, "cccccc"=>2, "551a8b"=>1}
|
24
16
|
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
colors = page.colors_by_frequency
|
18
|
+
=> ["ffffff", "1111cc", "3366cc", "000000", "cccccc",
|
19
|
+
"0000cc", "dd8e27", "c9d7f1", "990000", "551a8b"]
|
28
20
|
```
|
29
21
|
|
30
22
|
## Installation
|
31
23
|
|
32
|
-
|
33
|
-
|
34
|
-
```
|
35
|
-
```
|
24
|
+
To install ColorParser, add the gem to your Gemfile:
|
25
|
+
|
26
|
+
```ruby
|
36
27
|
gem "color_parser"
|
37
28
|
```
|
38
29
|
|
39
30
|
## LICENSE
|
40
31
|
|
41
|
-
Copyright (c)
|
32
|
+
Copyright (c) 2013 Derek DeVries
|
42
33
|
|
43
34
|
Released under the [MIT License](http://www.opensource.org/licenses/MIT)
|
data/color_parser.gemspec
CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
21
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
22
|
gem.require_paths = ["lib"]
|
23
|
-
|
24
|
-
gem.add_runtime_dependency("
|
25
|
-
gem.add_runtime_dependency("
|
23
|
+
|
24
|
+
gem.add_runtime_dependency("stylesheet", "~> 0.1.8")
|
25
|
+
gem.add_runtime_dependency("color_conversion", "~> 0.1.0")
|
26
26
|
|
27
27
|
gem.add_development_dependency("rake")
|
28
28
|
gem.add_development_dependency("rspec", "~> 2.9")
|
data/lib/color_parser.rb
CHANGED
@@ -1,57 +1,11 @@
|
|
1
1
|
require 'uri'
|
2
|
-
require '
|
3
|
-
require '
|
2
|
+
require 'stylesheet'
|
3
|
+
require 'color_conversion'
|
4
4
|
|
5
5
|
require 'color_parser/errors'
|
6
6
|
require 'color_parser/version'
|
7
|
-
require 'color_parser/request'
|
8
7
|
require 'color_parser/page'
|
9
8
|
require 'color_parser/stylesheet'
|
10
|
-
require 'color_parser/color'
|
11
|
-
require 'color_parser/image'
|
12
9
|
|
13
10
|
module ColorParser
|
14
|
-
# Build url of an asset based on the relative/absolute url
|
15
|
-
def self.parse_asset(doc_url, asset_url)
|
16
|
-
doc_host, doc_path, doc_query = self.parse_url(doc_url)
|
17
|
-
asset_host, asset_path, asset_query = self.parse_url(asset_url)
|
18
|
-
|
19
|
-
# absolute path
|
20
|
-
host, path, query = if asset_url.include?("http")
|
21
|
-
[asset_host, asset_path, asset_query]
|
22
|
-
|
23
|
-
# root relative
|
24
|
-
elsif asset_url[0,1] == "/"
|
25
|
-
[doc_host, asset_path, asset_query]
|
26
|
-
|
27
|
-
# relative
|
28
|
-
else
|
29
|
-
path = File.expand_path("#{doc_path.gsub(/[^\/]*$/, "")}#{asset_path}", "/")
|
30
|
-
[doc_host, path, asset_query]
|
31
|
-
end
|
32
|
-
|
33
|
-
"http://#{host}#{path}#{"?"+query if query}"
|
34
|
-
end
|
35
|
-
|
36
|
-
# parse url parts
|
37
|
-
def self.parse_url(url)
|
38
|
-
begin
|
39
|
-
uri = URI.parse(url.strip)
|
40
|
-
rescue URI::InvalidURIError
|
41
|
-
uri = URI.parse(URI.escape(url.strip))
|
42
|
-
end
|
43
|
-
|
44
|
-
[uri.host, (uri.path != "" ? uri.path : "/"), uri.query]
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
# Request
|
49
|
-
|
50
|
-
def self.request=(request)
|
51
|
-
@request = request
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.request
|
55
|
-
@request ||= Request.new
|
56
|
-
end
|
57
11
|
end
|
data/lib/color_parser/page.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
module ColorParser
|
2
2
|
# a webpage
|
3
3
|
class Page
|
4
|
-
attr_reader :url, :
|
4
|
+
attr_reader :url, :text
|
5
5
|
|
6
6
|
def initialize(url)
|
7
|
-
@
|
8
|
-
@host, @path, @query = ColorParser.parse_url(url)
|
7
|
+
@style_document = self.class.style_document.new(url)
|
9
8
|
|
10
|
-
@
|
11
|
-
@
|
9
|
+
@url = @style_document.location.href
|
10
|
+
@text = @style_document.text
|
11
|
+
end
|
12
|
+
|
13
|
+
def stylesheets
|
14
|
+
@stylesheets ||= @style_document.style_sheets.map do |style_sheet|
|
15
|
+
Stylesheet.new(style_sheet)
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
def colors
|
@@ -28,61 +33,13 @@ module ColorParser
|
|
28
33
|
colors.sort {|a,b| b[1]<=>a[1] }.map {|clr| clr.first }
|
29
34
|
end
|
30
35
|
|
31
|
-
def images
|
32
|
-
@images ||= inline_images + stylesheet_images
|
33
|
-
end
|
34
36
|
|
35
|
-
def
|
36
|
-
@
|
37
|
+
def self.style_document
|
38
|
+
@style_document ||= ::Stylesheet::Document
|
37
39
|
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# find all inline styles and build new stylesheet from them
|
43
|
-
def inline_styles
|
44
|
-
doc.css("style").map do |style|
|
45
|
-
Stylesheet.new(text: style.inner_html,
|
46
|
-
type: "inline",
|
47
|
-
url: "http://#{host}#{path}")
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def external_styles
|
52
|
-
styles = []
|
53
|
-
|
54
|
-
doc.css("link[rel='stylesheet']").each do |style|
|
55
|
-
next unless href = style["href"]
|
56
|
-
|
57
|
-
asset_url = ColorParser.parse_asset(url, href)
|
58
|
-
next unless text = ColorParser.request.get(asset_url)
|
59
|
-
|
60
|
-
css = Stylesheet.new(text: text,
|
61
|
-
type: "external",
|
62
|
-
url: asset_url)
|
63
|
-
styles << css
|
64
|
-
end
|
65
|
-
|
66
|
-
styles
|
67
|
-
end
|
68
|
-
|
69
|
-
def inline_images
|
70
|
-
images = []
|
71
|
-
|
72
|
-
doc.css("img").map do |image|
|
73
|
-
next unless src = image["src"]
|
74
|
-
next unless src.match(/gif|jpg|jpeg|png|bmp/)
|
75
|
-
|
76
|
-
asset_url = ColorParser.parse_asset(url, src)
|
77
|
-
images << Image.new(asset_url)
|
78
|
-
end
|
79
|
-
|
80
|
-
images
|
81
|
-
end
|
82
|
-
|
83
|
-
def stylesheet_images
|
84
|
-
[stylesheets.map {|style| style.images }].flatten
|
41
|
+
def self.style_document=(style_document)
|
42
|
+
@style_document = style_document
|
85
43
|
end
|
86
44
|
end
|
87
|
-
|
88
45
|
end
|
@@ -1,23 +1,9 @@
|
|
1
1
|
module ColorParser
|
2
|
-
|
2
|
+
|
3
3
|
class Stylesheet
|
4
|
-
attr_reader :url, :type, :host, :path, :query, :text
|
5
4
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
@text = options[:text]
|
9
|
-
@url = options[:url]
|
10
|
-
|
11
|
-
@host, @path, @query = ColorParser.parse_url(url)
|
12
|
-
end
|
13
|
-
|
14
|
-
def name
|
15
|
-
path.split("/").last
|
16
|
-
end
|
17
|
-
|
18
|
-
# get imported stylesheets
|
19
|
-
def stylesheets
|
20
|
-
@stylesheets ||= imported_stylesheets
|
5
|
+
def initialize(style_sheet)
|
6
|
+
@style_sheet = style_sheet
|
21
7
|
end
|
22
8
|
|
23
9
|
# gst list of colors from styles
|
@@ -28,87 +14,44 @@ module ColorParser
|
|
28
14
|
def bg_colors
|
29
15
|
@bg_colors ||= parse_colors(bg_properties)
|
30
16
|
end
|
31
|
-
|
17
|
+
|
32
18
|
def text_colors
|
33
19
|
@text_colors ||= parse_colors(text_properties)
|
34
20
|
end
|
35
|
-
|
21
|
+
|
36
22
|
def border_colors
|
37
23
|
@border_colors ||= parse_colors(border_properties)
|
38
24
|
end
|
39
25
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
images << Image.new(asset_url)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
images
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
# groups of css selectors (including imported styles)
|
56
|
-
def selectors
|
57
|
-
selectors = {}
|
58
|
-
|
59
|
-
text.scan(/([^\s\}]+)[\s]*?\{(.*?)\}/m).each do |match|
|
60
|
-
selector, rule = match
|
61
|
-
selectors[selector] ||= []
|
62
|
-
selectors[selector] << rule.strip
|
63
|
-
end
|
64
|
-
|
65
|
-
# imported styles
|
66
|
-
stylesheets.each do |style|
|
67
|
-
style.selectors.each do |selector, rule|
|
68
|
-
selectors[selector] ||= []
|
69
|
-
selectors[selector] += rule
|
26
|
+
def rules
|
27
|
+
rules = {}
|
28
|
+
|
29
|
+
([@style_sheet] + style_sheets).each do |css|
|
30
|
+
css.style_rules.each do |rule|
|
31
|
+
rules[rule.selector_text] ||= {}
|
32
|
+
rules[rule.selector_text].merge!(rule.style.declarations)
|
70
33
|
end
|
71
34
|
end
|
72
35
|
|
73
|
-
|
36
|
+
rules
|
74
37
|
end
|
75
38
|
|
76
39
|
# split up selectors into properties, and return property key/value pairs
|
77
40
|
def properties
|
78
41
|
properties = []
|
79
42
|
|
80
|
-
|
81
|
-
|
82
|
-
rule.split(";").each do |property|
|
83
|
-
props = property.split(":", 2).map {|v| v.strip }
|
84
|
-
properties << props if props.size == 2
|
85
|
-
end
|
86
|
-
end
|
43
|
+
rules.values.each do |declarations|
|
44
|
+
declarations.each {|property, value| properties << [property, value] }
|
87
45
|
end
|
88
|
-
|
46
|
+
|
89
47
|
properties
|
90
48
|
end
|
91
49
|
|
92
|
-
|
93
50
|
private
|
94
51
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
styles = []
|
99
|
-
text.scan(/@import(?:\surl|\s)(.*?)[;\n]+/).each do |style|
|
100
|
-
style_path = style.first.gsub(/['"\(\);]/, "")
|
101
|
-
|
102
|
-
asset_url = ColorParser.parse_asset(url, style_path)
|
103
|
-
next unless text = ColorParser.request.get(asset_url)
|
104
|
-
|
105
|
-
css = Stylesheet.new(text: text,
|
106
|
-
type: "imported",
|
107
|
-
url: asset_url)
|
108
|
-
styles << css
|
109
|
-
end
|
110
|
-
|
111
|
-
styles
|
52
|
+
# get imported stylesheets
|
53
|
+
def style_sheets
|
54
|
+
@style_sheets ||= @style_sheet.import_rules.map {|rule| rule.style_sheet }
|
112
55
|
end
|
113
56
|
|
114
57
|
# find properties that might have a color
|
@@ -119,7 +62,7 @@ module ColorParser
|
|
119
62
|
"border-left-color", "color", "outline-color"].include?(key)
|
120
63
|
end
|
121
64
|
end
|
122
|
-
|
65
|
+
|
123
66
|
# properties with bg colors
|
124
67
|
def bg_properties
|
125
68
|
color_properties.select {|key, value| key.include?("background") }
|
@@ -137,54 +80,22 @@ module ColorParser
|
|
137
80
|
end
|
138
81
|
end
|
139
82
|
|
140
|
-
# find properties that might have an image
|
141
|
-
def image_properties
|
142
|
-
color_properties.select {|key, value| key.include?("background") }
|
143
|
-
end
|
144
|
-
|
145
83
|
def parse_colors(property_list)
|
146
84
|
colors = {}
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
# hex
|
152
|
-
hex = if matches = value.match(/#([0-9a-f]{3,6})/i)
|
153
|
-
normalize_hex(matches[1])
|
154
|
-
|
155
|
-
# rgb/rgba
|
156
|
-
elsif matches = value.match(/rgba?\((\d{1,3}[,\s]+\d{1,3}[,\s]+\d{1,3})/)
|
157
|
-
rgb_to_hex(matches[1])
|
158
|
-
|
159
|
-
# textual
|
160
|
-
elsif matches = value.match(/(#{text_colors})/)
|
161
|
-
text_to_hex(matches[1])
|
85
|
+
property_list.each do |key, value|
|
86
|
+
color = nil
|
87
|
+
value.gsub(/\s?,\s?/, ",").split(" ").each do |part|
|
88
|
+
color ||= ColorConversion::Color.new(part) rescue nil
|
162
89
|
end
|
90
|
+
next unless color
|
163
91
|
|
164
|
-
|
165
|
-
|
92
|
+
hex = color.hex.gsub("#", "")
|
166
93
|
colors[hex] ? colors[hex] += 1 : colors[hex] = 1
|
167
94
|
end
|
168
95
|
|
169
96
|
# sort by colors with most occurrances
|
170
97
|
colors
|
171
98
|
end
|
172
|
-
|
173
|
-
# convert rgb to hex
|
174
|
-
def rgb_to_hex(rgb)
|
175
|
-
r, g, b = rgb.split(",").map {|color| color.strip }
|
176
|
-
"%02x" % r + "%02x" % g + "%02x" % b
|
177
|
-
end
|
178
|
-
|
179
|
-
# find hex for textual color
|
180
|
-
def text_to_hex(color)
|
181
|
-
ColorParser::Color.text_colors[color.intern]
|
182
|
-
end
|
183
|
-
|
184
|
-
# convert 3 digit hex to 6
|
185
|
-
def normalize_hex(hex)
|
186
|
-
(hex.length == 3 ? hex[0,1]*2 + hex[1,1]*2 + hex[2,1]*2: hex).downcase
|
187
|
-
end
|
188
|
-
|
99
|
+
|
189
100
|
end
|
190
101
|
end
|
data/lib/color_parser/version.rb
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
/* color names - 008080 */
|
2
|
-
div {
|
2
|
+
div.one {
|
3
3
|
background-color: teal;
|
4
4
|
}
|
5
5
|
|
6
6
|
/* hex */
|
7
|
-
div {
|
7
|
+
div.two {
|
8
8
|
color: #386ec0;
|
9
9
|
}
|
10
10
|
|
11
11
|
/* hex short */
|
12
|
-
div {
|
12
|
+
div.three {
|
13
13
|
color: #c0c;
|
14
14
|
}
|
15
15
|
|
16
16
|
/* rgb -- 718ad7 */
|
17
|
-
div {
|
17
|
+
div.four {
|
18
18
|
color: rgb(113,138,215);
|
19
19
|
}
|
20
20
|
|
21
21
|
/* rgb spaces -- 3a5dc4 */
|
22
|
-
div {
|
22
|
+
div.five {
|
23
23
|
color: rgb(58, 93, 196);
|
24
24
|
}
|
25
25
|
|
26
26
|
/* rgba -- 29469e */
|
27
|
-
div {
|
27
|
+
div.six {
|
28
28
|
color: rgba(41,70,158,0.5);
|
29
29
|
}
|
30
30
|
|
31
31
|
/* rgba spaces -- 3f6aeb */
|
32
|
-
div {
|
32
|
+
div.seven {
|
33
33
|
color: rgba(63, 106, 235, 0.5);
|
34
34
|
}
|