esvg 4.2.0 → 4.2.1
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/lib/esvg.rb +1 -5
- data/lib/esvg/svgs.rb +4 -3
- data/lib/esvg/symbol.rb +39 -11
- data/lib/esvg/version.rb +1 -1
- metadata +2 -3
- data/lib/esvg/svg-old.rb +0 -717
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6afe3c4e87f91799f761a2a51b28b3296f9bc1e5
|
|
4
|
+
data.tar.gz: 891eb3479a51840f05d37cfebdb3c88aeb63d30e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0368905cf2e9c135c1337f112b6eea8cdf7244a7a9421d23b45708205bc61d85ab2a0ac303c03c3ec7a9cf50b68d55bdadbaeaba8c816063e5fbb9dea6acf3e4
|
|
7
|
+
data.tar.gz: e142f0dd1b4e1ab20194480171a075f2642f371fa2f90270e9a6b0e43ccf77012d4f8d0c5ce9da16fa5dc00a998371f9df18118af6970312fa5800248ea8f2dd
|
data/lib/esvg.rb
CHANGED
|
@@ -68,14 +68,10 @@ module Esvg
|
|
|
68
68
|
input
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
def build(options={})
|
|
72
|
-
new(options).build
|
|
73
|
-
end
|
|
74
|
-
|
|
75
71
|
def precompile_assets
|
|
76
72
|
if rails? && defined?(Rake)
|
|
77
73
|
::Rake::Task['assets:precompile'].enhance do
|
|
78
|
-
|
|
74
|
+
Svgs.new(gzip: true, print: true).build
|
|
79
75
|
end
|
|
80
76
|
end
|
|
81
77
|
end
|
data/lib/esvg/svgs.rb
CHANGED
|
@@ -122,7 +122,7 @@ module Esvg
|
|
|
122
122
|
puts "Writing #{file.path}" if config[:print]
|
|
123
123
|
paths << file.path
|
|
124
124
|
|
|
125
|
-
if !file.asset && config[:gzip] && gz = compress(file
|
|
125
|
+
if !file.asset && config[:gzip] && gz = compress(file.path)
|
|
126
126
|
puts "Writing #{gz}" if config[:print]
|
|
127
127
|
paths << gz
|
|
128
128
|
end
|
|
@@ -137,8 +137,9 @@ module Esvg
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def read_cache
|
|
140
|
-
(YAML.load(read_tmp '.symbols') || []).each do |
|
|
141
|
-
|
|
140
|
+
(YAML.load(read_tmp '.symbols') || []).each do |c|
|
|
141
|
+
config[:cache] = c
|
|
142
|
+
@symbols << Symbol.new(c[:path], config)
|
|
142
143
|
end
|
|
143
144
|
end
|
|
144
145
|
|
data/lib/esvg/symbol.rb
CHANGED
|
@@ -22,7 +22,16 @@ module Esvg
|
|
|
22
22
|
@content = pre_optimize File.read(@path)
|
|
23
23
|
@size = dimensions
|
|
24
24
|
@optimized = nil
|
|
25
|
+
@optimized_at = nil
|
|
25
26
|
end
|
|
27
|
+
|
|
28
|
+
# Ensure that cache optimization matches current optimization settings
|
|
29
|
+
# If config has changed name, reset optimized build (name gets baked in)
|
|
30
|
+
if @svgo_optimized != svgo? || name != file_name
|
|
31
|
+
@optimized = nil
|
|
32
|
+
@optimized_at = nil
|
|
33
|
+
end
|
|
34
|
+
|
|
26
35
|
@group = dir_key
|
|
27
36
|
@name = file_name
|
|
28
37
|
@id = file_id file_key
|
|
@@ -30,17 +39,26 @@ module Esvg
|
|
|
30
39
|
self
|
|
31
40
|
end
|
|
32
41
|
|
|
42
|
+
def width
|
|
43
|
+
@size[:width]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def height
|
|
47
|
+
@size[:height]
|
|
48
|
+
end
|
|
49
|
+
|
|
33
50
|
def data
|
|
34
51
|
{
|
|
35
52
|
path: @path,
|
|
36
53
|
id: @id,
|
|
37
54
|
name: @name,
|
|
38
55
|
group: @group,
|
|
39
|
-
|
|
56
|
+
mtime: @mtime,
|
|
40
57
|
size: @size,
|
|
41
58
|
content: @content,
|
|
42
59
|
optimized: @optimized,
|
|
43
|
-
optimized_at: @optimized_at
|
|
60
|
+
optimized_at: @optimized_at,
|
|
61
|
+
svgo_optimized: svgo? && @svgo_optimized
|
|
44
62
|
}
|
|
45
63
|
end
|
|
46
64
|
|
|
@@ -58,7 +76,8 @@ module Esvg
|
|
|
58
76
|
class: [@config[:class], @config[:prefix]+"-"+@name, options[:class]].compact.join(' '),
|
|
59
77
|
viewBox: @size[:viewBox],
|
|
60
78
|
style: options[:style],
|
|
61
|
-
fill: options[:fill]
|
|
79
|
+
fill: options[:fill],
|
|
80
|
+
role: 'img'
|
|
62
81
|
}
|
|
63
82
|
|
|
64
83
|
# If user doesn't pass a size or set scale: true
|
|
@@ -79,6 +98,8 @@ module Esvg
|
|
|
79
98
|
|
|
80
99
|
def use_tag(options={})
|
|
81
100
|
options["xlink:href"] = "##{@id}"
|
|
101
|
+
options[:width] ||= width
|
|
102
|
+
options[:height] ||= height
|
|
82
103
|
%Q{<use #{attributes(options)}/>}
|
|
83
104
|
end
|
|
84
105
|
|
|
@@ -98,16 +119,23 @@ module Esvg
|
|
|
98
119
|
end
|
|
99
120
|
end
|
|
100
121
|
|
|
122
|
+
def svgo?
|
|
123
|
+
@config[:optimize] && !!Esvg.node_module('svgo')
|
|
124
|
+
end
|
|
125
|
+
|
|
101
126
|
def optimize
|
|
102
127
|
# Only optimize again if the file has changed
|
|
103
|
-
return @optimized if @
|
|
128
|
+
return @optimized if @optimized && @optimized_at > @mtime
|
|
104
129
|
|
|
105
130
|
@optimized = @content
|
|
106
131
|
sub_def_ids
|
|
107
132
|
|
|
108
|
-
if
|
|
133
|
+
if svgo?
|
|
109
134
|
response = Open3.capture3(%Q{#{Esvg.node_module('svgo')} --disable=removeUselessDefs -s '#{@optimized}' -o -})
|
|
110
|
-
|
|
135
|
+
if !response[0].empty? && response[2].success?
|
|
136
|
+
@optimized = response[0]
|
|
137
|
+
@svgo_optimized = true
|
|
138
|
+
end
|
|
111
139
|
end
|
|
112
140
|
|
|
113
141
|
post_optimize
|
|
@@ -121,7 +149,7 @@ module Esvg
|
|
|
121
149
|
def load_data
|
|
122
150
|
if @config[:cache]
|
|
123
151
|
@config.delete(:cache).each do |name, value|
|
|
124
|
-
|
|
152
|
+
instance_variable_set("@#{name}", value)
|
|
125
153
|
end
|
|
126
154
|
end
|
|
127
155
|
end
|
|
@@ -214,7 +242,7 @@ module Esvg
|
|
|
214
242
|
@optimized.sub!(/ #{key}=".+?"/,'')
|
|
215
243
|
end
|
|
216
244
|
|
|
217
|
-
@optimized.sub
|
|
245
|
+
@optimized.sub(/<svg/, "<symbol #{attributes(attr)}")
|
|
218
246
|
end
|
|
219
247
|
|
|
220
248
|
# Scans <def> blocks for IDs
|
|
@@ -232,15 +260,15 @@ module Esvg
|
|
|
232
260
|
if @optimized.match(/url\(##{id}\)/)
|
|
233
261
|
new_id = "def-#{@id}-#{index}"
|
|
234
262
|
|
|
235
|
-
@optimized.gsub
|
|
236
|
-
|
|
263
|
+
@optimized = @optimized.gsub(/id="#{id}"/, %Q{class="#{new_id}"})
|
|
264
|
+
.gsub(/url\(##{id}\)/, "url(##{new_id})")
|
|
237
265
|
|
|
238
266
|
# Otherwise just leave the IDs of the
|
|
239
267
|
# defs and change them to classes to
|
|
240
268
|
# avoid SVGO ID mangling
|
|
241
269
|
#
|
|
242
270
|
else
|
|
243
|
-
@optimized.gsub
|
|
271
|
+
@optimized = @optimized.gsub /id="#{id}"/, %Q{class="#{id}"}
|
|
244
272
|
end
|
|
245
273
|
end
|
|
246
274
|
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: 4.2.
|
|
4
|
+
version: 4.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brandon Mathis
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-05-
|
|
11
|
+
date: 2017-05-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -89,7 +89,6 @@ files:
|
|
|
89
89
|
- lib/esvg.rb
|
|
90
90
|
- lib/esvg/helpers.rb
|
|
91
91
|
- lib/esvg/railties.rb
|
|
92
|
-
- lib/esvg/svg-old.rb
|
|
93
92
|
- lib/esvg/svg.rb
|
|
94
93
|
- lib/esvg/svgs.rb
|
|
95
94
|
- lib/esvg/symbol.rb
|
data/lib/esvg/svg-old.rb
DELETED
|
@@ -1,717 +0,0 @@
|
|
|
1
|
-
require 'yaml'
|
|
2
|
-
require 'json'
|
|
3
|
-
|
|
4
|
-
module Esvg
|
|
5
|
-
class SVG
|
|
6
|
-
attr_accessor :svgs, :last_read, :svg_symbols
|
|
7
|
-
|
|
8
|
-
include Esvg::Utils
|
|
9
|
-
|
|
10
|
-
CONFIG = {
|
|
11
|
-
filename: 'svgs',
|
|
12
|
-
class: 'svg-symbol',
|
|
13
|
-
namespace: 'svg',
|
|
14
|
-
core: true,
|
|
15
|
-
namespace_before: true,
|
|
16
|
-
optimize: false,
|
|
17
|
-
gzip: false,
|
|
18
|
-
fingerprint: true,
|
|
19
|
-
throttle_read: 4,
|
|
20
|
-
flatten: [],
|
|
21
|
-
alias: {}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
CONFIG_RAILS = {
|
|
25
|
-
source: "app/assets/svgs",
|
|
26
|
-
assets: "app/assets/javascripts",
|
|
27
|
-
build: "public/javascripts",
|
|
28
|
-
temp: "tmp"
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
def initialize(options={})
|
|
32
|
-
config(options)
|
|
33
|
-
|
|
34
|
-
@modules = {}
|
|
35
|
-
@last_read = nil
|
|
36
|
-
|
|
37
|
-
read_cache
|
|
38
|
-
read_files
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def config(options={})
|
|
42
|
-
@config ||= begin
|
|
43
|
-
paths = [options[:config_file], 'config/esvg.yml', 'esvg.yml'].compact
|
|
44
|
-
|
|
45
|
-
config = CONFIG.dup
|
|
46
|
-
|
|
47
|
-
if Esvg.rails? || options[:rails]
|
|
48
|
-
config.merge!(CONFIG_RAILS)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
if path = paths.select{ |p| File.exist?(p)}.first
|
|
52
|
-
config.merge!(symbolize_keys(YAML.load(File.read(path) || {})))
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
config.merge!(options)
|
|
56
|
-
|
|
57
|
-
config[:filename] = File.basename(config[:filename], '.*')
|
|
58
|
-
|
|
59
|
-
config[:pwd] = File.expand_path Dir.pwd
|
|
60
|
-
config[:source] = File.expand_path config[:source] || config[:pwd]
|
|
61
|
-
config[:build] = File.expand_path config[:build] || config[:pwd]
|
|
62
|
-
config[:assets] = File.expand_path config[:assets] || config[:pwd]
|
|
63
|
-
|
|
64
|
-
config[:temp] = config[:pwd] if config[:temp].nil?
|
|
65
|
-
config[:temp] = File.expand_path File.join(config[:temp], '.esvg-cache')
|
|
66
|
-
|
|
67
|
-
config[:aliases] = load_aliases(config[:alias])
|
|
68
|
-
config[:flatten] = [config[:flatten]].flatten.map { |dir| File.join(dir, '/') }.join('|')
|
|
69
|
-
|
|
70
|
-
config
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def read_files
|
|
75
|
-
if !@last_read.nil? && (Time.now.to_i - @last_read) < config[:throttle_read]
|
|
76
|
-
return
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Get a list of svg files and modification times
|
|
80
|
-
#
|
|
81
|
-
find_files
|
|
82
|
-
|
|
83
|
-
@last_read = Time.now.to_i
|
|
84
|
-
|
|
85
|
-
puts "Read #{svgs.size} files from #{config[:source]}" if config[:print]
|
|
86
|
-
|
|
87
|
-
if svgs.empty? && config[:print]
|
|
88
|
-
puts "No svgs found at #{config[:source]}"
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def find_files
|
|
93
|
-
files = Dir[File.join(config[:source], '**/*.svg')].uniq.sort
|
|
94
|
-
@svg_symbols = {}
|
|
95
|
-
|
|
96
|
-
# Remove deleted files from svg cache
|
|
97
|
-
(svgs.keys - file_keys(files)).each { |f| svgs.delete(f) }
|
|
98
|
-
|
|
99
|
-
dirs = {}
|
|
100
|
-
|
|
101
|
-
files.each do |path|
|
|
102
|
-
mtime = File.mtime(path).to_i
|
|
103
|
-
key = file_key path
|
|
104
|
-
dkey = dir_key path
|
|
105
|
-
|
|
106
|
-
# Use cache if possible
|
|
107
|
-
if svgs[key].nil? || svgs[key][:last_modified] != mtime
|
|
108
|
-
svgs[key] = process_file(path, mtime)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Name may have changed due to flatten config
|
|
112
|
-
svgs[key][:name] = file_name(path)
|
|
113
|
-
svgs[key][:attr][:name] = id(svgs[key][:name])
|
|
114
|
-
|
|
115
|
-
dirs[dkey] ||= {}
|
|
116
|
-
(dirs[dkey][:files] ||= []) << key
|
|
117
|
-
|
|
118
|
-
if dirs[dkey][:last_modified].nil? || dirs[dkey][:last_modified] < mtime
|
|
119
|
-
dirs[dkey][:last_modified] = mtime
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
dirs = sort(dirs)
|
|
124
|
-
|
|
125
|
-
dirs.each do |dir, data|
|
|
126
|
-
|
|
127
|
-
# overwrite cache if
|
|
128
|
-
if svg_symbols[dir].nil? || # No cache for this dir yet
|
|
129
|
-
svg_symbols[dir][:last_modified] != data[:last_modified] || # New or updated file
|
|
130
|
-
svg_symbols[dir][:files] != data[:files] # Changed files
|
|
131
|
-
|
|
132
|
-
attributes = data[:files].map { |f| svgs[f][:attr] }
|
|
133
|
-
mtimes = data[:files].map { |f| svgs[f][:last_modified] }.join
|
|
134
|
-
|
|
135
|
-
svg_symbols[dir] = data.merge({
|
|
136
|
-
name: dir,
|
|
137
|
-
asset: File.basename(dir).start_with?('_'),
|
|
138
|
-
version: config[:version] || Digest::MD5.hexdigest(mtimes)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
svg_symbols[dir][:path] = write_path(dir)
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
@svg_symbols = sort(@svg_symbols)
|
|
146
|
-
@svgs = sort(@svgs)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def read_cache
|
|
150
|
-
@svgs = YAML.load(read_tmp '.svgs') || {}
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def write_cache
|
|
154
|
-
return if production?
|
|
155
|
-
|
|
156
|
-
write_tmp '.svgs', sort(@svgs).to_yaml
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def sort(hash)
|
|
160
|
-
sorted = {}
|
|
161
|
-
hash.sort.each do |h|
|
|
162
|
-
sorted[h.first] = h.last
|
|
163
|
-
end
|
|
164
|
-
sorted
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def embed_script(key=nil)
|
|
168
|
-
if script = js(key)
|
|
169
|
-
"<script>#{script}</script>"
|
|
170
|
-
else
|
|
171
|
-
''
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def build_paths(keys=nil)
|
|
176
|
-
build_files(keys).map { |s| File.basename(s[:path]) }
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def build_files(keys=nil)
|
|
180
|
-
valid_keys(keys).reject do |k|
|
|
181
|
-
svg_symbols[k][:asset]
|
|
182
|
-
end.map { |k| svg_symbols[k] }
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def asset_files(keys=nil)
|
|
186
|
-
valid_keys(keys).select do |k|
|
|
187
|
-
svg_symbols[k][:asset]
|
|
188
|
-
end.map { |k| svg_symbols[k] }
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def process_file(path, mtime)
|
|
192
|
-
content = File.read(path)
|
|
193
|
-
id = id(file_key(path))
|
|
194
|
-
size_attr = dimensions(content)
|
|
195
|
-
|
|
196
|
-
{
|
|
197
|
-
path: path,
|
|
198
|
-
use: %Q{<use xlink:href="##{id}"/>},
|
|
199
|
-
last_modified: mtime,
|
|
200
|
-
attr: { id: id }.merge(size_attr),
|
|
201
|
-
content: content
|
|
202
|
-
}
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
def use(file, options={})
|
|
206
|
-
if svg = find_svg(file, options[:fallback])
|
|
207
|
-
|
|
208
|
-
if options[:color]
|
|
209
|
-
options[:style] ||= ''
|
|
210
|
-
options[:style] += "color:#{options[:color]};#{options[:style]}"
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
attr = {
|
|
214
|
-
fill: options[:fill],
|
|
215
|
-
style: options[:style],
|
|
216
|
-
viewBox: svg[:attr][:viewBox],
|
|
217
|
-
class: [config[:class], id(svg[:name]), options[:class]].compact.join(' ')
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
# If user doesn't pass a size or set scale: true
|
|
221
|
-
if !(options[:width] || options[:height] || options[:scale])
|
|
222
|
-
|
|
223
|
-
# default to svg dimensions
|
|
224
|
-
attr[:width] = svg[:attr][:width]
|
|
225
|
-
attr[:height] = svg[:attr][:height]
|
|
226
|
-
else
|
|
227
|
-
|
|
228
|
-
# Add sizes (nil options will be stripped)
|
|
229
|
-
attr[:width] = options[:width]
|
|
230
|
-
attr[:height] = options[:height]
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
use = %Q{<svg #{attributes(attr)}>#{svg[:use]}#{title(options)}#{desc(options)}#{options[:content]||''}</svg>}
|
|
234
|
-
|
|
235
|
-
if Esvg.rails?
|
|
236
|
-
use.html_safe
|
|
237
|
-
else
|
|
238
|
-
use
|
|
239
|
-
end
|
|
240
|
-
else
|
|
241
|
-
if production?
|
|
242
|
-
return ''
|
|
243
|
-
else
|
|
244
|
-
raise "no svg named '#{get_alias(file)}' exists at #{config[:source]}"
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
alias :svg_icon :use
|
|
250
|
-
|
|
251
|
-
def dimensions(input)
|
|
252
|
-
viewbox = input.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten.last
|
|
253
|
-
if viewbox
|
|
254
|
-
coords = viewbox.split(' ')
|
|
255
|
-
|
|
256
|
-
{
|
|
257
|
-
viewBox: viewbox,
|
|
258
|
-
width: coords[2].to_i - coords[0].to_i,
|
|
259
|
-
height: coords[3].to_i - coords[1].to_i
|
|
260
|
-
}
|
|
261
|
-
else
|
|
262
|
-
{}
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def attributes(hash)
|
|
267
|
-
att = []
|
|
268
|
-
hash.each do |key, value|
|
|
269
|
-
att << %Q{#{key}="#{value}"} unless value.nil?
|
|
270
|
-
end
|
|
271
|
-
att.join(' ')
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def exist?(name, fallback=nil)
|
|
275
|
-
!find_svg(name, fallback).nil?
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def find_svg(name, fallback=nil)
|
|
279
|
-
name = get_alias dasherize(name)
|
|
280
|
-
|
|
281
|
-
if svg = svgs.values.find { |v| v[:name] == name }
|
|
282
|
-
svg
|
|
283
|
-
elsif fallback
|
|
284
|
-
find_svg(fallback)
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
alias_method :exists?, :exist?
|
|
289
|
-
|
|
290
|
-
def id(name)
|
|
291
|
-
name = name_key(name)
|
|
292
|
-
if config[:namespace_before]
|
|
293
|
-
dasherize "#{config[:namespace]}-#{name}"
|
|
294
|
-
else
|
|
295
|
-
dasherize "#{name}-#{config[:namespace]}"
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def title(options)
|
|
300
|
-
if options[:title]
|
|
301
|
-
"<title>#{options[:title]}</title>"
|
|
302
|
-
else
|
|
303
|
-
''
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
def desc(options)
|
|
308
|
-
if options[:desc]
|
|
309
|
-
"<desc>#{options[:desc]}</desc>"
|
|
310
|
-
else
|
|
311
|
-
''
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
def version(key)
|
|
316
|
-
svg_symbols[key][:version]
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def build
|
|
320
|
-
paths = write_files svg_symbols.values
|
|
321
|
-
|
|
322
|
-
if config[:core]
|
|
323
|
-
path = File.join config[:assets], "_esvg.js"
|
|
324
|
-
write_file(path, js_core)
|
|
325
|
-
paths << path
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
paths
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
def write_files(files)
|
|
332
|
-
paths = []
|
|
333
|
-
|
|
334
|
-
files.each do |file|
|
|
335
|
-
content = js(file[:name])
|
|
336
|
-
|
|
337
|
-
write_file(file[:path], content)
|
|
338
|
-
puts "Writing #{file[:path]}" if config[:print]
|
|
339
|
-
paths << file[:path]
|
|
340
|
-
|
|
341
|
-
if !file[:asset] && gz = compress(file[:path])
|
|
342
|
-
puts "Writing #{gz}" if config[:print]
|
|
343
|
-
paths << gz
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
write_cache
|
|
348
|
-
|
|
349
|
-
paths
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def symbols(keys)
|
|
353
|
-
symbols = valid_keys(keys).map { |key|
|
|
354
|
-
# Build on demand
|
|
355
|
-
build_symbols(svg_symbols[key][:files])
|
|
356
|
-
}.join.gsub(/\n/,'')
|
|
357
|
-
|
|
358
|
-
%Q{<svg id="esvg-#{key_id(keys)}" version="1.1" style="height:0;position:absolute">#{symbols}</svg>}
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def build_symbols(files)
|
|
362
|
-
files.map { |file|
|
|
363
|
-
if svgs[file][:optimized_at].nil? || svgs[file][:optimized_at] < svgs[file][:last_modified]
|
|
364
|
-
svgs[file][:optimized_content] = optimize(svgs[file])
|
|
365
|
-
end
|
|
366
|
-
svgs[file][:optimized_content]
|
|
367
|
-
}.join.gsub(/\n/,'')
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
def js(key)
|
|
371
|
-
keys = valid_keys(key)
|
|
372
|
-
return if keys.empty?
|
|
373
|
-
|
|
374
|
-
script key_id(keys), symbols(keys).gsub('/n','').gsub("'"){"\\'"}
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
def script(id, symbols)
|
|
378
|
-
%Q{(function(){
|
|
379
|
-
|
|
380
|
-
function embed() {
|
|
381
|
-
if (!document.querySelector('#esvg-#{id}')) {
|
|
382
|
-
document.querySelector('body').insertAdjacentHTML('afterbegin', '#{symbols}')
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// If DOM is already ready, embed SVGs
|
|
387
|
-
if (document.readyState == 'interactive') { embed() }
|
|
388
|
-
|
|
389
|
-
// Handle Turbolinks page change events
|
|
390
|
-
if ( window.Turbolinks ) {
|
|
391
|
-
document.addEventListener("turbolinks:load", function(event) { embed() })
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Handle standard DOM ready events
|
|
395
|
-
document.addEventListener("DOMContentLoaded", function(event) { embed() })
|
|
396
|
-
})()}
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def js_core
|
|
400
|
-
%Q{(function(){
|
|
401
|
-
var names
|
|
402
|
-
|
|
403
|
-
function attr( source, name ){
|
|
404
|
-
if (typeof source == 'object')
|
|
405
|
-
return name+'="'+source.getAttribute(name)+'" '
|
|
406
|
-
else
|
|
407
|
-
return name+'="'+source+'" ' }
|
|
408
|
-
|
|
409
|
-
function dasherize( input ) {
|
|
410
|
-
return input.replace(/[\\W,_]/g, '-').replace(/-{2,}/g, '-')
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function svgName( name ) {
|
|
414
|
-
#{if config[:namespace_before]
|
|
415
|
-
%Q{return "#{config[:namespace]}-"+dasherize( name )}
|
|
416
|
-
else
|
|
417
|
-
%Q{return dasherize( name )+"-#{config[:namespace]}"}
|
|
418
|
-
end}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
function use( name, options ) {
|
|
422
|
-
options = options || {}
|
|
423
|
-
var id = dasherize( svgName( name ) )
|
|
424
|
-
var symbol = svgs()[id]
|
|
425
|
-
|
|
426
|
-
if ( symbol ) {
|
|
427
|
-
var svg = document.createRange().createContextualFragment( '<svg><use xlink:href="#'+id+'"/></svg>' ).firstChild;
|
|
428
|
-
svg.setAttribute( 'class', '#{config[:class]} '+id+' '+( options.classname || '' ).trim() )
|
|
429
|
-
svg.setAttribute( 'viewBox', symbol.getAttribute( 'viewBox' ) )
|
|
430
|
-
|
|
431
|
-
if ( !( options.width || options.height || options.scale ) ) {
|
|
432
|
-
|
|
433
|
-
svg.setAttribute('width', symbol.getAttribute('width'))
|
|
434
|
-
svg.setAttribute('height', symbol.getAttribute('height'))
|
|
435
|
-
|
|
436
|
-
} else {
|
|
437
|
-
|
|
438
|
-
if ( options.width ) svg.setAttribute( 'width', options.width )
|
|
439
|
-
if ( options.height ) svg.setAttribute( 'height', options.height )
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return svg
|
|
443
|
-
} else {
|
|
444
|
-
console.error('Cannot find "'+name+'" svg symbol. Ensure that svg scripts are loaded')
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function svgs(){
|
|
449
|
-
if ( !names ) {
|
|
450
|
-
names = {}
|
|
451
|
-
for( var symbol of document.querySelectorAll( 'svg[id^=esvg] symbol' ) ) {
|
|
452
|
-
names[symbol.getAttribute('name')] = symbol
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
return names
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
var esvg = {
|
|
459
|
-
svgs: svgs,
|
|
460
|
-
use: use
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Handle Turbolinks page change events
|
|
464
|
-
if ( window.Turbolinks ) {
|
|
465
|
-
document.addEventListener( "turbolinks:load", function( event ) { names = null; esvg.svgs() })
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if( typeof( module ) != 'undefined' ) { module.exports = esvg }
|
|
469
|
-
else window.esvg = esvg
|
|
470
|
-
|
|
471
|
-
})()}
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
private
|
|
475
|
-
|
|
476
|
-
def file_key(path)
|
|
477
|
-
dasherize sub_path(config[:source], path).sub('.svg','')
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
def dir_key(path)
|
|
481
|
-
dir = File.dirname(flatten_path(path))
|
|
482
|
-
|
|
483
|
-
# Flattened paths which should be treated as assets will use '_' as their dir key
|
|
484
|
-
# - flatten: _foo - _foo/icon.svg will have a dirkey of _
|
|
485
|
-
# - filename: _icons - treats all root or flattened files as assets
|
|
486
|
-
if dir == '.' && ( sub_path(config[:source], path).start_with?('_') || config[:filename].start_with?('_') )
|
|
487
|
-
'_'
|
|
488
|
-
else
|
|
489
|
-
dir
|
|
490
|
-
end
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
def file_name(path)
|
|
494
|
-
dasherize flatten_path(path).sub('.svg','')
|
|
495
|
-
end
|
|
496
|
-
|
|
497
|
-
def flatten_path(path)
|
|
498
|
-
sub_path(config[:source], path).sub(Regexp.new(config[:flatten]), '')
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
def file_keys(paths)
|
|
502
|
-
paths.flatten.map { |p| file_key(p) }
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
def name_key(key)
|
|
506
|
-
if key == '_' # Root level asset file
|
|
507
|
-
"_#{config[:filename]}".sub(/_+/, '_')
|
|
508
|
-
elsif key == '.' # Root level build file
|
|
509
|
-
config[:filename]
|
|
510
|
-
else
|
|
511
|
-
"#{key}"
|
|
512
|
-
end
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
def write_path(key)
|
|
516
|
-
name = name_key(key)
|
|
517
|
-
|
|
518
|
-
if name.start_with?('_') # Is it an asset?
|
|
519
|
-
File.join config[:assets], "#{name}.js"
|
|
520
|
-
else # or a build file?
|
|
521
|
-
|
|
522
|
-
# User doesn't want a fingerprinted build file and hasn't set a version
|
|
523
|
-
if !config[:fingerprint] && !config[:version]
|
|
524
|
-
File.join config[:build], "#{name}.js"
|
|
525
|
-
else
|
|
526
|
-
File.join config[:build], "#{name}-#{version(key)}.js"
|
|
527
|
-
end
|
|
528
|
-
end
|
|
529
|
-
end
|
|
530
|
-
|
|
531
|
-
def svgo?
|
|
532
|
-
!!(config[:optimize] && svgo_cmd)
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
def optimize(svg)
|
|
536
|
-
svg[:optimized_content] = pre_optimize svg[:content]
|
|
537
|
-
svg[:optimized_content] = sub_def_ids svg
|
|
538
|
-
|
|
539
|
-
if svgo?
|
|
540
|
-
response = Open3.capture3(%Q{#{} --disable=removeUselessDefs -s '#{svg[:content]}' -o -})
|
|
541
|
-
svg[:optimized_content] = response[0] if response[2].success?
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
svg[:optimized_at] = Time.now.to_i
|
|
545
|
-
svg[:optimized_content] = post_optimize svg
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
def pre_optimize(svg)
|
|
549
|
-
reg = %w(xmlns xmlns:xlink xml:space version).map { |m| "#{m}=\".+?\"" }.join('|')
|
|
550
|
-
svg.gsub(Regexp.new(reg), '') # Remove unwanted attributes
|
|
551
|
-
.sub(/.+?<svg/,'<svg') # Get rid of doctypes and comments
|
|
552
|
-
.gsub(/style="([^"]*?)fill:(.+?);/m, 'fill="\2" style="\1') # Make fill a property instead of a style
|
|
553
|
-
.gsub(/style="([^"]*?)fill-opacity:(.+?);/m, 'fill-opacity="\2" style="\1') # Move fill-opacity a property instead of a style
|
|
554
|
-
.gsub(/\n/, '') # Remove endlines
|
|
555
|
-
.gsub(/\s{2,}/, ' ') # Remove whitespace
|
|
556
|
-
.gsub(/>\s+</, '><') # Remove whitespace between tags
|
|
557
|
-
.gsub(/\s?fill="(#0{3,6}|black|rgba?\(0,0,0\))"/,'') # Strip black fill
|
|
558
|
-
end
|
|
559
|
-
|
|
560
|
-
def post_optimize(svg)
|
|
561
|
-
svg[:optimized_content] = set_attributes(svg)
|
|
562
|
-
.gsub(/<\/svg/,'</symbol') # Replace svgs with symbols
|
|
563
|
-
.gsub(/class="def-/,'id="def-') # Replace <def> classes with ids (classes are generated in sub_def_ids)
|
|
564
|
-
.gsub(/\w+=""/,'') # Remove empty attributes
|
|
565
|
-
end
|
|
566
|
-
|
|
567
|
-
def set_attributes(svg)
|
|
568
|
-
svg[:attr].keys.each { |key| svg[:optimized_content].sub!(/ #{key}=".+?"/,'') }
|
|
569
|
-
svg[:optimized_content].sub(/<svg/, "<symbol #{attributes(svg[:attr])}")
|
|
570
|
-
end
|
|
571
|
-
|
|
572
|
-
def svgo_cmd
|
|
573
|
-
find_node_module('svgo')
|
|
574
|
-
end
|
|
575
|
-
|
|
576
|
-
# Scans <def> blocks for IDs
|
|
577
|
-
# If urls(#id) are used, ensure these IDs are unique to this file
|
|
578
|
-
# Only replace IDs if urls exist to avoid replacing defs
|
|
579
|
-
# used in other svg files
|
|
580
|
-
#
|
|
581
|
-
def sub_def_ids(svg)
|
|
582
|
-
content = svg[:optimized_content]
|
|
583
|
-
name = svg[:attr][:id]
|
|
584
|
-
|
|
585
|
-
return content unless !!content.match(/<defs>/)
|
|
586
|
-
|
|
587
|
-
content.scan(/<defs>.+<\/defs>/m).flatten.each do |defs|
|
|
588
|
-
defs.scan(/id="(.+?)"/).flatten.uniq.each_with_index do |id, index|
|
|
589
|
-
|
|
590
|
-
if content.match(/url\(##{id}\)/)
|
|
591
|
-
new_id = "def-#{name}-#{index}"
|
|
592
|
-
|
|
593
|
-
content = content.gsub(/id="#{id}"/, %Q{class="#{new_id}"})
|
|
594
|
-
.gsub(/url\(##{id}\)/, "url(##{new_id})" )
|
|
595
|
-
else
|
|
596
|
-
content = content.gsub(/id="#{id}"/, %Q{class="#{id}"})
|
|
597
|
-
end
|
|
598
|
-
end
|
|
599
|
-
end
|
|
600
|
-
|
|
601
|
-
content
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
def compress(file)
|
|
605
|
-
return if !config[:gzip]
|
|
606
|
-
|
|
607
|
-
mtime = File.mtime(file)
|
|
608
|
-
gz_file = "#{file}.gz"
|
|
609
|
-
|
|
610
|
-
return if (File.exist?(gz_file) && File.mtime(gz_file) >= mtime)
|
|
611
|
-
|
|
612
|
-
File.open(gz_file, "wb") do |dest|
|
|
613
|
-
gz = ::Zlib::GzipWriter.new(dest, Zlib::BEST_COMPRESSION)
|
|
614
|
-
gz.mtime = mtime.to_i
|
|
615
|
-
IO.copy_stream(open(file), gz)
|
|
616
|
-
gz.close
|
|
617
|
-
end
|
|
618
|
-
|
|
619
|
-
File.utime(mtime, mtime, gz_file)
|
|
620
|
-
|
|
621
|
-
gz_file
|
|
622
|
-
end
|
|
623
|
-
|
|
624
|
-
def write_tmp(name, content)
|
|
625
|
-
path = File.join(config[:temp], name)
|
|
626
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
627
|
-
write_file path, content
|
|
628
|
-
path
|
|
629
|
-
end
|
|
630
|
-
|
|
631
|
-
def read_tmp(name)
|
|
632
|
-
path = File.join(config[:temp], name)
|
|
633
|
-
if File.exist? path
|
|
634
|
-
File.read path
|
|
635
|
-
else
|
|
636
|
-
''
|
|
637
|
-
end
|
|
638
|
-
end
|
|
639
|
-
|
|
640
|
-
def log_path(path)
|
|
641
|
-
File.expand_path(path).sub(config[:pwd], '').sub(/^\//,'')
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
def write_file(path, contents)
|
|
645
|
-
FileUtils.mkdir_p(File.expand_path(File.dirname(path)))
|
|
646
|
-
File.open(path, 'w') do |io|
|
|
647
|
-
io.write(contents)
|
|
648
|
-
end
|
|
649
|
-
end
|
|
650
|
-
|
|
651
|
-
def key_id(keys)
|
|
652
|
-
keys.map do |key|
|
|
653
|
-
(key == '.') ? 'symbols' : id(key)
|
|
654
|
-
end.join('-')
|
|
655
|
-
end
|
|
656
|
-
|
|
657
|
-
# Determine if an NPM module is installed by checking paths with `npm bin`
|
|
658
|
-
# Returns path to binary if installed
|
|
659
|
-
def find_node_module(cmd)
|
|
660
|
-
require 'open3'
|
|
661
|
-
|
|
662
|
-
return @modules[cmd] unless @modules[cmd].nil?
|
|
663
|
-
|
|
664
|
-
@modules[cmd] = begin
|
|
665
|
-
local = "$(npm bin)/#{cmd}"
|
|
666
|
-
global = "$(npm -g bin)/#{cmd}"
|
|
667
|
-
|
|
668
|
-
if Open3.capture3(local)[2].success?
|
|
669
|
-
local
|
|
670
|
-
elsif Open3.capture3(global)[2].success?
|
|
671
|
-
global
|
|
672
|
-
else
|
|
673
|
-
false
|
|
674
|
-
end
|
|
675
|
-
end
|
|
676
|
-
end
|
|
677
|
-
|
|
678
|
-
# Return non-empty key names for groups of svgs
|
|
679
|
-
def valid_keys(keys)
|
|
680
|
-
if keys.nil? || keys.empty?
|
|
681
|
-
svg_symbols.keys
|
|
682
|
-
else
|
|
683
|
-
keys = [keys].flatten.map { |k| dasherize k }
|
|
684
|
-
svg_symbols.keys.select { |k| keys.include? dasherize(k) }
|
|
685
|
-
end
|
|
686
|
-
end
|
|
687
|
-
|
|
688
|
-
# Load aliases from configuration.
|
|
689
|
-
# returns a hash of aliasees mapped to a name.
|
|
690
|
-
# Converts configuration YAML:
|
|
691
|
-
# alias:
|
|
692
|
-
# foo: bar
|
|
693
|
-
# baz: zip, zop
|
|
694
|
-
# To output:
|
|
695
|
-
# { :bar => "foo", :zip => "baz", :zop => "baz" }
|
|
696
|
-
#
|
|
697
|
-
def load_aliases(aliases)
|
|
698
|
-
a = {}
|
|
699
|
-
aliases.each do |name,alternates|
|
|
700
|
-
alternates.split(',').each do |val|
|
|
701
|
-
a[dasherize(val.strip).to_sym] = dasherize(name.to_s)
|
|
702
|
-
end
|
|
703
|
-
end
|
|
704
|
-
a
|
|
705
|
-
end
|
|
706
|
-
|
|
707
|
-
def get_alias(name)
|
|
708
|
-
config[:aliases][dasherize(name).to_sym] || name
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
def production?
|
|
712
|
-
config[:produciton] || if Esvg.rails?
|
|
713
|
-
Rails.env.production?
|
|
714
|
-
end
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
end
|