esvg 4.2.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e72d63ad2c2cfef588743462ff62a67dafa89223
4
- data.tar.gz: 17be6a677e527aa9f510b70814e7917856ebfc19
3
+ metadata.gz: 6afe3c4e87f91799f761a2a51b28b3296f9bc1e5
4
+ data.tar.gz: 891eb3479a51840f05d37cfebdb3c88aeb63d30e
5
5
  SHA512:
6
- metadata.gz: 25445968d27bf910caa4931d703536a11cf059925bdb6ec134b8a7685d06fc80de19d36a475888cc4d0863d99b7f61edacfb4842e38cc8bb43eca225e05202b4
7
- data.tar.gz: 8a35b1ab01151de2de062bd20847cf5f4f58974d9f51a44274fe286f2781afa8477553a75918057df96d82add97fa3ac703402c397ab4018eb9be39cd46a8953
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
- build(gzip: true, print: true)
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[:path])
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 |s|
141
- @symbols << Symbol.new(s[:path], config)
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
- last_modified: @mtime,
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 @optimized_at && @optimized_at > @mtime
128
+ return @optimized if @optimized && @optimized_at > @mtime
104
129
 
105
130
  @optimized = @content
106
131
  sub_def_ids
107
132
 
108
- if @config[:optimize] && Esvg.node_module('svgo')
133
+ if svgo?
109
134
  response = Open3.capture3(%Q{#{Esvg.node_module('svgo')} --disable=removeUselessDefs -s '#{@optimized}' -o -})
110
- @optimized = response[0] if response[2].success?
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
- set_instance name.to_s, value
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!(/<svg/, "<symbol #{attributes(attr)}")
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! /id="#{id}"/, %Q{class="#{new_id}"}
236
- @optimized.gsub! /url\(##{id}\)/, "url(##{new_id})"
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! /id="#{id}"/, %Q{class="#{id}"}
271
+ @optimized = @optimized.gsub /id="#{id}"/, %Q{class="#{id}"}
244
272
  end
245
273
  end
246
274
  end
data/lib/esvg/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Esvg
2
- VERSION = "4.2.0"
2
+ VERSION = "4.2.1"
3
3
  end
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.0
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-24 00:00:00.000000000 Z
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