esvg 3.2.0 → 4.0.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/.gitignore +1 -0
- data/.travis.yml +1 -1
- data/README.md +50 -37
- data/exe/esvg +7 -11
- data/lib/esvg.rb +13 -2
- data/lib/esvg/helpers.rb +15 -8
- data/lib/esvg/svg.rb +471 -314
- 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: 103e4a996c91ea83c6ce587b3f6c7f9f21e1f3ab
|
4
|
+
data.tar.gz: e221e24b800402c241ade048d560fd578086d741
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48afc2004b3768882d80f17d338bf476116584005694c20e737ccdc0a411a67fcc1446aa50641ddcfbe214e6ed861f8e848a610b26c042129db653f23433a577
|
7
|
+
data.tar.gz: e2637534ae3808537b3b8619433d959584d9f69a2b0a078d6a8b77e964e5cbbb1647480629f55269b9639d3bc5a7c7c54ff4e3adb0f5dae988bcc1421df83503
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Esvg
|
2
2
|
|
3
|
-
|
3
|
+
Easly slip optimized, cached svgs into your workflow using standalone CLI or the simple Rails integration.
|
4
4
|
|
5
|
-
1. Converts a directory full of SVGs into a
|
6
|
-
2.
|
5
|
+
1. Converts a directory full of SVGs into a an optimized SVG using symbols for each file.
|
6
|
+
2. Build a Javascript file to inject SVGs into pages, so it's easily cacheable.
|
7
7
|
3. Offers Rails application helpers for placing icons in your views.
|
8
8
|
|
9
9
|
[](https://rubygems.org/gems/esvg)
|
@@ -28,63 +28,75 @@ Or install it yourself as:
|
|
28
28
|
|
29
29
|
## Usage: Rails
|
30
30
|
|
31
|
-
|
31
|
+
Add `Esvg.precompile_assets` to your `config/initializers/assets.rb` to add build with `rake assets:precomile`.
|
32
32
|
|
33
|
-
|
33
|
+
Add SVG files to your `app/assets/svgs/` directory.
|
34
34
|
|
35
|
-
|
35
|
+
for example:
|
36
36
|
|
37
37
|
```
|
38
|
-
|
38
|
+
app/assets/svgs
|
39
|
+
- logo.svg
|
40
|
+
- share.svg
|
41
|
+
- thumbs-up.svg
|
39
42
|
```
|
40
43
|
|
41
|
-
|
44
|
+
### Inject SVG symbols
|
45
|
+
|
46
|
+
Add this to a page or layout where SVGs should be available
|
42
47
|
|
43
48
|
```
|
44
|
-
|
49
|
+
<%= embed_svgs %>
|
45
50
|
```
|
46
51
|
|
47
|
-
|
52
|
+
**During development:**
|
53
|
+
|
54
|
+
This will embed a `<script>` which will place svg symbols at the top of your site's `<body>` as soon as the DOM is ready, and after any Turbolinks page load events.
|
55
|
+
|
56
|
+
**In Production:**
|
57
|
+
|
58
|
+
The `embed_svgs` view helper will write a `javascript_include_tag` to include the script (rather than embeding it in the page).
|
59
|
+
When you run `rake assets:precompile` this script will be built to `public/assets/svgs-{fingerprint}.js` (and a gzipped version).
|
60
|
+
|
61
|
+
This allows browsers to cache the javascript files and reduce the weight of downloading svgs for each page.
|
48
62
|
|
49
|
-
###
|
63
|
+
### Placing an SVG
|
50
64
|
|
51
|
-
To place an SVG
|
65
|
+
To place an SVG, use the `use_svg` vew helper. This helper will embed an SVG `use` tag which will reference the appropriate symbol. Here's how it works.
|
52
66
|
|
53
67
|
```
|
54
|
-
# Syntax:
|
68
|
+
# Syntax: use_svg name, [options]
|
55
69
|
|
56
70
|
# Standard example
|
57
|
-
<%=
|
71
|
+
<%= use_svg 'logo' %>
|
58
72
|
|
59
73
|
# Output:
|
60
|
-
# <svg class="
|
74
|
+
# <svg class="svg-symbol svg-logo"><use xlink:href="#svg-logo"/></svg>
|
61
75
|
|
62
76
|
# Add custom classnames
|
63
|
-
<%=
|
77
|
+
<%= use_svg 'share', class: 'disabled' %>
|
64
78
|
|
65
79
|
# Output:
|
66
|
-
# <svg class="
|
67
|
-
|
68
|
-
# Provide fallback icon if an icon is missing
|
69
|
-
<%= svg_icon 'missing', fallback: 'kitten' %>
|
70
|
-
|
71
|
-
# Output:
|
72
|
-
# <svg class="icon kitten-icon"><use xlink:href="#kitten-icon"/></svg>
|
80
|
+
# <svg class="svg-symbol svg-share disabled"><use xlink:href="#svg-share"/></svg>
|
73
81
|
|
74
82
|
# Add custom styles
|
75
|
-
<%=
|
83
|
+
<%= use_svg 'logo', style: 'fill: #c0ffee' %>
|
76
84
|
|
77
85
|
# Output:
|
78
|
-
# <svg class="
|
86
|
+
# <svg class="svg-symbol svg-logo" style="fill: #coffee;"><use xlink:href="#svg-logo"/></svg>
|
79
87
|
|
80
88
|
# Add title and desc tags for SVG accessibility.
|
81
|
-
<%=
|
89
|
+
<%= use_svg 'kitten', title: "Mr. Snuggles", desc: "A graphic of a cat snuggling a ball of yarn" %>
|
82
90
|
|
83
91
|
# Output:
|
84
92
|
# <svg class="icon kitten-icon"><use xlink:href="#kitten-icon"/>
|
85
93
|
# <title>Mr. Snuggles</title>
|
86
94
|
# <desc>A graphic of a cat snuggling a ball of yarn</desc>
|
87
95
|
# </svg>
|
96
|
+
|
97
|
+
# Provide fallback icon if an icon is missing (great for when you are generating icon names from code)
|
98
|
+
<%= use_svg 'missing', fallback: 'default' %>
|
99
|
+
|
88
100
|
```
|
89
101
|
|
90
102
|
## Usage: stand-alone CLI
|
@@ -94,10 +106,10 @@ To place an SVG icon, use the `svg_icon` helper. This helper will embed an SVG `
|
|
94
106
|
$ esvg PATH [options]
|
95
107
|
|
96
108
|
# Examples:
|
97
|
-
$ esvg
|
98
|
-
$ esvg icons
|
99
|
-
$ esvg --output
|
100
|
-
$ esvg -c --config foo.yml
|
109
|
+
$ esvg # Read icons from current directory, write js to ./svgs.js
|
110
|
+
$ esvg icons # Read icons from 'icons' directory, write js to ./svgs.js
|
111
|
+
$ esvg --output build # Read icons from current directory, write js to build/svgs.js
|
112
|
+
$ esvg -c --config foo.yml # Read confguration from foo.yml (otherwise, defaults to esvg.yml, or config/esvg.yml)
|
101
113
|
```
|
102
114
|
|
103
115
|
## Configuration
|
@@ -105,13 +117,14 @@ $ esvg -c --config foo.yml # Read confguration from foo.yml (otherwise, def
|
|
105
117
|
If you're using esvg from the command line, configuration files are read from `./esvg.yml` or you can pass a path with the `--config` option to read the config file from elsewhere.
|
106
118
|
|
107
119
|
```
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
120
|
+
source: . # Where to find SVG icons (Rails defaults to app/assets/esvg)
|
121
|
+
build: . # Where to write build files
|
122
|
+
assets: . # Where to write asset files (builds for directories beginning in _)
|
123
|
+
tmp: . # Write temporary cache files (will write to #{dir}/.esvg-cache/
|
124
|
+
|
125
|
+
class: svg-symbol # All svgs with `use_svg` will have this base classname
|
126
|
+
namespace: svg # Namespace for symbol ids, e.g 'svg-logo'
|
127
|
+
namespace_before: true # Add namespace before, e.g. 'svg-logo', false would be 'logo-svg'
|
115
128
|
|
116
129
|
alias: # Add aliases for icon names
|
117
130
|
comment: chat # use "chat" to reference comment.svg
|
data/exe/esvg
CHANGED
@@ -8,12 +8,8 @@ require 'esvg'
|
|
8
8
|
options = {}
|
9
9
|
|
10
10
|
OptionParser.new do |opts|
|
11
|
-
opts.on("-f", "--format TYPE", String, "Options: js, html (defaults to js)") do |format|
|
12
|
-
options[:format] = format
|
13
|
-
end
|
14
|
-
|
15
11
|
opts.on("-o", "--output PATH", String, "Where should JS/HTML files be written, (default: current directory)") do |path|
|
16
|
-
options[:
|
12
|
+
options[:build] = path
|
17
13
|
end
|
18
14
|
|
19
15
|
opts.on("-c", "--config PATH", String, "Path to a config file (default: esvg.yml, config/esvg.yml)") do |path|
|
@@ -24,11 +20,11 @@ OptionParser.new do |opts|
|
|
24
20
|
options[:rails] = true
|
25
21
|
end
|
26
22
|
|
27
|
-
opts.on("-O", "--optimize", "Optimize svgs with svgo") do
|
28
|
-
options[:optimize] =
|
23
|
+
opts.on("-O", "--optimize", "Optimize svgs with svgo") do
|
24
|
+
options[:optimize] = true
|
29
25
|
end
|
30
26
|
|
31
|
-
opts.on("-v", "--version", "Print version") do
|
27
|
+
opts.on("-v", "--version", "Print version") do
|
32
28
|
options[:version] = true
|
33
29
|
end
|
34
30
|
|
@@ -39,9 +35,9 @@ if options[:version]
|
|
39
35
|
else
|
40
36
|
|
41
37
|
if path = ARGV.shift
|
42
|
-
options[:
|
38
|
+
options[:source] = path
|
43
39
|
end
|
44
40
|
|
45
|
-
options[:
|
46
|
-
esvg = Esvg::SVG.new(options).
|
41
|
+
options[:print] = true
|
42
|
+
esvg = Esvg::SVG.new(options).build
|
47
43
|
end
|
data/lib/esvg.rb
CHANGED
@@ -19,12 +19,23 @@ module Esvg
|
|
19
19
|
@svgs
|
20
20
|
end
|
21
21
|
|
22
|
-
def embed
|
23
|
-
new.embed
|
22
|
+
def embed(key)
|
23
|
+
new.embed(key)
|
24
24
|
end
|
25
25
|
|
26
26
|
def rails?
|
27
27
|
defined?(Rails)
|
28
28
|
end
|
29
29
|
|
30
|
+
def build(options={})
|
31
|
+
new(options).build
|
32
|
+
end
|
33
|
+
|
34
|
+
def precompile_assets
|
35
|
+
if rails? && defined?(Rake)
|
36
|
+
::Rake::Task['assets:precompile'].enhance do
|
37
|
+
build(compress: true, print: true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
30
41
|
end
|
data/lib/esvg/helpers.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
1
|
module Esvg::Helpers
|
2
|
-
def svg_icons(options={})
|
3
|
-
svgs = Esvg.svgs
|
4
2
|
|
5
|
-
|
6
|
-
|
3
|
+
def embed_svgs(*keys)
|
4
|
+
if Rails.env.production?
|
5
|
+
esvg_files.build_paths(keys).each do |path|
|
6
|
+
javascript_include_tag(path)
|
7
|
+
end.join("\n")
|
8
|
+
else
|
9
|
+
esvg_files.embed_script(keys).html_safe
|
7
10
|
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def use_svg(name, options={})
|
14
|
+
esvg_files.use(name, options).html_safe
|
15
|
+
end
|
16
|
+
|
17
|
+
def esvg_files
|
18
|
+
svgs = Esvg.svgs || Esvg.new()
|
8
19
|
|
9
20
|
svgs.read_files if Rails.env.development?
|
10
21
|
|
11
22
|
svgs
|
12
23
|
end
|
13
|
-
|
14
|
-
def svg_icon(name, options={})
|
15
|
-
svg_icons.use(name, options).html_safe
|
16
|
-
end
|
17
24
|
end
|
data/lib/esvg/svg.rb
CHANGED
@@ -1,34 +1,38 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'json'
|
3
|
+
require 'zlib'
|
3
4
|
|
4
5
|
module Esvg
|
5
6
|
class SVG
|
6
|
-
attr_accessor :
|
7
|
+
attr_accessor :svgs, :last_read, :svg_symbols
|
7
8
|
|
8
9
|
CONFIG = {
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
filename: 'svgs',
|
11
|
+
class: 'svg-symbol',
|
12
|
+
namespace: 'svg',
|
13
|
+
core: true,
|
12
14
|
namespace_before: true,
|
13
|
-
|
14
|
-
|
15
|
-
format: 'js',
|
15
|
+
optimize: false,
|
16
|
+
compress: false,
|
16
17
|
throttle_read: 4,
|
17
18
|
flatten: [],
|
18
19
|
alias: {}
|
19
20
|
}
|
20
21
|
|
21
22
|
CONFIG_RAILS = {
|
22
|
-
|
23
|
-
|
23
|
+
source: "app/assets/svgs",
|
24
|
+
assets: "app/assets/javascripts",
|
25
|
+
build: "public/assets",
|
26
|
+
temp: "tmp"
|
24
27
|
}
|
25
28
|
|
26
29
|
def initialize(options={})
|
27
30
|
config(options)
|
28
31
|
|
32
|
+
@modules = {}
|
29
33
|
@last_read = nil
|
30
|
-
@svg_cache = {}
|
31
34
|
|
35
|
+
read_cache
|
32
36
|
read_files
|
33
37
|
end
|
34
38
|
|
@@ -36,10 +40,10 @@ module Esvg
|
|
36
40
|
@config ||= begin
|
37
41
|
paths = [options[:config_file], 'config/esvg.yml', 'esvg.yml'].compact
|
38
42
|
|
39
|
-
config = CONFIG
|
43
|
+
config = CONFIG.dup
|
40
44
|
|
41
45
|
if Esvg.rails? || options[:rails]
|
42
|
-
config.merge!(CONFIG_RAILS)
|
46
|
+
config.merge!(CONFIG_RAILS)
|
43
47
|
end
|
44
48
|
|
45
49
|
if path = paths.select{ |p| File.exist?(p)}.first
|
@@ -47,61 +51,21 @@ module Esvg
|
|
47
51
|
end
|
48
52
|
|
49
53
|
config.merge!(options)
|
50
|
-
|
51
|
-
config[:path] ||= Dir.pwd
|
52
|
-
config[:output_path] ||= Dir.pwd
|
53
54
|
|
54
|
-
|
55
|
-
config[:path] = File.expand_path(config[:path])
|
56
|
-
config[:output_path] = File.expand_path(config[:output_path])
|
57
|
-
end
|
55
|
+
config[:filename] = File.basename(config[:filename], '.*')
|
58
56
|
|
59
|
-
config[:
|
60
|
-
config[:
|
61
|
-
config[:
|
62
|
-
config.
|
63
|
-
config[:aliases] = load_aliases(config[:alias])
|
64
|
-
config[:flatten] = config[:flatten].map { |dir| File.join(dir, '/') }.join('|')
|
57
|
+
config[:pwd] = File.expand_path Dir.pwd
|
58
|
+
config[:source] = File.expand_path config[:source] || config[:pwd]
|
59
|
+
config[:build] = File.expand_path config[:build] || config[:pwd]
|
60
|
+
config[:assets] = File.expand_path config[:assets] || config[:pwd]
|
65
61
|
|
66
|
-
config
|
67
|
-
|
68
|
-
end
|
62
|
+
config[:temp] = config[:pwd] if config[:temp].nil?
|
63
|
+
config[:temp] = File.expand_path File.join(config[:temp], '.esvg-cache')
|
69
64
|
|
70
|
-
|
71
|
-
|
72
|
-
# Converts configuration YAML:
|
73
|
-
# alias:
|
74
|
-
# foo: bar
|
75
|
-
# baz: zip, zop
|
76
|
-
# To output:
|
77
|
-
# { :bar => "foo", :zip => "baz", :zop => "baz" }
|
78
|
-
#
|
79
|
-
def load_aliases(aliases)
|
80
|
-
a = {}
|
81
|
-
aliases.each do |name,alternates|
|
82
|
-
alternates.split(',').each do |val|
|
83
|
-
a[dasherize(val.strip).to_sym] = dasherize(name.to_s)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
a
|
87
|
-
end
|
88
|
-
|
89
|
-
def get_alias(name)
|
90
|
-
config[:aliases][dasherize(name).to_sym] || name
|
91
|
-
end
|
92
|
-
|
93
|
-
def embed
|
94
|
-
return if files.empty?
|
95
|
-
output = if config[:format] == "html"
|
96
|
-
html
|
97
|
-
elsif config[:format] == "js"
|
98
|
-
js
|
99
|
-
end
|
65
|
+
config[:aliases] = load_aliases(config[:alias])
|
66
|
+
config[:flatten] = [config[:flatten]].flatten.map { |dir| File.join(dir, '/') }.join('|')
|
100
67
|
|
101
|
-
|
102
|
-
output.html_safe
|
103
|
-
else
|
104
|
-
output
|
68
|
+
config
|
105
69
|
end
|
106
70
|
end
|
107
71
|
|
@@ -110,157 +74,219 @@ module Esvg
|
|
110
74
|
return
|
111
75
|
end
|
112
76
|
|
113
|
-
@files = {}
|
114
|
-
|
115
77
|
# Get a list of svg files and modification times
|
116
78
|
#
|
117
|
-
find_files
|
118
|
-
|
119
|
-
end
|
79
|
+
find_files
|
80
|
+
write_cache
|
120
81
|
|
121
82
|
@last_read = Time.now.to_i
|
122
83
|
|
123
|
-
puts "Read #{
|
84
|
+
puts "Read #{svgs.size} files from #{config[:source]}" if config[:print]
|
124
85
|
|
125
|
-
|
126
|
-
|
127
|
-
if files.empty? && config[:cli]
|
128
|
-
puts "No svgs found at #{config[:path]}"
|
86
|
+
if svgs.empty? && config[:print]
|
87
|
+
puts "No svgs found at #{config[:source]}"
|
129
88
|
end
|
130
89
|
end
|
131
90
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
91
|
+
def find_files
|
92
|
+
files = Dir[File.join(config[:source], '**/*.svg')].uniq.sort
|
93
|
+
|
94
|
+
# Remove deleted files from svg cache
|
95
|
+
(svgs.keys - file_keys(files)).each { |f| svgs.delete(f) }
|
96
|
+
|
97
|
+
dirs = {}
|
98
|
+
|
99
|
+
files.each do |path|
|
136
100
|
|
137
|
-
|
138
|
-
key = file_key
|
101
|
+
mtime = File.mtime(path).to_i
|
102
|
+
key = file_key path
|
103
|
+
dkey = dir_key path
|
139
104
|
|
140
|
-
|
141
|
-
|
105
|
+
# Use cache if possible
|
106
|
+
if svgs[key].nil? || svgs[key][:last_modified] != mtime
|
107
|
+
svgs[key] = process_file(path, mtime, key)
|
142
108
|
end
|
143
109
|
|
144
|
-
|
145
|
-
|
110
|
+
dirs[dkey] ||= {}
|
111
|
+
(dirs[dkey][:files] ||= []) << key
|
112
|
+
|
113
|
+
if dirs[dkey][:last_modified].nil? || dirs[dkey][:last_modified] < mtime
|
114
|
+
dirs[dkey][:last_modified] = mtime
|
115
|
+
end
|
146
116
|
end
|
147
117
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
118
|
+
dirs = sort(dirs)
|
119
|
+
|
120
|
+
# Remove deleted directories from svg_symbols cache
|
121
|
+
(svg_symbols.keys - dirs.keys).each {|dir| svg_symbols.delete(dir) }
|
122
|
+
|
123
|
+
dirs.each do |dir, data|
|
124
|
+
|
125
|
+
# overwrite cache if
|
126
|
+
if svg_symbols[dir].nil? || # No cache for this dir yet
|
127
|
+
svg_symbols[dir][:last_modified] != data[:last_modified] || # New or updated file
|
128
|
+
svg_symbols[dir][:optimized] != optimize? || # Cache is unoptimized
|
129
|
+
svg_symbols[dir][:files] != data[:files] # Changed files
|
130
|
+
|
131
|
+
symbols = data[:files].map { |f| svgs[f][:content] }.join
|
132
|
+
attributes = data[:files].map { |f| svgs[f][:attr] }
|
133
|
+
|
134
|
+
svg_symbols[dir] = data.merge({
|
135
|
+
name: dir,
|
136
|
+
symbols: optimize(symbols, attributes),
|
137
|
+
optimized: optimize?,
|
138
|
+
version: config[:version] || Digest::MD5.hexdigest(symbols),
|
139
|
+
asset: File.basename(dir).start_with?('_')
|
140
|
+
})
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
svg_symbols.keys.each do |dir|
|
145
|
+
svg_symbols[dir][:path] = write_path(dir)
|
146
|
+
end
|
152
147
|
end
|
148
|
+
|
149
|
+
@svg_symbols = sort(@svg_symbols)
|
150
|
+
@svgs = sort(@svgs)
|
151
|
+
end
|
152
|
+
|
153
|
+
def read_cache
|
154
|
+
@svgs = YAML.load(read_tmp '.svgs') || {}
|
155
|
+
@svg_symbols = YAML.load(read_tmp '.svg_symbols') || {}
|
156
|
+
end
|
157
|
+
|
158
|
+
def write_cache
|
159
|
+
return if production?
|
160
|
+
|
161
|
+
write_tmp '.svgs', sort(@svgs).to_yaml
|
162
|
+
write_tmp '.svg_symbols', sort(@svg_symbols).to_yaml
|
163
|
+
|
153
164
|
end
|
154
165
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
config[:flatten].include?(key)
|
166
|
+
def sort(hash)
|
167
|
+
sorted = {}
|
168
|
+
hash.sort.each do |h|
|
169
|
+
sorted[h.first] = h.last
|
160
170
|
end
|
171
|
+
sorted
|
172
|
+
end
|
161
173
|
|
162
|
-
|
174
|
+
def embed_script(key=nil)
|
175
|
+
if script = js(key)
|
176
|
+
"<script>#{script}</script>"
|
177
|
+
else
|
178
|
+
''
|
179
|
+
end
|
163
180
|
end
|
164
181
|
|
165
|
-
def
|
166
|
-
|
182
|
+
def build_paths(keys=nil)
|
183
|
+
build_files(keys).map { |s| File.basename(s[:path]) }
|
184
|
+
end
|
167
185
|
|
168
|
-
|
169
|
-
|
186
|
+
def build_files(keys=nil)
|
187
|
+
valid_keys(keys).reject do |k|
|
188
|
+
svg_symbols[k][:asset]
|
189
|
+
end.map { |k| svg_symbols[k] }
|
170
190
|
end
|
171
191
|
|
172
|
-
def
|
173
|
-
|
192
|
+
def asset_files(keys=nil)
|
193
|
+
valid_keys(keys).select do |k|
|
194
|
+
svg_symbols[k][:asset]
|
195
|
+
end.map { |k| svg_symbols[k] }
|
174
196
|
end
|
175
197
|
|
176
198
|
def process_file(file, mtime, name)
|
177
|
-
content
|
178
|
-
|
199
|
+
content = File.read(file)
|
200
|
+
classname = classname(name)
|
201
|
+
size_attr = dimensions(content)
|
202
|
+
|
203
|
+
svg = {
|
179
204
|
name: name,
|
180
|
-
|
181
|
-
|
182
|
-
|
205
|
+
use: %Q{<use xlink:href="##{classname}"/>},
|
206
|
+
last_modified: mtime,
|
207
|
+
attr: { class: classname }.merge(dimensions(content))
|
183
208
|
}
|
184
|
-
|
209
|
+
# Add attributes
|
210
|
+
svg[:content] = prep_svg(content, svg[:attr])
|
185
211
|
|
186
|
-
|
187
|
-
name = classname get_alias(file)
|
188
|
-
viewbox = content.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten.first
|
189
|
-
%Q{<svg class="#{config[:base_class]} #{name}" #{viewbox}><use xlink:href="##{name}"/></svg>}
|
212
|
+
svg
|
190
213
|
end
|
191
214
|
|
192
|
-
def
|
193
|
-
name =
|
194
|
-
|
195
|
-
if !exist?(name)
|
196
|
-
if fallback = options.delete(:fallback)
|
197
|
-
svg_icon(fallback, options)
|
198
|
-
else
|
199
|
-
if Esvg.rails? && Rails.env.production?
|
200
|
-
return ''
|
201
|
-
else
|
202
|
-
raise "no svg named '#{get_alias(file)}' exists at #{config[:path]}"
|
203
|
-
end
|
204
|
-
end
|
205
|
-
else
|
206
|
-
|
207
|
-
embed = use_icon(name)
|
208
|
-
embed = embed.sub(/class="(.+?)"/, 'class="\1 '+options[:class]+'"') if options[:class]
|
215
|
+
def use(file, options={})
|
216
|
+
if name = exist?(file, options[:fallback])
|
217
|
+
svg = svgs[name]
|
209
218
|
|
210
219
|
if options[:color]
|
211
220
|
options[:style] ||= ''
|
212
|
-
options[:style] += "
|
221
|
+
options[:style] += "color:#{options[:color]};#{options[:style]}"
|
213
222
|
end
|
214
223
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
224
|
+
attr = {
|
225
|
+
fill: options[:fill],
|
226
|
+
style: options[:style],
|
227
|
+
viewbox: svg[:attr][:viewbox],
|
228
|
+
classname: [config[:class], svg[:attr][:class], options[:class]].compact.join(' ')
|
229
|
+
}
|
219
230
|
|
220
|
-
|
231
|
+
# If user doesn't pass a size or set scale: true
|
232
|
+
if !(options[:width] || options[:height] || options[:scale])
|
221
233
|
|
222
|
-
|
223
|
-
|
234
|
+
# default to svg dimensions
|
235
|
+
attr[:width] = svg[:attr][:width]
|
236
|
+
attr[:height] = svg[:attr][:height]
|
224
237
|
else
|
225
|
-
|
238
|
+
|
239
|
+
# Add sizes (nil options will be stripped)
|
240
|
+
attr[:width] = options[:width]
|
241
|
+
attr[:height] = options[:height]
|
226
242
|
end
|
227
|
-
end
|
228
|
-
end
|
229
243
|
|
230
|
-
|
244
|
+
use = %Q{<svg #{attributes(attr)}>#{svg[:use]}#{title(options)}#{desc(options)}</svg>}
|
231
245
|
|
232
|
-
|
233
|
-
|
234
|
-
content = content.to_s
|
235
|
-
if tag.match(/#{attr}/)
|
236
|
-
if append
|
237
|
-
tag.sub(/#{attr}="(.+?)"/, attr+'="\1'+append+content+'"')
|
246
|
+
if Esvg.rails?
|
247
|
+
use.html_safe
|
238
248
|
else
|
239
|
-
|
249
|
+
use
|
240
250
|
end
|
241
251
|
else
|
242
|
-
|
252
|
+
if production?
|
253
|
+
return ''
|
254
|
+
else
|
255
|
+
raise "no svg named '#{get_alias(file)}' exists at #{config[:source]}"
|
256
|
+
end
|
243
257
|
end
|
244
258
|
end
|
245
259
|
|
260
|
+
alias :svg_icon :use
|
261
|
+
|
246
262
|
def dimensions(input)
|
247
|
-
|
248
|
-
|
249
|
-
coords = dimension.last.split(' ')
|
263
|
+
viewbox = input.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten.last
|
264
|
+
coords = viewbox.split(' ')
|
250
265
|
|
251
|
-
|
252
|
-
|
253
|
-
|
266
|
+
{
|
267
|
+
viewbox: viewbox,
|
268
|
+
width: coords[2].to_i - coords[0].to_i,
|
269
|
+
height: coords[3].to_i - coords[1].to_i
|
270
|
+
}
|
254
271
|
end
|
255
272
|
|
256
|
-
def
|
257
|
-
|
258
|
-
|
273
|
+
def attributes(hash)
|
274
|
+
att = []
|
275
|
+
hash.each do |key, value|
|
276
|
+
att << %Q{#{key}="#{value}"} unless value.nil?
|
277
|
+
end
|
278
|
+
att.join(' ')
|
259
279
|
end
|
260
280
|
|
261
|
-
def exist?(name)
|
262
|
-
name = get_alias(name)
|
263
|
-
|
281
|
+
def exist?(name, fallback=nil)
|
282
|
+
name = get_alias dasherize(name)
|
283
|
+
|
284
|
+
if svgs[name].nil?
|
285
|
+
exist?(fallback) if fallback
|
286
|
+
else
|
287
|
+
name
|
288
|
+
end
|
289
|
+
|
264
290
|
end
|
265
291
|
|
266
292
|
alias_method :exists?, :exist?
|
@@ -277,12 +303,6 @@ module Esvg
|
|
277
303
|
input.gsub(/[\W,_]/, '-').sub(/^-/,'').gsub(/-{2,}/, '-')
|
278
304
|
end
|
279
305
|
|
280
|
-
def find_files
|
281
|
-
path = File.expand_path(File.join(config[:path], '**/*.svg'))
|
282
|
-
Dir[path].uniq
|
283
|
-
end
|
284
|
-
|
285
|
-
|
286
306
|
def title(options)
|
287
307
|
if options[:title]
|
288
308
|
"<title>#{options[:title]}</title>"
|
@@ -299,104 +319,224 @@ module Esvg
|
|
299
319
|
end
|
300
320
|
end
|
301
321
|
|
302
|
-
def
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
322
|
+
def version(key)
|
323
|
+
svg_symbols[key][:version]
|
324
|
+
end
|
325
|
+
|
326
|
+
def build
|
327
|
+
paths = write_files svg_symbols.values
|
328
|
+
|
329
|
+
if config[:core]
|
330
|
+
path = File.join config[:assets], "_esvg.js"
|
331
|
+
write_file(path, js_core)
|
332
|
+
paths << path
|
309
333
|
end
|
310
334
|
|
311
|
-
|
312
|
-
|
335
|
+
paths
|
336
|
+
end
|
337
|
+
|
338
|
+
def write_files(files)
|
339
|
+
paths = []
|
340
|
+
|
341
|
+
files.each do |file|
|
342
|
+
if file[:asset] || !File.exist?(file[:path])
|
343
|
+
write_file(file[:path], js(file[:name]))
|
344
|
+
puts "Writing #{file[:path]}" if config[:print]
|
345
|
+
paths << file[:path]
|
346
|
+
|
347
|
+
if !file[:asset] && gz = compress(file[:path])
|
348
|
+
puts "Writing #{gz}" if config[:print]
|
349
|
+
paths << gz
|
350
|
+
end
|
351
|
+
end
|
313
352
|
end
|
314
353
|
|
315
|
-
|
354
|
+
paths
|
316
355
|
end
|
317
356
|
|
318
|
-
def
|
319
|
-
|
357
|
+
def symbols(keys)
|
358
|
+
symbols = valid_keys(keys).map { |key|
|
359
|
+
svg_symbols[key][:symbols]
|
360
|
+
}.join.gsub(/\n/,'')
|
361
|
+
|
362
|
+
%Q{<svg id="esvg-#{key_id(keys)}" version="1.1" style="height:0;position:absolute">#{symbols}</svg>}
|
320
363
|
end
|
321
364
|
|
322
|
-
def
|
323
|
-
|
324
|
-
|
325
|
-
|
365
|
+
def js(key)
|
366
|
+
keys = valid_keys(key)
|
367
|
+
return if keys.empty?
|
368
|
+
|
369
|
+
script key_id(keys), symbols(keys).gsub('/n','').gsub("'"){"\\'"}
|
326
370
|
end
|
327
371
|
|
328
|
-
def
|
329
|
-
|
372
|
+
def script(id, symbols)
|
373
|
+
%Q{(function(){
|
330
374
|
|
331
|
-
|
332
|
-
|
333
|
-
|
375
|
+
function embed() {
|
376
|
+
if (!document.querySelector('#esvg-#{id}')) {
|
377
|
+
document.querySelector('body').insertAdjacentHTML('afterbegin', '#{symbols}')
|
378
|
+
}
|
379
|
+
}
|
334
380
|
|
335
|
-
|
336
|
-
|
381
|
+
// If DOM is already ready, embed SVGs
|
382
|
+
if (document.readyState == 'interactive') { embed() }
|
337
383
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
end
|
384
|
+
// Handle Turbolinks page change events
|
385
|
+
if ( window.Turbolinks ) {
|
386
|
+
document.addEventListener("turbolinks:load", function(event) { embed() })
|
387
|
+
}
|
343
388
|
|
344
|
-
|
389
|
+
// Handle standard DOM ready events
|
390
|
+
document.addEventListener("DOMContentLoaded", function(event) { embed() })
|
391
|
+
})()}
|
345
392
|
end
|
346
393
|
|
347
|
-
def
|
348
|
-
|
394
|
+
def js_core
|
395
|
+
%Q{(function(){
|
396
|
+
var names
|
349
397
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
398
|
+
function attr( source, name ){
|
399
|
+
if (typeof source == 'object')
|
400
|
+
return name+'="'+source.getAttribute(name)+'" '
|
401
|
+
else
|
402
|
+
return name+'="'+source+'" ' }
|
355
403
|
|
356
|
-
|
357
|
-
|
404
|
+
function dasherize( input ) {
|
405
|
+
return input.replace(/[\\W,_]/g, '-').replace(/-{2,}/g, '-')
|
406
|
+
}
|
358
407
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
408
|
+
function svgName( name ) {
|
409
|
+
#{if config[:namespace_before]
|
410
|
+
%Q{return "#{config[:namespace]}-"+dasherize( name )}
|
411
|
+
else
|
412
|
+
%Q{return dasherize( name )+"-#{config[:namespace]}"}
|
413
|
+
end}
|
414
|
+
}
|
415
|
+
|
416
|
+
function use( name, options ) {
|
417
|
+
options = options || {}
|
418
|
+
var id = dasherize( svgName( name ) )
|
419
|
+
var symbol = svgs()[id]
|
420
|
+
|
421
|
+
if ( symbol ) {
|
422
|
+
var svg = document.createRange().createContextualFragment( '<svg><use xlink:href="#'+id+'"/></svg>' ).firstChild;
|
423
|
+
svg.setAttribute( 'class', '#{config[:class]} '+id+' '+( options.classname || '' ).trim() )
|
424
|
+
svg.setAttribute( 'viewBox', symbol.getAttribute( 'viewBox' ) )
|
425
|
+
|
426
|
+
if ( !( options.width || options.height || options.scale ) ) {
|
427
|
+
|
428
|
+
svg.setAttribute('width', symbol.getAttribute('width'))
|
429
|
+
svg.setAttribute('height', symbol.getAttribute('height'))
|
430
|
+
|
431
|
+
} else {
|
432
|
+
|
433
|
+
if ( options.width ) svg.setAttribute( 'width', options.width )
|
434
|
+
if ( options.height ) svg.setAttribute( 'height', options.height )
|
435
|
+
}
|
436
|
+
|
437
|
+
return svg
|
438
|
+
} else {
|
439
|
+
console.error('Cannot find "'+name+'" svg symbol. Ensure that svg scripts are loaded')
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
443
|
+
function svgs(){
|
444
|
+
if ( !names ) {
|
445
|
+
names = {}
|
446
|
+
for( var symbol of document.querySelectorAll( 'svg[id^=esvg] symbol' ) ) {
|
447
|
+
names[symbol.id] = symbol
|
448
|
+
}
|
449
|
+
}
|
450
|
+
return names
|
451
|
+
}
|
452
|
+
|
453
|
+
var esvg = {
|
454
|
+
svgs: svgs,
|
455
|
+
use: use
|
456
|
+
}
|
457
|
+
|
458
|
+
// Handle Turbolinks page change events
|
459
|
+
if ( window.Turbolinks ) {
|
460
|
+
document.addEventListener( "turbolinks:load", function( event ) { names = null; esvg.svgs() })
|
461
|
+
}
|
462
|
+
|
463
|
+
if( typeof( module ) != 'undefined' ) { module.exports = esvg }
|
464
|
+
else window.esvg = esvg
|
465
|
+
|
466
|
+
})()}
|
364
467
|
end
|
365
468
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
469
|
+
private
|
470
|
+
|
471
|
+
def dir_key(path)
|
472
|
+
dir = File.dirname(flatten_path(path))
|
473
|
+
|
474
|
+
# Flattened paths which should be treated as assets will use '_' as their dir key
|
475
|
+
if dir == '.' && ( sub_path(path).start_with?('_') || config[:filename].start_with?('_') )
|
476
|
+
'_'
|
477
|
+
else
|
478
|
+
dir
|
371
479
|
end
|
480
|
+
end
|
372
481
|
|
373
|
-
|
482
|
+
def sub_path(path)
|
483
|
+
path.sub("#{config[:source]}/",'')
|
374
484
|
end
|
375
485
|
|
376
|
-
def
|
377
|
-
|
378
|
-
|
486
|
+
def flatten_path(path)
|
487
|
+
sub_path(path).sub(Regexp.new(config[:flatten]), '')
|
488
|
+
end
|
379
489
|
|
380
|
-
|
381
|
-
|
490
|
+
def file_key(path)
|
491
|
+
dasherize flatten_path(path).sub('.svg', '')
|
492
|
+
end
|
493
|
+
|
494
|
+
def file_keys(paths)
|
495
|
+
paths.flatten.map { |p| file_key(p) }
|
496
|
+
end
|
497
|
+
|
498
|
+
def write_path(key)
|
499
|
+
name = if key == '_' # Root level asset file
|
500
|
+
"_#{config[:filename]}".sub(/_+/, '_')
|
501
|
+
elsif key == '.' # Root level build file
|
502
|
+
config[:filename]
|
382
503
|
else
|
383
|
-
|
504
|
+
"#{key}"
|
505
|
+
end
|
506
|
+
|
507
|
+
# Is it an asset, or a build file
|
508
|
+
if name.start_with?('_')
|
509
|
+
File.join config[:assets], "#{name}.js"
|
510
|
+
else
|
511
|
+
File.join config[:build], "#{name}-#{version(key)}.js"
|
384
512
|
end
|
385
513
|
end
|
386
514
|
|
515
|
+
def prep_svg(content, attr)
|
516
|
+
content = content.gsub(/<?.+\?>/,'').gsub(/<!.+?>/,'') # Get rid of doctypes and comments
|
517
|
+
.gsub(/\n/, '') # Remove endlines
|
518
|
+
.gsub(/\s{2,}/, ' ') # Remove whitespace
|
519
|
+
.gsub(/>\s+</, '><') # Remove whitespace between tags
|
520
|
+
.gsub(/\s?fill="(#0{3,6}|black|rgba?\(0,0,0\))"/,'') # Strip black fill
|
521
|
+
.gsub(/style="([^"]*?)fill:(.+?);/m, 'fill="\2" style="\1') # Make fill a property instead of a style
|
522
|
+
.gsub(/style="([^"]*?)fill-opacity:(.+?);/m, 'fill-opacity="\2" style="\1') # Move fill-opacity a property instead of a style
|
523
|
+
|
524
|
+
sub_def_ids(content, attr[:class])
|
525
|
+
end
|
526
|
+
|
387
527
|
# Scans <def> blocks for IDs
|
388
528
|
# If urls(#id) are used, ensure these IDs are unique to this file
|
389
529
|
# Only replace IDs if urls exist to avoid replacing defs
|
390
530
|
# used in other svg files
|
391
531
|
#
|
392
|
-
def sub_def_ids(
|
532
|
+
def sub_def_ids(content, classname)
|
393
533
|
return content unless !!content.match(/<defs>/)
|
394
534
|
|
395
535
|
content.scan(/<defs>.+<\/defs>/m).flatten.each do |defs|
|
396
536
|
defs.scan(/id="(.+?)"/).flatten.uniq.each_with_index do |id, index|
|
397
537
|
|
398
538
|
if content.match(/url\(##{id}\)/)
|
399
|
-
new_id = "#{classname
|
539
|
+
new_id = "#{classname}-def#{index}"
|
400
540
|
|
401
541
|
content = content.gsub(/id="#{id}"/, %Q{class="#{new_id}"})
|
402
542
|
.gsub(/url\(##{id}\)/, "url(##{new_id})" )
|
@@ -409,127 +549,106 @@ module Esvg
|
|
409
549
|
content
|
410
550
|
end
|
411
551
|
|
412
|
-
def
|
413
|
-
|
414
|
-
|
415
|
-
.gsub(/\s{2,}/, ' ') # Remove whitespace
|
416
|
-
.gsub(/>\s+</, '><') # Remove whitespace between tags
|
417
|
-
.gsub(/\s?fill="(#0{3,6}|black|rgba?\(0,0,0\))"/,'') # Strip black fill
|
418
|
-
.gsub(/style="([^"]*?)fill:(.+?);/m, 'fill="\2" style="\1') # Make fill a property instead of a style
|
419
|
-
.gsub(/style="([^"]*?)fill-opacity:(.+?);/m, 'fill-opacity="\2" style="\1') # Move fill-opacity a property instead of a style
|
552
|
+
def optimize?
|
553
|
+
!!(config[:optimize] && svgo_cmd)
|
554
|
+
end
|
420
555
|
|
421
|
-
|
556
|
+
def svgo_cmd
|
557
|
+
find_node_module('svgo')
|
422
558
|
end
|
423
559
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
560
|
+
|
561
|
+
def optimize(svg, attributes)
|
562
|
+
if optimize?
|
563
|
+
path = write_tmp '.svgo-tmp', svg
|
564
|
+
command = "#{svgo_cmd} --disable=removeUselessDefs '#{path}' -o -"
|
565
|
+
svg = `#{command}`
|
566
|
+
FileUtils.rm(path) if File.exist? path
|
429
567
|
end
|
430
568
|
|
431
|
-
svg
|
569
|
+
id_symbols(svg, attributes)
|
432
570
|
end
|
433
571
|
|
434
|
-
def
|
435
|
-
|
436
|
-
|
437
|
-
else
|
438
|
-
symbols = []
|
439
|
-
svgs[key].each do |name, data|
|
440
|
-
symbols << prep_svg(name, data[:content])
|
441
|
-
end
|
442
|
-
|
443
|
-
symbols = optimize(symbols.join).gsub(/class=/,'id=').gsub(/svg/,'symbol')
|
444
|
-
|
445
|
-
%Q{<svg id="esvg-#{key_id(key)}" version="1.1" style="height:0;position:absolute">#{symbols}</svg>}
|
572
|
+
def id_symbols(svg, attr)
|
573
|
+
svg.gsub(/<svg.+?>/).with_index do |match, index|
|
574
|
+
%Q{<symbol #{attributes(attr[index])}>} # Remove clutter from svg declaration
|
446
575
|
end
|
576
|
+
.gsub(/<\/svg/,'</symbol') # Replace svgs with symbols
|
577
|
+
.gsub(/class=/,'id=') # Replace classes with ids (classes are generated here)
|
578
|
+
.gsub(/\w+=""/,'') # Remove empty attributes
|
447
579
|
end
|
448
580
|
|
449
|
-
def
|
450
|
-
|
451
|
-
end
|
581
|
+
def compress(file)
|
582
|
+
return if !config[:compress]
|
452
583
|
|
453
|
-
|
454
|
-
|
455
|
-
end
|
584
|
+
mtime = File.mtime(file)
|
585
|
+
gz_file = "#{file}.gz"
|
456
586
|
|
457
|
-
|
458
|
-
%Q{var esvg = {
|
459
|
-
icon: function(name, classnames) {
|
460
|
-
var svgName = this.iconName(name)
|
461
|
-
var element = document.querySelector('#'+svgName)
|
587
|
+
return if (File.exist?(gz_file) && File.mtime(gz_file) >= mtime)
|
462
588
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
iconName: function(name) {
|
470
|
-
var before = #{config[:namespace_before]}
|
471
|
-
if (before) {
|
472
|
-
return "#{config[:namespace]}-"+this.dasherize(name)
|
473
|
-
} else {
|
474
|
-
return name+"-#{config[:namespace]}"
|
475
|
-
}
|
476
|
-
},
|
477
|
-
dimensions: function(el) {
|
478
|
-
return 'viewBox="'+el.getAttribute('viewBox')+'" width="'+el.getAttribute('width')+'" height="'+el.getAttribute('height')+'"'
|
479
|
-
},
|
480
|
-
dasherize: function(input) {
|
481
|
-
return input.replace(/[\W,_]/g, '-').replace(/-{2,}/g, '-')
|
482
|
-
},
|
483
|
-
aliases: #{config[:aliases].to_json},
|
484
|
-
alias: function(name) {
|
485
|
-
var aliased = this.aliases[name]
|
486
|
-
if (typeof(aliased) != "undefined") {
|
487
|
-
return aliased
|
488
|
-
} else {
|
489
|
-
return name
|
490
|
-
}
|
491
|
-
}
|
492
|
-
}
|
589
|
+
File.open(gz_file, "wb") do |dest|
|
590
|
+
gz = ::Zlib::GzipWriter.new(dest, Zlib::BEST_COMPRESSION)
|
591
|
+
gz.mtime = mtime.to_i
|
592
|
+
IO.copy_stream(open(file), gz)
|
593
|
+
gz.close
|
594
|
+
end
|
493
595
|
|
494
|
-
|
495
|
-
|
496
|
-
|
596
|
+
File.utime(mtime, mtime, gz_file)
|
597
|
+
|
598
|
+
gz_file
|
497
599
|
end
|
498
600
|
|
499
|
-
def
|
500
|
-
|
601
|
+
def write_tmp(name, content)
|
602
|
+
path = File.join(config[:temp], name)
|
603
|
+
FileUtils.mkdir_p(File.dirname(path))
|
604
|
+
write_file path, content
|
605
|
+
path
|
606
|
+
end
|
501
607
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
608
|
+
def read_tmp(name)
|
609
|
+
path = File.join(config[:temp], name)
|
610
|
+
if File.exist? path
|
611
|
+
File.read path
|
612
|
+
else
|
613
|
+
''
|
614
|
+
end
|
615
|
+
end
|
507
616
|
|
508
|
-
|
509
|
-
|
617
|
+
def log_path(path)
|
618
|
+
File.expand_path(path).sub(config[:pwd], '').sub(/^\//,'')
|
619
|
+
end
|
510
620
|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
621
|
+
def write_file(path, contents)
|
622
|
+
FileUtils.mkdir_p(File.expand_path(File.dirname(path)))
|
623
|
+
File.open(path, 'w') do |io|
|
624
|
+
io.write(contents)
|
625
|
+
end
|
626
|
+
end
|
515
627
|
|
516
|
-
|
517
|
-
|
518
|
-
|
628
|
+
def key_id(keys)
|
629
|
+
keys.map do |key|
|
630
|
+
(key == '.') ? 'symbols' : classname(key)
|
631
|
+
end.join('-')
|
519
632
|
end
|
520
633
|
|
634
|
+
# Determine if an NPM module is installed by checking paths with `npm bin`
|
635
|
+
# Returns path to binary if installed
|
521
636
|
def find_node_module(cmd)
|
522
637
|
require 'open3'
|
523
638
|
|
524
|
-
|
639
|
+
return @modules[cmd] unless @modules[cmd].nil?
|
525
640
|
|
526
|
-
|
527
|
-
|
528
|
-
"$(npm bin)/#{cmd}"
|
641
|
+
@modules[cmd] = begin
|
642
|
+
local = "$(npm bin)/#{cmd}"
|
643
|
+
global = "$(npm -g bin)/#{cmd}"
|
529
644
|
|
530
|
-
|
531
|
-
|
532
|
-
|
645
|
+
if Open3.capture3(local)[2].success?
|
646
|
+
local
|
647
|
+
elsif Open3.capture3(global)[2].success?
|
648
|
+
global
|
649
|
+
else
|
650
|
+
false
|
651
|
+
end
|
533
652
|
end
|
534
653
|
end
|
535
654
|
|
@@ -539,5 +658,43 @@ if(typeof(module) != 'undefined') { module.exports = esvg }
|
|
539
658
|
h
|
540
659
|
end
|
541
660
|
|
661
|
+
# Return non-empty key names for groups of svgs
|
662
|
+
def valid_keys(keys)
|
663
|
+
if keys.nil? || keys.empty?
|
664
|
+
svg_symbols.keys
|
665
|
+
else
|
666
|
+
keys = [keys].flatten.map { |k| dasherize k }
|
667
|
+
svg_symbols.keys.select { |k| keys.include? dasherize(k) }
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
# Load aliases from configuration.
|
672
|
+
# returns a hash of aliasees mapped to a name.
|
673
|
+
# Converts configuration YAML:
|
674
|
+
# alias:
|
675
|
+
# foo: bar
|
676
|
+
# baz: zip, zop
|
677
|
+
# To output:
|
678
|
+
# { :bar => "foo", :zip => "baz", :zop => "baz" }
|
679
|
+
#
|
680
|
+
def load_aliases(aliases)
|
681
|
+
a = {}
|
682
|
+
aliases.each do |name,alternates|
|
683
|
+
alternates.split(',').each do |val|
|
684
|
+
a[dasherize(val.strip).to_sym] = dasherize(name.to_s)
|
685
|
+
end
|
686
|
+
end
|
687
|
+
a
|
688
|
+
end
|
689
|
+
|
690
|
+
def get_alias(name)
|
691
|
+
config[:aliases][dasherize(name).to_sym] || name
|
692
|
+
end
|
693
|
+
|
694
|
+
def production?
|
695
|
+
config[:produciton] || if Esvg.rails?
|
696
|
+
Rails.env.production?
|
697
|
+
end
|
698
|
+
end
|
542
699
|
end
|
543
700
|
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
|
+
version: 4.0.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: 2017-05-
|
11
|
+
date: 2017-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|