esvg 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/exe/esvg +8 -0
- data/lib/esvg.rb +5 -5
- data/lib/esvg/svg.rb +187 -156
- data/lib/esvg/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0929542e9c469ca502b8d6beb9f5bbf779a23d41
|
4
|
+
data.tar.gz: 2dc3070f381f16fa28c993277fdf19d6632c35ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5126999322552fa4ba22ccf16b459004e6851eb6b2f321992cc20ce93d720a0f3684dbdf34d2d84cd0431e6442cad7c2c5b072a63fcebaaef1ab39f3cf62ad77
|
7
|
+
data.tar.gz: 061e2c49fc7e2d537a5947048651da403c2b12fd78050d08756125b5dcd31d81397ff874bb7988b83e5b66ae346af700f85e3b0c4fa057303c9a6e4e7a3220d4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### 2.4.0 (2015-10-17)
|
4
|
+
- New: Caching is now based on file modification times. It's much faster and more efficient.
|
5
|
+
- New: Optimization is much faster now too, as it happens after symbol concatenation, so it only runs once per build.
|
6
|
+
- Change: Configuration `svgo_path` is now `npm_path` and points to the path where the `node_modules` folder can be found.
|
7
|
+
|
3
8
|
### 2.3.1 (2015-10-15)
|
4
9
|
- Minor: Added `svgo_path` config option to specifiy a direct path to the svgo binary.
|
5
10
|
|
data/exe/esvg
CHANGED
@@ -19,6 +19,14 @@ OptionParser.new do |opts|
|
|
19
19
|
opts.on("-c", "--config PATH", String, "Path to a config file (default: esvg.yml, config/esvg.yml)") do |path|
|
20
20
|
options[:config_file] = path
|
21
21
|
end
|
22
|
+
|
23
|
+
opts.on("-O", "--optimize", "Optimize svgs with svgo") do |svgo|
|
24
|
+
options[:optimize] = svgo
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-n", "--npm", String, "Path to node_modules director") do |path|
|
28
|
+
options[:npm_path] = path
|
29
|
+
end
|
22
30
|
end.parse!
|
23
31
|
|
24
32
|
options[:path] = ARGV.shift
|
data/lib/esvg.rb
CHANGED
@@ -12,10 +12,10 @@ module Esvg
|
|
12
12
|
extend self
|
13
13
|
|
14
14
|
def icons(options={})
|
15
|
-
@icons
|
16
|
-
|
17
|
-
|
18
|
-
@icons.
|
15
|
+
if @icons.nil?
|
16
|
+
@icons = SVG.new(options)
|
17
|
+
elsif !rails? || (rails? && ::Rails.env.downcase != 'production')
|
18
|
+
@icons.read_files
|
19
19
|
end
|
20
20
|
|
21
21
|
@icons
|
@@ -26,7 +26,7 @@ module Esvg
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def svg_icon(name, options={})
|
29
|
-
icons.svg_icon(name, options)
|
29
|
+
@icons.svg_icon(name, options)
|
30
30
|
end
|
31
31
|
|
32
32
|
def rails?
|
data/lib/esvg/svg.rb
CHANGED
@@ -9,7 +9,7 @@ module Esvg
|
|
9
9
|
base_class: 'svg-icon',
|
10
10
|
namespace: 'icon',
|
11
11
|
optimize: false,
|
12
|
-
|
12
|
+
npm_path: false,
|
13
13
|
namespace_before: true,
|
14
14
|
font_size: '1em',
|
15
15
|
output_path: Dir.pwd,
|
@@ -23,17 +23,42 @@ module Esvg
|
|
23
23
|
|
24
24
|
def initialize(options={})
|
25
25
|
config(options)
|
26
|
-
|
26
|
+
|
27
27
|
@svgo = nil
|
28
|
-
@
|
28
|
+
@svgs = {}
|
29
|
+
|
30
|
+
read_files
|
29
31
|
end
|
30
32
|
|
31
|
-
def
|
32
|
-
@
|
33
|
+
def config(options={})
|
34
|
+
@config ||= begin
|
35
|
+
paths = [options[:config_file], 'config/esvg.yml', 'esvg.yml'].compact
|
36
|
+
|
37
|
+
config = CONFIG
|
38
|
+
config.merge!(CONFIG_RAILS) if Esvg.rails?
|
39
|
+
|
40
|
+
if path = paths.select{ |p| File.exist?(p)}.first
|
41
|
+
config.merge!(symbolize_keys(YAML.load(File.read(path) || {})))
|
42
|
+
end
|
43
|
+
|
44
|
+
config.merge!(options)
|
45
|
+
|
46
|
+
if config[:verbose]
|
47
|
+
config[:path] = File.expand_path(config[:path])
|
48
|
+
config[:output_path] = File.expand_path(config[:output_path])
|
49
|
+
end
|
50
|
+
|
51
|
+
config[:js_path] ||= File.join(config[:output_path], 'esvg.js')
|
52
|
+
config[:css_path] ||= File.join(config[:output_path], 'esvg.css')
|
53
|
+
config[:html_path] ||= File.join(config[:output_path], 'esvg.html')
|
54
|
+
config.delete(:output_path)
|
55
|
+
|
56
|
+
config
|
57
|
+
end
|
33
58
|
end
|
34
59
|
|
35
60
|
def embed
|
36
|
-
return if
|
61
|
+
return if files.empty?
|
37
62
|
case config[:format]
|
38
63
|
when "html"
|
39
64
|
html
|
@@ -44,63 +69,120 @@ module Esvg
|
|
44
69
|
end
|
45
70
|
end
|
46
71
|
|
47
|
-
def
|
48
|
-
@
|
49
|
-
local_path = config[:svgo_path] || "#{Dir.pwd}/node_modules/svgo/bin/svgo"
|
72
|
+
def read_files
|
73
|
+
@files = {}
|
50
74
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
75
|
+
# Get a list of svg files and modification times
|
76
|
+
#
|
77
|
+
find_files.each do |f|
|
78
|
+
files[f] = File.mtime(f)
|
79
|
+
end
|
80
|
+
|
81
|
+
process_files
|
82
|
+
|
83
|
+
if files.empty? && config[:verbose]
|
84
|
+
puts "No svgs found at #{config[:path]}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Add new svgs, update modified svgs, remove deleted svgs
|
89
|
+
#
|
90
|
+
def process_files
|
91
|
+
files.each do |file, mtime|
|
92
|
+
name = file_key(file)
|
93
|
+
|
94
|
+
if svgs[name].nil? || svgs[name][:last_modified] != mtime
|
95
|
+
svgs[name] = process_file(file, mtime, name)
|
57
96
|
end
|
58
97
|
end
|
98
|
+
|
99
|
+
# Remove deleted svgs
|
100
|
+
#
|
101
|
+
(svgs.keys - files.keys.map {|file| file_key(file) }).each do |file|
|
102
|
+
svgs.delete(file)
|
103
|
+
end
|
59
104
|
end
|
60
105
|
|
61
|
-
def
|
62
|
-
|
106
|
+
def process_file(file, mtime, name)
|
107
|
+
content = File.read(file).gsub(/<?.+\?>/,'').gsub(/<!.+?>/,'')
|
108
|
+
{
|
109
|
+
content: content,
|
110
|
+
use: use_svg(name, content),
|
111
|
+
last_modified: mtime
|
112
|
+
}
|
63
113
|
end
|
64
114
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
115
|
+
#def compress(file)
|
116
|
+
#if config[:optimize] && svgo?
|
117
|
+
#Compress files outputting to $STDOUT which returns as a string
|
118
|
+
#`#{@svgo} #{file} -o -`
|
119
|
+
#else
|
120
|
+
#File.read(file)
|
121
|
+
#end
|
122
|
+
#end
|
123
|
+
|
124
|
+
def use_svg(file, content)
|
125
|
+
name = classname(file)
|
126
|
+
%Q{<svg class="#{config[:base_class]} #{name}" #{dimensions(content)}><use xlink:href="##{name}"/></svg>}
|
127
|
+
end
|
68
128
|
|
69
|
-
|
70
|
-
|
129
|
+
def svg_icon(file, options={})
|
130
|
+
file = icon_name(file.to_s)
|
131
|
+
embed = svgs[file][:use]
|
132
|
+
embed = embed.sub(/class="(.+?)"/, 'class="\1 '+options[:class]+'"') if options[:class]
|
133
|
+
embed = embed.sub(/><\/svg/, ">#{title(options)}#{desc(options)}</svg")
|
134
|
+
embed
|
135
|
+
end
|
71
136
|
|
72
|
-
|
73
|
-
|
74
|
-
|
137
|
+
def dimensions(input)
|
138
|
+
dimension = input.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten
|
139
|
+
viewbox = dimension.first
|
140
|
+
coords = dimension.last.split(' ')
|
141
|
+
|
142
|
+
width = coords[2].to_i - coords[0].to_i
|
143
|
+
height = coords[3].to_i - coords[1].to_i
|
144
|
+
%Q{#{viewbox} width="#{width}" height="#{height}"}
|
145
|
+
end
|
75
146
|
|
76
|
-
|
77
|
-
|
147
|
+
def icon_name(name)
|
148
|
+
if svgs[name].nil?
|
149
|
+
raise "No svg named '#{name}' exists at #{config[:path]}"
|
78
150
|
end
|
151
|
+
classname(name)
|
79
152
|
end
|
80
153
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
154
|
+
def classname(name)
|
155
|
+
name = dasherize(name)
|
156
|
+
if config[:namespace_before]
|
157
|
+
"#{config[:namespace]}-#{name}"
|
158
|
+
else
|
159
|
+
"#{name}-#{config[:namespace]}"
|
84
160
|
end
|
85
161
|
end
|
86
162
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
163
|
+
def dasherize(input)
|
164
|
+
input.gsub(/[\W,_]/, '-').gsub(/-{2,}/, '-')
|
165
|
+
end
|
166
|
+
|
167
|
+
def find_files
|
168
|
+
path = File.expand_path(File.join(config[:path], '*.svg'))
|
169
|
+
Dir[path].uniq
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def title(options)
|
174
|
+
if options[:title]
|
175
|
+
"<title>#{options[:title]}</title>"
|
91
176
|
else
|
92
|
-
|
177
|
+
''
|
93
178
|
end
|
94
179
|
end
|
95
180
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
if svgo?
|
100
|
-
puts "Optimzing #{config[:path]}"
|
101
|
-
system "svgo -f #{config[:path]}"
|
181
|
+
def desc(options)
|
182
|
+
if options[:desc]
|
183
|
+
"<desc>#{options[:desc]}</desc>"
|
102
184
|
else
|
103
|
-
|
185
|
+
''
|
104
186
|
end
|
105
187
|
end
|
106
188
|
|
@@ -116,13 +198,6 @@ module Esvg
|
|
116
198
|
end
|
117
199
|
end
|
118
200
|
|
119
|
-
def write_file(path, contents)
|
120
|
-
FileUtils.mkdir_p(File.expand_path(File.dirname(path)))
|
121
|
-
File.open(path, 'w') do |io|
|
122
|
-
io.write(contents)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
201
|
def write_js
|
127
202
|
write_file config[:js_path], js
|
128
203
|
end
|
@@ -135,12 +210,18 @@ module Esvg
|
|
135
210
|
write_file config[:html_path], html
|
136
211
|
end
|
137
212
|
|
213
|
+
def write_file(path, contents)
|
214
|
+
FileUtils.mkdir_p(File.expand_path(File.dirname(path)))
|
215
|
+
File.open(path, 'w') do |io|
|
216
|
+
io.write(contents)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
138
220
|
def css
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
preamble = %Q{#{classes} {
|
221
|
+
styles = []
|
222
|
+
|
223
|
+
classes = svgs.keys.map{|k| ".#{classname(k)}"}.join(', ')
|
224
|
+
preamble = %Q{#{classes} {
|
144
225
|
font-size: #{config[:font_size]};
|
145
226
|
clip: auto;
|
146
227
|
background-size: auto;
|
@@ -156,43 +237,52 @@ module Esvg
|
|
156
237
|
vertical-align: middle;
|
157
238
|
line-height: 1em;
|
158
239
|
}}
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
240
|
+
styles << preamble
|
241
|
+
|
242
|
+
svgs.each do |name, data|
|
243
|
+
if data[:css]
|
244
|
+
styles << css
|
245
|
+
else
|
246
|
+
svg_css = data[:content].gsub(/</, '%3C') # escape <
|
247
|
+
.gsub(/>/, '%3E') # escape >
|
248
|
+
.gsub(/#/, '%23') # escape #
|
249
|
+
.gsub(/\n/,'') # remove newlines
|
250
|
+
styles << data[:css] = ".#{classname(name)} { background-image: url('data:image/svg+xml;utf-8,#{svg_css}'); }"
|
167
251
|
end
|
168
|
-
styles.join("\n")
|
169
252
|
end
|
253
|
+
styles.join("\n")
|
254
|
+
end
|
255
|
+
|
256
|
+
def prep_svg(file, content)
|
257
|
+
content.gsub(/<svg.+?>/, %Q{<svg class="#{classname(file)}" #{dimensions(content)}>}) # convert svg to symbols
|
258
|
+
.gsub(/\n/, '') # remove endlines
|
259
|
+
.gsub(/\s{2,}/, ' ') # remove whitespace
|
260
|
+
.gsub(/>\s+</, '><') # remove whitespace between tags
|
170
261
|
end
|
171
262
|
|
172
263
|
def html
|
173
|
-
@
|
174
|
-
|
175
|
-
|
264
|
+
if @files.empty?
|
265
|
+
''
|
266
|
+
else
|
267
|
+
symbols = []
|
268
|
+
svgs.each do |name, data|
|
269
|
+
symbols << prep_svg(name, data[:content])
|
270
|
+
end
|
271
|
+
|
272
|
+
symbols = if config[:optimize] && svgo?
|
273
|
+
`svgo -s '#{symbols.join}' -o -`
|
176
274
|
else
|
177
|
-
|
178
|
-
@svgs[name] = contents.gsub(/<svg.+?>/, %Q{<symbol id="#{icon_name(name)}" #{dimensions(contents)}>}) # convert svg to symbols
|
179
|
-
.gsub(/<\/svg/, '</symbol') # convert svg to symbols
|
180
|
-
.gsub(/style=['"].+?['"]/, '') # remove inline styles
|
181
|
-
.gsub(/\n/, '') # remove endlines
|
182
|
-
.gsub(/\s{2,}/, ' ') # remove whitespace
|
183
|
-
.gsub(/>\s+</, '><') # remove whitespace between tags
|
184
|
-
end
|
185
|
-
|
186
|
-
icons = @svgs
|
187
|
-
|
188
|
-
%Q{<svg id="esvg-symbols" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">#{icons.values.join("\n")}</svg>}
|
275
|
+
symbols.join
|
189
276
|
end
|
277
|
+
|
278
|
+
symbols = symbols.gsub(/class/,'id').gsub(/svg/,'symbol')
|
279
|
+
|
280
|
+
%Q{<svg id="esvg-symbols" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">#{symbols}</svg>}
|
190
281
|
end
|
191
282
|
end
|
192
283
|
|
193
284
|
def js
|
194
|
-
|
195
|
-
%Q{var esvg = {
|
285
|
+
%Q{var esvg = {
|
196
286
|
embed: function(){
|
197
287
|
if (!document.querySelector('#esvg-symbols')) {
|
198
288
|
document.querySelector('body').insertAdjacentHTML('afterbegin', '#{html.gsub(/\n/,'').gsub("'"){"\\'"}}')
|
@@ -239,96 +329,37 @@ esvg.load()
|
|
239
329
|
// Work with module exports:
|
240
330
|
if(typeof(module) != 'undefined') { module.exports = esvg }
|
241
331
|
}
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
def svg_icon(file, options={})
|
246
|
-
file = dasherize(file.to_s)
|
247
|
-
@cache[cache_name(file, options)] ||= begin
|
248
|
-
name = icon_name(file)
|
249
|
-
%Q{<svg class="#{config[:base_class]} #{name} #{options[:class] || ""}" #{dimensions(@files[file])}><use xlink:href="##{name}"/>#{title(options)}#{desc(options)}</svg>}
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
def title(options)
|
254
|
-
if options[:title]
|
255
|
-
"<title>#{options[:title]}</title>"
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
def desc(options)
|
260
|
-
if options[:desc]
|
261
|
-
"<desc>#{options[:desc]}</desc>"
|
262
|
-
end
|
263
332
|
end
|
264
333
|
|
265
|
-
def
|
266
|
-
@
|
267
|
-
|
268
|
-
|
269
|
-
config = CONFIG
|
270
|
-
config.merge!(CONFIG_RAILS) if Esvg.rails?
|
334
|
+
def svgo?
|
335
|
+
@svgo ||= begin
|
336
|
+
npm_path = "#{config[:npm_path] || Dir.pwd}/node_modules"
|
337
|
+
local_path = File.join(npm_path, "svgo/bin/svgo")
|
271
338
|
|
272
|
-
if
|
273
|
-
|
339
|
+
if config[:npm_path] && !File.exist?(npm_path)
|
340
|
+
abort "NPM Path not found: #{File.expand_path(config[:npm_path])}"
|
274
341
|
end
|
275
342
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
343
|
+
if File.exist?(local_path)
|
344
|
+
local_path
|
345
|
+
elsif `npm ls -g svgo`.match(/empty/).nil?
|
346
|
+
"svgo"
|
347
|
+
else
|
348
|
+
false
|
281
349
|
end
|
282
|
-
|
283
|
-
config[:js_path] ||= File.join(config[:output_path], 'esvg.js')
|
284
|
-
config[:css_path] ||= File.join(config[:output_path], 'esvg.css')
|
285
|
-
config[:html_path] ||= File.join(config[:output_path], 'esvg.html')
|
286
|
-
config.delete(:output_path)
|
287
|
-
|
288
|
-
config
|
289
350
|
end
|
290
351
|
end
|
291
352
|
|
353
|
+
def file_key(name)
|
354
|
+
dasherize(File.basename(name, ".*"))
|
355
|
+
end
|
356
|
+
|
357
|
+
|
292
358
|
def symbolize_keys(hash)
|
293
359
|
h = {}
|
294
360
|
hash.each {|k,v| h[k.to_sym] = v }
|
295
361
|
h
|
296
362
|
end
|
297
363
|
|
298
|
-
def dimensions(input)
|
299
|
-
dimension = input.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten
|
300
|
-
viewbox = dimension.first
|
301
|
-
coords = dimension.last.split(' ')
|
302
|
-
|
303
|
-
width = coords[2].to_i - coords[0].to_i
|
304
|
-
height = coords[3].to_i - coords[1].to_i
|
305
|
-
%Q{#{viewbox} width="#{width}" height="#{height}"}
|
306
|
-
end
|
307
|
-
|
308
|
-
def icon_name(name)
|
309
|
-
if @files[name].nil?
|
310
|
-
raise "No icon named '#{name}' exists at #{config[:path]}"
|
311
|
-
end
|
312
|
-
classname(name)
|
313
|
-
end
|
314
|
-
|
315
|
-
def classname(name)
|
316
|
-
name = dasherize(name)
|
317
|
-
if config[:namespace_before]
|
318
|
-
"#{config[:namespace]}-#{name}"
|
319
|
-
else
|
320
|
-
"#{name}-#{config[:namespace]}"
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def dasherize(input)
|
325
|
-
input.gsub(/[\W,_]/, '-').gsub(/-{2,}/, '-')
|
326
|
-
end
|
327
|
-
|
328
|
-
def find_files
|
329
|
-
path = File.expand_path(File.join(config[:path], '*.svg'))
|
330
|
-
Dir[path].uniq
|
331
|
-
end
|
332
|
-
|
333
364
|
end
|
334
365
|
end
|
data/lib/esvg/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: esvg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Mathis
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|