middleman-clowncar 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +7 -0
- data/Gemfile +17 -0
- data/LICENSE.md +20 -0
- data/README.md +97 -0
- data/Rakefile +22 -0
- data/features/clowncar_tag_build.feature +163 -0
- data/features/clowncar_tag_preview.feature +101 -0
- data/features/generate_clowncar_build.feature +75 -0
- data/features/generate_clowncar_preview.feature +55 -0
- data/features/step_definitions/server_steps.rb +0 -0
- data/features/support/env.rb +6 -0
- data/fixtures/clowncar-app/config.rb +1 -0
- data/fixtures/clowncar-app/source/images/logo-with-fallback/big.png +0 -0
- data/fixtures/clowncar-app/source/images/logo-with-fallback/fallback.png +0 -0
- data/fixtures/clowncar-app/source/images/logo-with-fallback/medium.png +0 -0
- data/fixtures/clowncar-app/source/images/logo-with-fallback/small.png +0 -0
- data/fixtures/clowncar-app/source/images/logo/big.png +0 -0
- data/fixtures/clowncar-app/source/images/logo/fallback.png +0 -0
- data/fixtures/clowncar-app/source/images/logo/medium.png +0 -0
- data/fixtures/clowncar-app/source/images/logo/small.png +0 -0
- data/fixtures/clowncar-app/source/index.html.erb +0 -0
- data/lib/middleman-clowncar.rb +7 -0
- data/lib/middleman-clowncar/extension.rb +215 -0
- data/lib/middleman-clowncar/fastimage.rb +287 -0
- data/lib/middleman-clowncar/version.rb +5 -0
- data/lib/middleman_extension.rb +1 -0
- data/middleman-clowncar.gemspec +22 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b0ef235d8132cbbe805dfa221ba354daeca05033
|
4
|
+
data.tar.gz: 4f4b3f56912f9e3d3ffc89610b419d5756bfc608
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8ce9bb579f9beae4e1f03957cc67fc6f47a84eb6eb88607e709bf71ab091a3313d8eb039ff0a05278268214085612dd6c46ae7daed8bf23c59e3d812989155af
|
7
|
+
data.tar.gz: 5a0145df3b5d61ded91c5bc6bbe25ba817390fd62861cb2cebe5ce5c2ddc39b0448d0d4d8b6617ec43ffd27050d0f9dcb83d79b82c32e522a66442a66a351d82
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in middleman-blog.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "rake", "~> 0.9.2"
|
8
|
+
gem "rdoc", "~> 3.9"
|
9
|
+
gem "yard", "~> 0.8.0"
|
10
|
+
end
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
gem "cucumber", "~> 1.2.0"
|
14
|
+
gem "fivemat"
|
15
|
+
gem "aruba", "~> 0.4.11"
|
16
|
+
gem "rspec", "~> 2.7"
|
17
|
+
end
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010-2013 Thomas Reynolds
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# middleman-clowncar
|
2
|
+
|
3
|
+
middleman-clowncar is an extension for the [Middleman](http://middlemanapp.com) static site generator that makes it easy to generate [ClownCar](https://github.com/estelle/clowncar)-style responsive images.
|
4
|
+
|
5
|
+
# Install
|
6
|
+
|
7
|
+
In an existing Middleman project:
|
8
|
+
Add `middleman-clowncar` to your `Gemfile`
|
9
|
+
```
|
10
|
+
gem "middleman-clowncar"
|
11
|
+
```
|
12
|
+
|
13
|
+
Then open your `config.rb` and add:
|
14
|
+
```
|
15
|
+
activate :clowncar
|
16
|
+
```
|
17
|
+
|
18
|
+
# API
|
19
|
+
|
20
|
+
The extension adds two helper methods.
|
21
|
+
|
22
|
+
## `generate_clowncar`
|
23
|
+
|
24
|
+
`generate_clowncar` can be used in your `config.rb` file to create a new `.svg` file in the sitemap which references the various image sizes you have available. You can then reference this `.svg` using a normal `image_tag`. Loading this SVG will be one request, then another will happen to load the correct size according to the `@media` query.
|
25
|
+
|
26
|
+
Here is an example:
|
27
|
+
|
28
|
+
```
|
29
|
+
generate_clowncar "logo"
|
30
|
+
```
|
31
|
+
|
32
|
+
This will look for a folder in `images/logo` and inspect the files within. It will create an SVG which loads the smallest possible image given the current image size. It will generate a file named: `images/logo-responsive.svg`
|
33
|
+
|
34
|
+
## `clowncar_tag`
|
35
|
+
|
36
|
+
`clowncar_tag` is used in your templates to inline an SVG clowncar directly into a page. This approach will make sure only 1 request is ever made, for the correctly sized image only.
|
37
|
+
|
38
|
+
Here are some examples:
|
39
|
+
|
40
|
+
```
|
41
|
+
<%= clowncar_tag "logo" %>
|
42
|
+
```
|
43
|
+
|
44
|
+
This will look for a folder in `images/logo` and inspect the files within. It will create an SVG which loads the smallest possible image given the current image size. It will then, base-64 encode that SVG into an `object` tag.
|
45
|
+
|
46
|
+
```
|
47
|
+
<%= clowncar_tag "logo" %>
|
48
|
+
```
|
49
|
+
|
50
|
+
## Remote Assets
|
51
|
+
|
52
|
+
Sometimes you don't actually have the resized files locally, but an external service will be handling this for you. In this case, you can pass your sizes and URLs using the `:sizes` parameter. Doing this will override any files you may have in a folder on disk. You'll also need to specify the `:width` and `:height` of the images.
|
53
|
+
|
54
|
+
```
|
55
|
+
generate_clowncar "logo", :width => 768, :height => 480, :sizes => { 768 => "//remote.com/size-768", 1024 => "//remote.com/size-2024"}
|
56
|
+
|
57
|
+
# or
|
58
|
+
|
59
|
+
<%= clowncar_tag "logo", :width => 768, :height => 480, :sizes => { 768 => "//remote.com/size-768", 1024 => "//remote.com/size-2024"} %>
|
60
|
+
```
|
61
|
+
|
62
|
+
## OldIE
|
63
|
+
|
64
|
+
Old IE (6-7) doesn't support SVG, if you want to use a fallback image for these browsers, add the `:fallback` parameter to either API method and point it at the fallback image you wish to use, relative to the `logo` folder. This only works for the embedded method. So, if you had `images/logo/fallback.png` you'd use the following method:
|
65
|
+
|
66
|
+
```
|
67
|
+
<%= clowncar_tag "logo", :fallback => "fallback.png" %>
|
68
|
+
```
|
69
|
+
|
70
|
+
## Build Status
|
71
|
+
|
72
|
+
[![Gem Version](https://badge.fury.io/rb/middleman-clowncar.png)](https://rubygems.org/gems/middleman-clowncar)
|
73
|
+
[![Build Status](https://travis-ci.org/middleman/middleman-clowncar.png)](http://travis-ci.org/middleman/middleman-clowncar)
|
74
|
+
|
75
|
+
# Community
|
76
|
+
|
77
|
+
The official community forum is available at:
|
78
|
+
|
79
|
+
http://forum.middlemanapp.com/
|
80
|
+
|
81
|
+
# Bug Reports
|
82
|
+
|
83
|
+
GitHub Issues are used for managing bug reports and feature requests. If you run into issues, please search the issues and submit new problems:
|
84
|
+
|
85
|
+
https://github.com/middleman/middleman-clowncar/issues
|
86
|
+
|
87
|
+
The best way to get quick responses to your issues and swift fixes to your bugs is to submit detailed bug reports, include test cases and respond to developer questions in a timely manner. Even better, if you know Ruby, you can submit Pull Requests containing Cucumber Features which describe how your feature should work or exploit the bug you are submitting.
|
88
|
+
|
89
|
+
# Support Us
|
90
|
+
|
91
|
+
[![Support via Gittip](https://rawgithub.com/twolfson/gittip-badge/0.1.0/dist/gittip.png)](https://www.gittip.com/tdreyno/)
|
92
|
+
|
93
|
+
[Support via Donation](https://spacebox.io/s/4dXbHBorC3)
|
94
|
+
|
95
|
+
## License
|
96
|
+
|
97
|
+
Copyright (c) 2013 Thomas Reynolds. MIT Licensed, see [LICENSE](https://github.com/middleman/middleman-clowncar/blob/master/LICENSE.md) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
|
6
|
+
Cucumber::Rake::Task.new(:cucumber, 'Run features that should pass') do |t|
|
7
|
+
ENV["TEST"] = "true"
|
8
|
+
|
9
|
+
exempt_tags = ""
|
10
|
+
exempt_tags << "--tags ~@nojava" if RUBY_PLATFORM == "java"
|
11
|
+
|
12
|
+
t.cucumber_opts = "--color --tags ~@wip #{exempt_tags} --strict --format #{ENV['CUCUMBER_FORMAT'] || 'pretty'}"
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'rake/clean'
|
16
|
+
|
17
|
+
task :test => ["cucumber"]
|
18
|
+
|
19
|
+
desc "Build HTML documentation"
|
20
|
+
task :doc do
|
21
|
+
sh 'bundle exec yard'
|
22
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
Feature: Generating SVG clowncars during preview mode
|
2
|
+
|
3
|
+
Scenario: Basic command
|
4
|
+
Given a fixture app "clowncar-app"
|
5
|
+
And a file named "source/index.html.erb" with:
|
6
|
+
"""
|
7
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/" %>
|
8
|
+
"""
|
9
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
10
|
+
When I cd to "build"
|
11
|
+
Then the following files should not exist:
|
12
|
+
| images/logo.svg |
|
13
|
+
Then the following files should exist:
|
14
|
+
| images/logo/small.png |
|
15
|
+
| images/logo/medium.png |
|
16
|
+
| images/logo/big.png |
|
17
|
+
Then the file "index.html" should contain "<object"
|
18
|
+
And the file "index.html" should contain "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
19
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
20
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
21
|
+
|
22
|
+
Scenario: Basic command with asset_host
|
23
|
+
Given a fixture app "clowncar-app"
|
24
|
+
And a file named "config.rb" with:
|
25
|
+
"""
|
26
|
+
activate :clowncar
|
27
|
+
activate :asset_host, :host => "http://localhost:4567/"
|
28
|
+
"""
|
29
|
+
And a file named "source/index.html.erb" with:
|
30
|
+
"""
|
31
|
+
<%= clowncar_tag "logo" %>
|
32
|
+
"""
|
33
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
34
|
+
When I cd to "build"
|
35
|
+
Then the following files should not exist:
|
36
|
+
| images/logo.svg |
|
37
|
+
Then the following files should exist:
|
38
|
+
| images/logo/small.png |
|
39
|
+
| images/logo/medium.png |
|
40
|
+
| images/logo/big.png |
|
41
|
+
Then the file "index.html" should contain "<object"
|
42
|
+
And the file "index.html" should contain "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
43
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
44
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
45
|
+
|
46
|
+
Scenario: With OldIE Fallback
|
47
|
+
Given a fixture app "clowncar-app"
|
48
|
+
And a file named "source/index.html.erb" with:
|
49
|
+
"""
|
50
|
+
<%= clowncar_tag "logo-with-fallback", :host => "http://localhost:4567/", :fallback => "fallback.png" %>
|
51
|
+
"""
|
52
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
53
|
+
When I cd to "build"
|
54
|
+
Then the following files should not exist:
|
55
|
+
| images/logo-with-fallback.svg |
|
56
|
+
Then the following files should exist:
|
57
|
+
| images/logo-with-fallback/small.png |
|
58
|
+
| images/logo-with-fallback/medium.png |
|
59
|
+
| images/logo-with-fallback/big.png |
|
60
|
+
Then the file "index.html" should contain "<object"
|
61
|
+
And the file "index.html" should contain "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo-with-fallback/small.png);%7D%7D"
|
62
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo-with-fallback/medium.png);%7D%7D"
|
63
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo-with-fallback/big.png);%7D%7D"
|
64
|
+
And the file "index.html" should contain "<!--[if lte IE 8]>"
|
65
|
+
And the file "index.html" should contain '<img src="/images/logo-with-fallback/fallback.png">'
|
66
|
+
And the file "index.html" should contain "<![endif]-->"
|
67
|
+
|
68
|
+
Scenario: With remote OldIE Fallback
|
69
|
+
Given a fixture app "clowncar-app"
|
70
|
+
And a file named "source/index.html.erb" with:
|
71
|
+
"""
|
72
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/", :fallback => "http://example.com/fallback.png" %>
|
73
|
+
"""
|
74
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
75
|
+
When I cd to "build"
|
76
|
+
Then the following files should not exist:
|
77
|
+
| images/logo.svg |
|
78
|
+
Then the following files should exist:
|
79
|
+
| images/logo/small.png |
|
80
|
+
| images/logo/medium.png |
|
81
|
+
| images/logo/big.png |
|
82
|
+
Then the file "index.html" should contain "<object"
|
83
|
+
And the file "index.html" should contain "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
84
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
85
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
86
|
+
And the file "index.html" should contain "<!--[if lte IE 8]>"
|
87
|
+
And the file "index.html" should contain '<img src="http://example.com/fallback.png">'
|
88
|
+
And the file "index.html" should contain "<![endif]-->"
|
89
|
+
|
90
|
+
Scenario: Reference rather than inline
|
91
|
+
Given a fixture app "clowncar-app"
|
92
|
+
And a file named "config.rb" with:
|
93
|
+
"""
|
94
|
+
activate :clowncar
|
95
|
+
generate_clowncar "logo"
|
96
|
+
"""
|
97
|
+
And a file named "source/index.html.erb" with:
|
98
|
+
"""
|
99
|
+
<%= clowncar_tag "logo", :inline => false %>
|
100
|
+
"""
|
101
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
102
|
+
When I cd to "build"
|
103
|
+
Then the following files should exist:
|
104
|
+
| images/logo.svg |
|
105
|
+
| images/logo/small.png |
|
106
|
+
| images/logo/medium.png |
|
107
|
+
| images/logo/big.png |
|
108
|
+
Then the file "index.html" should contain '<object type="image/svg+xml" data="/images/logo.svg"></object>'
|
109
|
+
|
110
|
+
Scenario: With custom sizes locally
|
111
|
+
Given a fixture app "clowncar-app"
|
112
|
+
And a file named "source/index.html.erb" with:
|
113
|
+
"""
|
114
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/", :sizes => { 768 => "big.png", 1024 => "medium.png", 1280 => "small.png" } %>
|
115
|
+
"""
|
116
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
117
|
+
When I cd to "build"
|
118
|
+
Then the following files should not exist:
|
119
|
+
| images/logo.svg |
|
120
|
+
Then the following files should exist:
|
121
|
+
| images/logo/small.png |
|
122
|
+
| images/logo/medium.png |
|
123
|
+
| images/logo/big.png |
|
124
|
+
Then the file "index.html" should contain "<object"
|
125
|
+
And the file "index.html" should contain "@media%20screen%20and%20(max-width:768px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
126
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:769px)%20and%20(max-width:1024px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
127
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:1025px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
128
|
+
|
129
|
+
Scenario: With single size
|
130
|
+
Given a fixture app "clowncar-app"
|
131
|
+
And a file named "source/index.html.erb" with:
|
132
|
+
"""
|
133
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/", :sizes => { 768 => "big.png" } %>
|
134
|
+
"""
|
135
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
136
|
+
When I cd to "build"
|
137
|
+
Then the following files should not exist:
|
138
|
+
| images/logo.svg |
|
139
|
+
Then the following files should exist:
|
140
|
+
| images/logo/small.png |
|
141
|
+
| images/logo/medium.png |
|
142
|
+
| images/logo/big.png |
|
143
|
+
Then the file "index.html" should contain "<object"
|
144
|
+
And the file "index.html" should contain "svg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D"
|
145
|
+
|
146
|
+
Scenario: With custom sizes remotely
|
147
|
+
Given a fixture app "clowncar-app"
|
148
|
+
And a file named "source/index.html.erb" with:
|
149
|
+
"""
|
150
|
+
<%= clowncar_tag "logo", :sizes => { 333 => "//remote.com/size-333", 768 => "//remote.com/size-768", 1024 => "//remote.com/size-1024" } %>
|
151
|
+
"""
|
152
|
+
Given a successfully built app at "clowncar-app" with flags "--verbose"
|
153
|
+
When I cd to "build"
|
154
|
+
Then the following files should not exist:
|
155
|
+
| images/logo.svg |
|
156
|
+
Then the following files should exist:
|
157
|
+
| images/logo/small.png |
|
158
|
+
| images/logo/medium.png |
|
159
|
+
| images/logo/big.png |
|
160
|
+
Then the file "index.html" should contain "<object"
|
161
|
+
And the file "index.html" should contain "@media%20screen%20and%20(max-width:333px)%7Bsvg%7Bbackground-image:url(//remote.com/size-333);%7D%7D"
|
162
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:334px)%20and%20(max-width:768px)%7Bsvg%7Bbackground-image:url(//remote.com/size-768);%7D%7D"
|
163
|
+
And the file "index.html" should contain "@media%20screen%20and%20(min-width:769px)%7Bsvg%7Bbackground-image:url(//remote.com/size-1024);%7D%7D"
|
@@ -0,0 +1,101 @@
|
|
1
|
+
Feature: Generating SVG clowncars during preview mode
|
2
|
+
|
3
|
+
Scenario: Basic command
|
4
|
+
Given a fixture app "clowncar-app"
|
5
|
+
And a file named "source/index.html.erb" with:
|
6
|
+
"""
|
7
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/" %>
|
8
|
+
"""
|
9
|
+
And the Server is running at "clowncar-app"
|
10
|
+
When I go to "/index.html"
|
11
|
+
Then I should see "<object"
|
12
|
+
And I should see "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
13
|
+
And I should see "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
14
|
+
And I should see "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
15
|
+
|
16
|
+
Scenario: Basic command with asset_host
|
17
|
+
Given a fixture app "clowncar-app"
|
18
|
+
And a file named "config.rb" with:
|
19
|
+
"""
|
20
|
+
activate :clowncar
|
21
|
+
activate :asset_host, :host => "http://localhost:4567/"
|
22
|
+
"""
|
23
|
+
And a file named "source/index.html.erb" with:
|
24
|
+
"""
|
25
|
+
<%= clowncar_tag "logo" %>
|
26
|
+
"""
|
27
|
+
And the Server is running at "clowncar-app"
|
28
|
+
When I go to "/index.html"
|
29
|
+
Then I should see "<object"
|
30
|
+
And I should see "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
31
|
+
And I should see "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
32
|
+
And I should see "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
33
|
+
|
34
|
+
Scenario: With OldIE Fallback
|
35
|
+
Given a fixture app "clowncar-app"
|
36
|
+
And a file named "source/index.html.erb" with:
|
37
|
+
"""
|
38
|
+
<%= clowncar_tag "logo-with-fallback", :host => "http://localhost:4567/", :fallback => "fallback.png" %>
|
39
|
+
"""
|
40
|
+
And the Server is running at "clowncar-app"
|
41
|
+
When I go to "/index.html"
|
42
|
+
Then I should see "<object"
|
43
|
+
And I should see "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo-with-fallback/small.png);%7D%7D"
|
44
|
+
And I should see "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo-with-fallback/medium.png);%7D%7D"
|
45
|
+
And I should see "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo-with-fallback/big.png);%7D%7D"
|
46
|
+
And I should see "<!--[if lte IE 8]>"
|
47
|
+
And I should see '<img src="/images/logo-with-fallback/fallback.png">'
|
48
|
+
And I should see "<![endif]-->"
|
49
|
+
|
50
|
+
Scenario: With remote OldIE Fallback
|
51
|
+
Given a fixture app "clowncar-app"
|
52
|
+
And a file named "source/index.html.erb" with:
|
53
|
+
"""
|
54
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/", :fallback => "http://example.com/fallback.png" %>
|
55
|
+
"""
|
56
|
+
And the Server is running at "clowncar-app"
|
57
|
+
When I go to "/index.html"
|
58
|
+
Then I should see "<object"
|
59
|
+
And I should see "@media%20screen%20and%20(max-width:300px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
60
|
+
And I should see "@media%20screen%20and%20(min-width:301px)%20and%20(max-width:600px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
61
|
+
And I should see "@media%20screen%20and%20(min-width:601px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
62
|
+
And I should see "<!--[if lte IE 8]>"
|
63
|
+
And I should see '<img src="http://example.com/fallback.png">'
|
64
|
+
And I should see "<![endif]-->"
|
65
|
+
|
66
|
+
Scenario: With custom sizes locally
|
67
|
+
Given a fixture app "clowncar-app"
|
68
|
+
And a file named "source/index.html.erb" with:
|
69
|
+
"""
|
70
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/", :sizes => { 768 => "big.png", 1024 => "medium.png", 1280 => "small.png" } %>
|
71
|
+
"""
|
72
|
+
And the Server is running at "clowncar-app"
|
73
|
+
When I go to "/index.html"
|
74
|
+
Then I should see "<object"
|
75
|
+
And I should see "@media%20screen%20and%20(max-width:768px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D%7D"
|
76
|
+
And I should see "@media%20screen%20and%20(min-width:769px)%20and%20(max-width:1024px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/medium.png);%7D%7D"
|
77
|
+
And I should see "@media%20screen%20and%20(min-width:1025px)%7Bsvg%7Bbackground-image:url(http://localhost:4567/images/logo/small.png);%7D%7D"
|
78
|
+
|
79
|
+
Scenario: With single size
|
80
|
+
Given a fixture app "clowncar-app"
|
81
|
+
And a file named "source/index.html.erb" with:
|
82
|
+
"""
|
83
|
+
<%= clowncar_tag "logo", :host => "http://localhost:4567/", :sizes => { 768 => "big.png" } %>
|
84
|
+
"""
|
85
|
+
And the Server is running at "clowncar-app"
|
86
|
+
When I go to "/index.html"
|
87
|
+
Then I should see "<object"
|
88
|
+
And I should see "svg%7Bbackground-image:url(http://localhost:4567/images/logo/big.png);%7D"
|
89
|
+
|
90
|
+
Scenario: With custom sizes remotely
|
91
|
+
Given a fixture app "clowncar-app"
|
92
|
+
And a file named "source/index.html.erb" with:
|
93
|
+
"""
|
94
|
+
<%= clowncar_tag "logo", :sizes => { 333 => "//remote.com/size-333", 768 => "//remote.com/size-768", 1024 => "//remote.com/size-1024" } %>
|
95
|
+
"""
|
96
|
+
And the Server is running at "clowncar-app"
|
97
|
+
When I go to "/index.html"
|
98
|
+
Then I should see "<object"
|
99
|
+
And I should see "@media%20screen%20and%20(max-width:333px)%7Bsvg%7Bbackground-image:url(//remote.com/size-333);%7D%7D"
|
100
|
+
And I should see "@media%20screen%20and%20(min-width:334px)%20and%20(max-width:768px)%7Bsvg%7Bbackground-image:url(//remote.com/size-768);%7D%7D"
|
101
|
+
And I should see "@media%20screen%20and%20(min-width:769px)%7Bsvg%7Bbackground-image:url(//remote.com/size-1024);%7D%7D"
|
@@ -0,0 +1,75 @@
|
|
1
|
+
Feature: Generating SVG clowncars during build mode
|
2
|
+
|
3
|
+
Scenario: Basic command
|
4
|
+
Given a fixture app "clowncar-app"
|
5
|
+
And a file named "config.rb" with:
|
6
|
+
"""
|
7
|
+
activate :clowncar
|
8
|
+
generate_clowncar "logo"
|
9
|
+
"""
|
10
|
+
Given a successfully built app at "clowncar-app"
|
11
|
+
When I cd to "build"
|
12
|
+
Then the following files should exist:
|
13
|
+
| images/logo.svg |
|
14
|
+
| images/logo/small.png |
|
15
|
+
| images/logo/medium.png |
|
16
|
+
| images/logo/big.png |
|
17
|
+
Then the file "images/logo.svg" should contain "<svg"
|
18
|
+
And the file "images/logo.svg" should contain "@media screen and (max-width:300px){svg{background-image:url(logo/small.png);}}"
|
19
|
+
And the file "images/logo.svg" should contain "@media screen and (min-width:301px) and (max-width:600px){svg{background-image:url(logo/medium.png);}}"
|
20
|
+
And the file "images/logo.svg" should contain "@media screen and (min-width:601px){svg{background-image:url(logo/big.png);}}"
|
21
|
+
|
22
|
+
Scenario: With custom sizes locally
|
23
|
+
Given a fixture app "clowncar-app"
|
24
|
+
And a file named "config.rb" with:
|
25
|
+
"""
|
26
|
+
activate :clowncar
|
27
|
+
generate_clowncar "logo", :sizes => { 768 => "big.png", 1024 => "medium.png", 1280 => "small.png" }
|
28
|
+
"""
|
29
|
+
Given a successfully built app at "clowncar-app"
|
30
|
+
When I cd to "build"
|
31
|
+
Then the following files should exist:
|
32
|
+
| images/logo.svg |
|
33
|
+
| images/logo/small.png |
|
34
|
+
| images/logo/medium.png |
|
35
|
+
| images/logo/big.png |
|
36
|
+
Then the file "images/logo.svg" should contain "<svg"
|
37
|
+
And the file "images/logo.svg" should contain "@media screen and (max-width:768px){svg{background-image:url(logo/big.png);}}"
|
38
|
+
And the file "images/logo.svg" should contain "@media screen and (min-width:769px) and (max-width:1024px){svg{background-image:url(logo/medium.png);}}"
|
39
|
+
And the file "images/logo.svg" should contain "@media screen and (min-width:1025px){svg{background-image:url(logo/small.png);}}"
|
40
|
+
|
41
|
+
Scenario: With single size
|
42
|
+
Given a fixture app "clowncar-app"
|
43
|
+
And a file named "config.rb" with:
|
44
|
+
"""
|
45
|
+
activate :clowncar
|
46
|
+
generate_clowncar "logo", :sizes => { 768 => "big.png" }
|
47
|
+
"""
|
48
|
+
Given a successfully built app at "clowncar-app"
|
49
|
+
When I cd to "build"
|
50
|
+
Then the following files should exist:
|
51
|
+
| images/logo.svg |
|
52
|
+
| images/logo/small.png |
|
53
|
+
| images/logo/medium.png |
|
54
|
+
| images/logo/big.png |
|
55
|
+
Then the file "images/logo.svg" should contain "<svg"
|
56
|
+
And the file "images/logo.svg" should contain "svg{background-image:url(logo/big.png);}"
|
57
|
+
|
58
|
+
Scenario: With custom sizes remotely
|
59
|
+
Given a fixture app "clowncar-app"
|
60
|
+
And a file named "config.rb" with:
|
61
|
+
"""
|
62
|
+
activate :clowncar
|
63
|
+
generate_clowncar "logo", :sizes => { 333 => "//remote.com/size-333", 768 => "//remote.com/size-768", 1024 => "//remote.com/size-1024" }
|
64
|
+
"""
|
65
|
+
Given a successfully built app at "clowncar-app"
|
66
|
+
When I cd to "build"
|
67
|
+
Then the following files should exist:
|
68
|
+
| images/logo.svg |
|
69
|
+
| images/logo/small.png |
|
70
|
+
| images/logo/medium.png |
|
71
|
+
| images/logo/big.png |
|
72
|
+
Then the file "images/logo.svg" should contain "<svg"
|
73
|
+
And the file "images/logo.svg" should contain "@media screen and (max-width:333px){svg{background-image:url(//remote.com/size-333);}}"
|
74
|
+
And the file "images/logo.svg" should contain "@media screen and (min-width:334px) and (max-width:768px){svg{background-image:url(//remote.com/size-768);}}"
|
75
|
+
And the file "images/logo.svg" should contain "@media screen and (min-width:769px){svg{background-image:url(//remote.com/size-1024);}}"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
Feature: Generating SVG clowncars during preview mode
|
2
|
+
|
3
|
+
Scenario: Basic command
|
4
|
+
Given a fixture app "clowncar-app"
|
5
|
+
And a file named "config.rb" with:
|
6
|
+
"""
|
7
|
+
activate :clowncar
|
8
|
+
generate_clowncar "logo"
|
9
|
+
"""
|
10
|
+
And the Server is running at "clowncar-app"
|
11
|
+
When I go to "/images/logo.svg"
|
12
|
+
Then I should see "<svg"
|
13
|
+
And I should see "@media screen and (max-width:300px){svg{background-image:url(logo/small.png);}}"
|
14
|
+
And I should see "@media screen and (min-width:301px) and (max-width:600px){svg{background-image:url(logo/medium.png);}}"
|
15
|
+
And I should see "@media screen and (min-width:601px){svg{background-image:url(logo/big.png);}}"
|
16
|
+
|
17
|
+
Scenario: With custom sizes locally
|
18
|
+
Given a fixture app "clowncar-app"
|
19
|
+
And a file named "config.rb" with:
|
20
|
+
"""
|
21
|
+
activate :clowncar
|
22
|
+
generate_clowncar "logo", :sizes => { 768 => "big.png", 1024 => "medium.png", 1280 => "small.png" }
|
23
|
+
"""
|
24
|
+
And the Server is running at "clowncar-app"
|
25
|
+
When I go to "/images/logo.svg"
|
26
|
+
Then I should see "<svg"
|
27
|
+
And I should see "@media screen and (max-width:768px){svg{background-image:url(logo/big.png);}}"
|
28
|
+
And I should see "@media screen and (min-width:769px) and (max-width:1024px){svg{background-image:url(logo/medium.png);}}"
|
29
|
+
And I should see "@media screen and (min-width:1025px){svg{background-image:url(logo/small.png);}}"
|
30
|
+
|
31
|
+
Scenario: With single size
|
32
|
+
Given a fixture app "clowncar-app"
|
33
|
+
And a file named "config.rb" with:
|
34
|
+
"""
|
35
|
+
activate :clowncar
|
36
|
+
generate_clowncar "logo", :sizes => { 768 => "big.png" }
|
37
|
+
"""
|
38
|
+
And the Server is running at "clowncar-app"
|
39
|
+
When I go to "/images/logo.svg"
|
40
|
+
Then I should see "<svg"
|
41
|
+
And I should see "svg{background-image:url(logo/big.png);}"
|
42
|
+
|
43
|
+
Scenario: With custom sizes remotely
|
44
|
+
Given a fixture app "clowncar-app"
|
45
|
+
And a file named "config.rb" with:
|
46
|
+
"""
|
47
|
+
activate :clowncar
|
48
|
+
generate_clowncar "logo", :sizes => { 333 => "//remote.com/size-333", 768 => "//remote.com/size-768", 1024 => "//remote.com/size-1024" }
|
49
|
+
"""
|
50
|
+
And the Server is running at "clowncar-app"
|
51
|
+
When I go to "/images/logo.svg"
|
52
|
+
Then I should see "<svg"
|
53
|
+
And I should see "@media screen and (max-width:333px){svg{background-image:url(//remote.com/size-333);}}"
|
54
|
+
And I should see "@media screen and (min-width:334px) and (max-width:768px){svg{background-image:url(//remote.com/size-768);}}"
|
55
|
+
And I should see "@media screen and (min-width:769px){svg{background-image:url(//remote.com/size-1024);}}"
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
activate :clowncar
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
File without changes
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module Middleman
|
2
|
+
class ClownCarExtension < ::Middleman::Extension
|
3
|
+
|
4
|
+
SVG_TEMPLATE = "<svg viewBox='0 0 ::width:: ::height::' preserveAspectRatio='xMidYMid meet' xmlns='http://www.w3.org/2000/svg'><style>svg{background-size:100% 100%;background-repeat:no-repeat;}::media_queries::</style></svg>"
|
5
|
+
|
6
|
+
def initialize(app, options_hash={})
|
7
|
+
super
|
8
|
+
|
9
|
+
require 'uri'
|
10
|
+
require 'pathname'
|
11
|
+
require File.join(File.dirname(__FILE__), 'fastimage')
|
12
|
+
|
13
|
+
@svg_files_to_generate = []
|
14
|
+
|
15
|
+
@ready = false
|
16
|
+
|
17
|
+
app.send :include, ClownCarConfigAPI
|
18
|
+
end
|
19
|
+
|
20
|
+
def after_configuration
|
21
|
+
@ready = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_relative_url?(path)
|
25
|
+
begin
|
26
|
+
uri = URI(path)
|
27
|
+
rescue URI::InvalidURIError
|
28
|
+
# Nothing we can do with it, it's not really a URI
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
|
32
|
+
!uri.host
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_image_path(name, path, is_relative, fallback_host)
|
36
|
+
begin
|
37
|
+
uri = URI(path)
|
38
|
+
rescue URI::InvalidURIError
|
39
|
+
# Nothing we can do with it, it's not really a URI
|
40
|
+
return path
|
41
|
+
end
|
42
|
+
|
43
|
+
if uri.host
|
44
|
+
path
|
45
|
+
else
|
46
|
+
svg_path = File.join(name, path)
|
47
|
+
|
48
|
+
if is_relative
|
49
|
+
url = app.asset_path(:images, svg_path)
|
50
|
+
|
51
|
+
if fallback_host &&is_relative_url?(url)
|
52
|
+
File.join(fallback_host, url)
|
53
|
+
else
|
54
|
+
url
|
55
|
+
end
|
56
|
+
else
|
57
|
+
svg_path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def generate_media_queries(name, sizes, is_relative, fallback_host)
|
63
|
+
output = []
|
64
|
+
|
65
|
+
if sizes.keys.length === 1
|
66
|
+
return "svg{background-image:url(#{get_image_path(name, sizes[sizes.keys.first], is_relative, fallback_host)});}"
|
67
|
+
end
|
68
|
+
|
69
|
+
previous_key = nil
|
70
|
+
sizes.keys.sort.each_with_index do |key, i|
|
71
|
+
line = ["@media screen and "]
|
72
|
+
|
73
|
+
if i == 0
|
74
|
+
line << "(max-width:#{key}px)"
|
75
|
+
elsif i == (sizes.keys.length - 1)
|
76
|
+
line << "(min-width:#{previous_key+1}px)"
|
77
|
+
else
|
78
|
+
line << "(min-width:#{previous_key+1}px) and (max-width:#{key}px)"
|
79
|
+
end
|
80
|
+
|
81
|
+
line << "{svg{background-image:url(#{get_image_path(name, sizes[key], is_relative, fallback_host)});}}"
|
82
|
+
|
83
|
+
output << line.join("")
|
84
|
+
previous_key = key
|
85
|
+
end
|
86
|
+
|
87
|
+
output.join("")
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_image_sizes(name, options)
|
91
|
+
p = Pathname(app.source_dir) + Pathname(File.join(app.images_dir, name.to_s))
|
92
|
+
|
93
|
+
return {} unless p.exist?
|
94
|
+
|
95
|
+
width = nil
|
96
|
+
height = nil
|
97
|
+
|
98
|
+
sizes = p.children.inject({}) do |sum, path|
|
99
|
+
begin
|
100
|
+
width, height = ::FastImage.size(path.to_s, :raise_on_failure => true)
|
101
|
+
rel_path = path.relative_path_from(p).to_s
|
102
|
+
|
103
|
+
unless rel_path === options[:fallback]
|
104
|
+
sum[width] = path.relative_path_from(p).to_s
|
105
|
+
end
|
106
|
+
rescue FastImage::UnknownImageType
|
107
|
+
# No message, it's just not supported
|
108
|
+
rescue
|
109
|
+
warn "Couldn't determine dimensions for image #{path}: #{$!.message}"
|
110
|
+
end
|
111
|
+
|
112
|
+
sum
|
113
|
+
end
|
114
|
+
|
115
|
+
[sizes, width, height]
|
116
|
+
end
|
117
|
+
|
118
|
+
def generate_svg(name, is_relative, options)
|
119
|
+
if options[:sizes]
|
120
|
+
sizes = options[:sizes]
|
121
|
+
width = options[:width]
|
122
|
+
height = options[:height]
|
123
|
+
else
|
124
|
+
sizes, width, height = get_image_sizes(name, options)
|
125
|
+
end
|
126
|
+
|
127
|
+
fallback_host = false
|
128
|
+
if is_relative
|
129
|
+
test_path = app.asset_path(:images, "#{name}.svg")
|
130
|
+
if is_relative_url?(test_path)
|
131
|
+
if options.has_key?(:host)
|
132
|
+
fallback_host = options[:host]
|
133
|
+
else
|
134
|
+
warn "WARNING: Inline clowncar images require absolute paths. Please set a :host value"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
media_queries = generate_media_queries(name, sizes, is_relative, fallback_host)
|
140
|
+
|
141
|
+
xml = SVG_TEMPLATE.dup
|
142
|
+
xml.sub!("::media_queries::", media_queries)
|
143
|
+
xml.sub!("::width::", width.to_s)
|
144
|
+
xml.sub!("::height::", height.to_s)
|
145
|
+
xml
|
146
|
+
end
|
147
|
+
|
148
|
+
def generate_clowncar(name, options={})
|
149
|
+
@svg_files_to_generate << [name, options]
|
150
|
+
end
|
151
|
+
|
152
|
+
def manipulate_resource_list(resources)
|
153
|
+
return resources unless @ready
|
154
|
+
|
155
|
+
resources + @svg_files_to_generate.map do |name, options|
|
156
|
+
file_name = File.join(app.images_dir, "#{name}.svg")
|
157
|
+
output = generate_svg(name, false, options)
|
158
|
+
ClownCarResource.new(app.sitemap, file_name, output)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class ClownCarResource < ::Middleman::Sitemap::Resource
|
163
|
+
def initialize(store, path, svg=nil)
|
164
|
+
super(store, path, nil)
|
165
|
+
|
166
|
+
@svg = svg
|
167
|
+
end
|
168
|
+
|
169
|
+
def render(opts={}, locs={}, &block)
|
170
|
+
@svg
|
171
|
+
end
|
172
|
+
|
173
|
+
def ignored?
|
174
|
+
false
|
175
|
+
end
|
176
|
+
|
177
|
+
def raw_data
|
178
|
+
{}
|
179
|
+
end
|
180
|
+
|
181
|
+
def metadata
|
182
|
+
@local_metadata
|
183
|
+
end
|
184
|
+
|
185
|
+
def binary?
|
186
|
+
false
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
helpers do
|
191
|
+
def clowncar_tag(name, options={})
|
192
|
+
internal = ""
|
193
|
+
|
194
|
+
if options[:fallback]
|
195
|
+
fallback_path = extensions[:clowncar].get_image_path(name, options[:fallback], true, false)
|
196
|
+
internal = %{<!--[if lte IE 8]><img src="#{fallback_path}"><![endif]-->}
|
197
|
+
end
|
198
|
+
|
199
|
+
if options.has_key?(:inline) && (options[:inline] === false)
|
200
|
+
url = asset_path(:images, "#{name}.svg")
|
201
|
+
%Q{<object type="image/svg+xml" data="#{url}">#{internal}</object>}
|
202
|
+
else
|
203
|
+
data = extensions[:clowncar].generate_svg(name, true, options)
|
204
|
+
%Q{<object type="image/svg+xml" data="data:image/svg+xml,#{::URI.escape(data)}">#{internal}</object>}
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
module ClownCarConfigAPI
|
210
|
+
def generate_clowncar(name, options={})
|
211
|
+
extensions[:clowncar].generate_clowncar(name, options)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
# FastImage finds the size or type of an image given its uri.
|
2
|
+
# It is careful to only fetch and parse as much of the image as is needed to determine the result.
|
3
|
+
# It does this by using a feature of Net::HTTP that yields strings from the resource being fetched
|
4
|
+
# as soon as the packets arrive.
|
5
|
+
#
|
6
|
+
# No external libraries such as ImageMagick are used here, this is a very lightweight solution to
|
7
|
+
# finding image information.
|
8
|
+
#
|
9
|
+
# FastImage knows about GIF, JPEG, BMP and PNG files.
|
10
|
+
#
|
11
|
+
# FastImage can also read files from the local filesystem by supplying the path instead of a uri.
|
12
|
+
# In this case FastImage uses the open-uri library to read the file in chunks of 256 bytes until
|
13
|
+
# it has enough. This is possibly a useful bandwidth-saving feature if the file is on a network
|
14
|
+
# attached disk rather than truly local.
|
15
|
+
#
|
16
|
+
# === Examples
|
17
|
+
# require 'fastimage'
|
18
|
+
#
|
19
|
+
# FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
|
20
|
+
# => [266, 56]
|
21
|
+
# FastImage.type("http://stephensykes.com/images/pngimage")
|
22
|
+
# => :png
|
23
|
+
# FastImage.type("/some/local/file.gif")
|
24
|
+
# => :gif
|
25
|
+
#
|
26
|
+
# === References
|
27
|
+
# * http://snippets.dzone.com/posts/show/805
|
28
|
+
# * http://www.anttikupila.com/flash/getting-jpg-dimensions-with-as3-without-loading-the-entire-file/
|
29
|
+
# * http://pennysmalls.com/2008/08/19/find-jpeg-dimensions-fast-in-ruby/
|
30
|
+
# * http://imagesize.rubyforge.org/
|
31
|
+
#
|
32
|
+
require 'net/https'
|
33
|
+
require 'open-uri'
|
34
|
+
|
35
|
+
class FastImage
|
36
|
+
attr_reader :size, :type
|
37
|
+
|
38
|
+
class FastImageException < StandardError # :nodoc:
|
39
|
+
end
|
40
|
+
class MoreCharsNeeded < FastImageException # :nodoc:
|
41
|
+
end
|
42
|
+
class UnknownImageType < FastImageException # :nodoc:
|
43
|
+
end
|
44
|
+
class ImageFetchFailure < FastImageException # :nodoc:
|
45
|
+
end
|
46
|
+
class SizeNotFound < FastImageException # :nodoc:
|
47
|
+
end
|
48
|
+
|
49
|
+
DefaultTimeout = 2
|
50
|
+
|
51
|
+
LocalFileChunkSize = 256
|
52
|
+
|
53
|
+
# Returns an array containing the width and height of the image.
|
54
|
+
# It will return nil if the image could not be fetched, or if the image type was not recognised.
|
55
|
+
#
|
56
|
+
# By default there is a timeout of 2 seconds for opening and reading from a remote server.
|
57
|
+
# This can be changed by passing a :timeout => number_of_seconds in the options.
|
58
|
+
#
|
59
|
+
# If you wish FastImage to raise if it cannot size the image for any reason, then pass
|
60
|
+
# :raise_on_failure => true in the options.
|
61
|
+
#
|
62
|
+
# FastImage knows about GIF, JPEG, BMP and PNG files.
|
63
|
+
#
|
64
|
+
# === Example
|
65
|
+
#
|
66
|
+
# require 'fastimage'
|
67
|
+
#
|
68
|
+
# FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
|
69
|
+
# => [266, 56]
|
70
|
+
# FastImage.size("http://stephensykes.com/images/pngimage")
|
71
|
+
# => [16, 16]
|
72
|
+
# FastImage.size("http://farm4.static.flickr.com/3023/3047236863_9dce98b836.jpg")
|
73
|
+
# => [500, 375]
|
74
|
+
# FastImage.size("http://www-ece.rice.edu/~wakin/images/lena512.bmp")
|
75
|
+
# => [512, 512]
|
76
|
+
# FastImage.size("test/fixtures/test.jpg")
|
77
|
+
# => [882, 470]
|
78
|
+
# FastImage.size("http://pennysmalls.com/does_not_exist")
|
79
|
+
# => nil
|
80
|
+
# FastImage.size("http://pennysmalls.com/does_not_exist", :raise_on_failure=>true)
|
81
|
+
# => raises FastImage::ImageFetchFailure
|
82
|
+
# FastImage.size("http://stephensykes.com/favicon.ico", :raise_on_failure=>true)
|
83
|
+
# => raises FastImage::UnknownImageType
|
84
|
+
# FastImage.size("http://stephensykes.com/favicon.ico", :raise_on_failure=>true, :timeout=>0.01)
|
85
|
+
# => raises FastImage::ImageFetchFailure
|
86
|
+
# FastImage.size("http://stephensykes.com/images/faulty.jpg", :raise_on_failure=>true)
|
87
|
+
# => raises FastImage::SizeNotFound
|
88
|
+
#
|
89
|
+
# === Supported options
|
90
|
+
# [:timeout]
|
91
|
+
# Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
|
92
|
+
# [:raise_on_failure]
|
93
|
+
# If set to true causes an exception to be raised if the image size cannot be found for any reason.
|
94
|
+
#
|
95
|
+
def self.size(uri, options={})
|
96
|
+
new(uri, options).size
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns an symbol indicating the image type fetched from a uri.
|
100
|
+
# It will return nil if the image could not be fetched, or if the image type was not recognised.
|
101
|
+
#
|
102
|
+
# By default there is a timeout of 2 seconds for opening and reading from a remote server.
|
103
|
+
# This can be changed by passing a :timeout => number_of_seconds in the options.
|
104
|
+
#
|
105
|
+
# If you wish FastImage to raise if it cannot find the type of the image for any reason, then pass
|
106
|
+
# :raise_on_failure => true in the options.
|
107
|
+
#
|
108
|
+
# === Example
|
109
|
+
#
|
110
|
+
# require 'fastimage'
|
111
|
+
#
|
112
|
+
# FastImage.type("http://stephensykes.com/images/ss.com_x.gif")
|
113
|
+
# => :gif
|
114
|
+
# FastImage.type("http://stephensykes.com/images/pngimage")
|
115
|
+
# => :png
|
116
|
+
# FastImage.type("http://farm4.static.flickr.com/3023/3047236863_9dce98b836.jpg")
|
117
|
+
# => :jpeg
|
118
|
+
# FastImage.type("http://www-ece.rice.edu/~wakin/images/lena512.bmp")
|
119
|
+
# => :bmp
|
120
|
+
# FastImage.type("test/fixtures/test.jpg")
|
121
|
+
# => :jpeg
|
122
|
+
# FastImage.type("http://pennysmalls.com/does_not_exist")
|
123
|
+
# => nil
|
124
|
+
#
|
125
|
+
# === Supported options
|
126
|
+
# [:timeout]
|
127
|
+
# Overrides the default timeout of 2 seconds. Applies both to reading from and opening the http connection.
|
128
|
+
# [:raise_on_failure]
|
129
|
+
# If set to true causes an exception to be raised if the image type cannot be found for any reason.
|
130
|
+
#
|
131
|
+
def self.type(uri, options={})
|
132
|
+
new(uri, options.merge(:type_only=>true)).type
|
133
|
+
end
|
134
|
+
|
135
|
+
def initialize(uri, options={})
|
136
|
+
@property = options[:type_only] ? :type : :size
|
137
|
+
@timeout = options[:timeout] || DefaultTimeout
|
138
|
+
@uri = uri
|
139
|
+
begin
|
140
|
+
@parsed_uri = URI.parse(uri)
|
141
|
+
rescue URI::InvalidURIError
|
142
|
+
fetch_using_open_uri
|
143
|
+
else
|
144
|
+
if @parsed_uri.scheme == "http" || @parsed_uri.scheme == "https"
|
145
|
+
fetch_using_http
|
146
|
+
else
|
147
|
+
fetch_using_open_uri
|
148
|
+
end
|
149
|
+
end
|
150
|
+
raise SizeNotFound if options[:raise_on_failure] && @property == :size && !@size
|
151
|
+
rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET,
|
152
|
+
ImageFetchFailure, Net::HTTPBadResponse, EOFError, Errno::ENOENT
|
153
|
+
raise ImageFetchFailure if options[:raise_on_failure]
|
154
|
+
rescue NoMethodError # 1.8.7p248 can raise this due to a net/http bug
|
155
|
+
raise ImageFetchFailure if options[:raise_on_failure]
|
156
|
+
rescue UnknownImageType
|
157
|
+
raise UnknownImageType if options[:raise_on_failure]
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def fetch_using_http
|
163
|
+
setup_http
|
164
|
+
@http.request_get(@parsed_uri.request_uri) do |res|
|
165
|
+
raise ImageFetchFailure unless res.is_a?(Net::HTTPSuccess)
|
166
|
+
res.read_body do |str|
|
167
|
+
break if parse_packet(str)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def setup_http
|
173
|
+
@http = Net::HTTP.new(@parsed_uri.host, @parsed_uri.port)
|
174
|
+
@http.use_ssl = (@parsed_uri.scheme == "https")
|
175
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
176
|
+
@http.open_timeout = @timeout
|
177
|
+
@http.read_timeout = @timeout
|
178
|
+
end
|
179
|
+
|
180
|
+
def fetch_using_open_uri
|
181
|
+
open(@uri) do |s|
|
182
|
+
while str = s.read(LocalFileChunkSize)
|
183
|
+
break if parse_packet(str)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# returns true once result is achieved
|
189
|
+
#
|
190
|
+
def parse_packet(str)
|
191
|
+
@str = (@unused_str || "") + str
|
192
|
+
@strpos = 0
|
193
|
+
begin
|
194
|
+
result = send("parse_#{@property}")
|
195
|
+
if result
|
196
|
+
instance_variable_set("@#{@property}", result)
|
197
|
+
true
|
198
|
+
end
|
199
|
+
rescue MoreCharsNeeded
|
200
|
+
false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_size
|
205
|
+
@type = parse_type unless @type
|
206
|
+
@strpos = 0
|
207
|
+
send("parse_size_for_#{@type}")
|
208
|
+
end
|
209
|
+
|
210
|
+
def get_chars(n)
|
211
|
+
if @strpos + n - 1 >= @str.size
|
212
|
+
@unused_str = @str[@strpos..-1]
|
213
|
+
raise MoreCharsNeeded
|
214
|
+
else
|
215
|
+
result = @str[@strpos..(@strpos + n - 1)]
|
216
|
+
@strpos += n
|
217
|
+
result
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def get_byte
|
222
|
+
get_chars(1).unpack("C")[0]
|
223
|
+
end
|
224
|
+
|
225
|
+
def read_int(str)
|
226
|
+
size_bytes = str.unpack("CC")
|
227
|
+
(size_bytes[0] << 8) + size_bytes[1]
|
228
|
+
end
|
229
|
+
|
230
|
+
def parse_type
|
231
|
+
case get_chars(2)
|
232
|
+
when "BM"
|
233
|
+
:bmp
|
234
|
+
when "GI"
|
235
|
+
:gif
|
236
|
+
when 0xff.chr + 0xd8.chr
|
237
|
+
:jpeg
|
238
|
+
when 0x89.chr + "P"
|
239
|
+
:png
|
240
|
+
else
|
241
|
+
raise UnknownImageType
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def parse_size_for_gif
|
246
|
+
get_chars(11)[6..10].unpack('SS')
|
247
|
+
end
|
248
|
+
|
249
|
+
def parse_size_for_png
|
250
|
+
get_chars(25)[16..24].unpack('NN')
|
251
|
+
end
|
252
|
+
|
253
|
+
def parse_size_for_jpeg
|
254
|
+
loop do
|
255
|
+
@state = case @state
|
256
|
+
when nil
|
257
|
+
get_chars(2)
|
258
|
+
:started
|
259
|
+
when :started
|
260
|
+
get_byte == 0xFF ? :sof : :started
|
261
|
+
when :sof
|
262
|
+
c = get_byte
|
263
|
+
if (0xe0..0xef).include?(c)
|
264
|
+
:skipframe
|
265
|
+
elsif [0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF].detect {|r| r.include? c}
|
266
|
+
:readsize
|
267
|
+
else
|
268
|
+
:skipframe
|
269
|
+
end
|
270
|
+
when :skipframe
|
271
|
+
@skip_chars = read_int(get_chars(2)) - 2
|
272
|
+
:do_skip
|
273
|
+
when :do_skip
|
274
|
+
get_chars(@skip_chars)
|
275
|
+
:started
|
276
|
+
when :readsize
|
277
|
+
s = get_chars(7)
|
278
|
+
return [read_int(s[5..6]), read_int(s[3..4])]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def parse_size_for_bmp
|
284
|
+
d = get_chars(29)[14..28]
|
285
|
+
d.unpack("C")[0] == 40 ? d[4..-1].unpack('LL') : d[4..8].unpack('SS')
|
286
|
+
end
|
287
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "middleman-clowncar"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "middleman-clowncar/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "middleman-clowncar"
|
7
|
+
s.version = Middleman::ClownCar::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Thomas Reynolds"]
|
10
|
+
s.email = ["me@tdreyno.com"]
|
11
|
+
s.homepage = "https://github.com/middleman/middleman-clowncar"
|
12
|
+
s.summary = %q{Adds ClownCar to Middleman}
|
13
|
+
s.description = %q{Adds ClownCar to Middleman}
|
14
|
+
|
15
|
+
s.rubyforge_project = "middleman-clowncar"
|
16
|
+
|
17
|
+
s.files = `git ls-files -z`.split("\0")
|
18
|
+
s.test_files = `git ls-files -z -- {fixtures,features}/*`.split("\0")
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency("middleman-core", [">= 3.1.5"])
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: middleman-clowncar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Reynolds
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: middleman-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.1.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.1.5
|
27
|
+
description: Adds ClownCar to Middleman
|
28
|
+
email:
|
29
|
+
- me@tdreyno.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- .travis.yml
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.md
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- features/clowncar_tag_build.feature
|
41
|
+
- features/clowncar_tag_preview.feature
|
42
|
+
- features/generate_clowncar_build.feature
|
43
|
+
- features/generate_clowncar_preview.feature
|
44
|
+
- features/step_definitions/server_steps.rb
|
45
|
+
- features/support/env.rb
|
46
|
+
- fixtures/clowncar-app/config.rb
|
47
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/big.png
|
48
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/fallback.png
|
49
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/medium.png
|
50
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/small.png
|
51
|
+
- fixtures/clowncar-app/source/images/logo/big.png
|
52
|
+
- fixtures/clowncar-app/source/images/logo/fallback.png
|
53
|
+
- fixtures/clowncar-app/source/images/logo/medium.png
|
54
|
+
- fixtures/clowncar-app/source/images/logo/small.png
|
55
|
+
- fixtures/clowncar-app/source/index.html.erb
|
56
|
+
- lib/middleman-clowncar.rb
|
57
|
+
- lib/middleman-clowncar/extension.rb
|
58
|
+
- lib/middleman-clowncar/fastimage.rb
|
59
|
+
- lib/middleman-clowncar/version.rb
|
60
|
+
- lib/middleman_extension.rb
|
61
|
+
- middleman-clowncar.gemspec
|
62
|
+
homepage: https://github.com/middleman/middleman-clowncar
|
63
|
+
licenses: []
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project: middleman-clowncar
|
81
|
+
rubygems_version: 2.0.3
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Adds ClownCar to Middleman
|
85
|
+
test_files:
|
86
|
+
- features/clowncar_tag_build.feature
|
87
|
+
- features/clowncar_tag_preview.feature
|
88
|
+
- features/generate_clowncar_build.feature
|
89
|
+
- features/generate_clowncar_preview.feature
|
90
|
+
- features/step_definitions/server_steps.rb
|
91
|
+
- features/support/env.rb
|
92
|
+
- fixtures/clowncar-app/config.rb
|
93
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/big.png
|
94
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/fallback.png
|
95
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/medium.png
|
96
|
+
- fixtures/clowncar-app/source/images/logo-with-fallback/small.png
|
97
|
+
- fixtures/clowncar-app/source/images/logo/big.png
|
98
|
+
- fixtures/clowncar-app/source/images/logo/fallback.png
|
99
|
+
- fixtures/clowncar-app/source/images/logo/medium.png
|
100
|
+
- fixtures/clowncar-app/source/images/logo/small.png
|
101
|
+
- fixtures/clowncar-app/source/index.html.erb
|
102
|
+
has_rdoc:
|