fontcustom 1.0.1 → 1.1.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/TODO.md +6 -0
- data/lib/fontcustom.rb +1 -1
- data/lib/fontcustom/actions.rb +28 -0
- data/lib/fontcustom/cli.rb +16 -15
- data/lib/fontcustom/generator/font.rb +31 -35
- data/lib/fontcustom/generator/template.rb +49 -34
- data/lib/fontcustom/options.rb +17 -11
- data/lib/fontcustom/scripts/generate.py +1 -1
- data/lib/fontcustom/templates/_fontcustom-bootstrap-ie7.scss +4 -3
- data/lib/fontcustom/templates/_fontcustom-bootstrap.scss +9 -14
- data/lib/fontcustom/templates/_fontcustom.scss +8 -14
- data/lib/fontcustom/templates/fontcustom-bootstrap-ie7.css +3 -2
- data/lib/fontcustom/templates/fontcustom-bootstrap.css +9 -8
- data/lib/fontcustom/templates/fontcustom-preview.html +2 -3
- data/lib/fontcustom/templates/fontcustom.css +7 -7
- data/lib/fontcustom/templates/fontcustom.yml +25 -66
- data/lib/fontcustom/util.rb +108 -31
- data/lib/fontcustom/version.rb +1 -1
- data/lib/fontcustom/watcher.rb +24 -8
- data/spec/fixtures/{mixed-output → generators}/.fontcustom-data +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/another-font.ttf +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/dont-delete-me.bro +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/fontcustom.css +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.eot +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.svg +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.ttf +0 -0
- data/spec/fixtures/{mixed-output → generators/mixed-output}/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.woff +0 -0
- data/spec/fixtures/{not-a-dir → shared/not-a-dir} +0 -0
- data/spec/fixtures/shared/templates/custom.css +1 -0
- data/spec/fixtures/shared/templates/regular.css +1 -0
- data/spec/fixtures/{empty → shared/vectors-empty}/no_vectors_here.txt +0 -0
- data/spec/fixtures/{vectors → shared/vectors}/C.svg +0 -0
- data/spec/fixtures/{vectors → shared/vectors}/D.svg +0 -0
- data/spec/fixtures/vectors/a_R3ally-eXotic f1Le Name.svg b/data/spec/fixtures/shared/vectors/a_R3ally-eXotic f1Le → Name.svg +0 -0
- data/spec/fixtures/util/config-is-in-dir/fontcustom.yml +1 -0
- data/spec/fixtures/util/fontcustom-malformed.yml +1 -0
- data/spec/fixtures/{fontcustom.yml → util/fontcustom.yml} +0 -0
- data/spec/fixtures/util/rails-like/config/fontcustom.yml +1 -0
- data/spec/fontcustom/actions_spec.rb +22 -0
- data/spec/fontcustom/generator/font_spec.rb +107 -62
- data/spec/fontcustom/generator/template_spec.rb +124 -29
- data/spec/fontcustom/util_spec.rb +299 -53
- data/spec/fontcustom/watcher_spec.rb +56 -10
- data/spec/spec_helper.rb +8 -3
- metadata +46 -34
- data/spec/fixtures/empty-data/.fontcustom-data +0 -0
@@ -1,20 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
else
|
5
|
-
@data[:file_name]
|
6
|
-
end
|
7
|
-
%>/*
|
8
|
-
* Font Custom: bare CSS
|
9
|
-
*/
|
1
|
+
//
|
2
|
+
// Icon Font: <%= @opts[:font_name] %>
|
3
|
+
//
|
10
4
|
|
11
5
|
@font-face {
|
12
6
|
font-family: "<%= @opts[:font_name] %>";
|
13
|
-
src: url("<%=
|
14
|
-
src: url("<%=
|
15
|
-
url("<%=
|
16
|
-
url("<%=
|
17
|
-
url("<%=
|
7
|
+
src: url("<%= @data[:paths][:preprocessor_to_fonts] %>.eot");
|
8
|
+
src: url("<%= @data[:paths][:preprocessor_to_fonts] %>.eot?#iefix") format("embedded-opentype"),
|
9
|
+
url("<%= @data[:paths][:preprocessor_to_fonts] %>.woff") format("woff"),
|
10
|
+
url("<%= @data[:paths][:preprocessor_to_fonts] %>.ttf") format("truetype"),
|
11
|
+
url("<%= @data[:paths][:preprocessor_to_fonts] %>.svg#<%= @opts[:font_name] %>") format("svg");
|
18
12
|
font-weight: normal;
|
19
13
|
font-style: normal;
|
20
14
|
}
|
@@ -1,14 +1,15 @@
|
|
1
|
-
/*
|
2
|
-
|
3
|
-
|
1
|
+
/*
|
2
|
+
Icon Font: <%= @opts[:font_name] %>
|
3
|
+
Bootstrap Override
|
4
|
+
*/
|
4
5
|
|
5
6
|
@font-face {
|
6
7
|
font-family: "<%= @opts[:font_name] %>";
|
7
|
-
src: url("<%= @data[:
|
8
|
-
src: url("<%= @data[:
|
9
|
-
url("<%= @data[:
|
10
|
-
url("<%= @data[:
|
11
|
-
url("<%= @data[:
|
8
|
+
src: url("<%= @data[:paths][:css_to_fonts] %>.eot");
|
9
|
+
src: url("<%= @data[:paths][:css_to_fonts] %>.eot?#iefix") format("embedded-opentype"),
|
10
|
+
url("<%= @data[:paths][:css_to_fonts] %>.woff") format("woff"),
|
11
|
+
url("<%= @data[:paths][:css_to_fonts] %>.ttf") format("truetype"),
|
12
|
+
url("<%= @data[:paths][:css_to_fonts] %>.svg#<%= @opts[:font_name] %>") format("svg");
|
12
13
|
font-weight: normal;
|
13
14
|
font-style: normal;
|
14
15
|
}
|
@@ -1,9 +1,8 @@
|
|
1
|
-
<% scale = %w|12 14 16 18 21 24 36 48 60 72|
|
2
|
-
<!DOCTYPE html>
|
1
|
+
<% scale = %w|12 14 16 18 21 24 36 48 60 72| %><!DOCTYPE html>
|
3
2
|
<html>
|
4
3
|
<head>
|
5
4
|
<title><%= @opts[:font_name] %> glyphs preview</title>
|
6
|
-
<link rel="stylesheet" href="
|
5
|
+
<link rel="stylesheet" href="<%= @data[:paths][:preview_to_css] %>" />
|
7
6
|
<!--[if lte IE 7]><link rel="stylesheet" href="fontcustom-ie7.css" /><![endif]-->
|
8
7
|
|
9
8
|
<style>
|
@@ -1,14 +1,14 @@
|
|
1
1
|
/*
|
2
|
-
|
3
|
-
|
2
|
+
Icon Font: <%= @opts[:font_name] %>
|
3
|
+
*/
|
4
4
|
|
5
5
|
@font-face {
|
6
6
|
font-family: "<%= @opts[:font_name] %>";
|
7
|
-
src: url("<%= @data[:
|
8
|
-
src: url("<%= @data[:
|
9
|
-
url("<%= @data[:
|
10
|
-
url("<%= @data[:
|
11
|
-
url("<%= @data[:
|
7
|
+
src: url("<%= @data[:paths][:css_to_fonts] %>.eot");
|
8
|
+
src: url("<%= @data[:paths][:css_to_fonts] %>.eot?#iefix") format("embedded-opentype"),
|
9
|
+
url("<%= @data[:paths][:css_to_fonts] %>.woff") format("woff"),
|
10
|
+
url("<%= @data[:paths][:css_to_fonts] %>.ttf") format("truetype"),
|
11
|
+
url("<%= @data[:paths][:css_to_fonts] %>.svg#<%= @opts[:font_name] %>") format("svg");
|
12
12
|
font-weight: normal;
|
13
13
|
font-style: normal;
|
14
14
|
}
|
@@ -1,80 +1,39 @@
|
|
1
|
-
##
|
2
|
-
# FONTCUSTOM CONFIG
|
3
|
-
#
|
4
|
-
# This file will automatically be loaded if located inside your vector dir.
|
5
|
-
# Otherwise, pass it to `compile` or `watch` with:
|
6
|
-
#
|
7
|
-
# --config=path/to/config.yml
|
8
|
-
# -c path/to/containing/dir/
|
9
|
-
##
|
10
|
-
|
11
1
|
# ---------------------------------------------------------------------------- #
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# default: INPUT/fontcustom
|
16
|
-
|
17
|
-
#output: path/to/output
|
18
|
-
|
2
|
+
# Project Info
|
3
|
+
# Learn more about these options with `fontcustom help`.
|
19
4
|
# ---------------------------------------------------------------------------- #
|
20
|
-
# Templates
|
21
|
-
# array of templates to generate alongside fonts
|
22
|
-
# accepts shortcuts or paths (absolute, relative to INPUT, or relative to `pwd`)
|
23
|
-
#
|
24
|
-
# shortcuts:
|
25
|
-
# preview
|
26
|
-
# css, scss
|
27
|
-
# bootstrap, bootstrap-scss
|
28
|
-
# bootstrap-ie7, bootstrap-ie7-scss
|
29
|
-
#
|
30
|
-
# default: [ css, preview ]
|
31
5
|
|
32
|
-
#
|
6
|
+
#font_name: My Custom Font
|
7
|
+
#file_hash: false
|
8
|
+
#css_prefix: glyph-
|
9
|
+
#preprocessor_font_path: fonts/fontcustom
|
10
|
+
#debug: true
|
11
|
+
#verbose: false
|
33
12
|
|
34
|
-
# ---------------------------------------------------------------------------- #
|
35
|
-
# Font Name
|
36
|
-
# automatically normalized to lower-spinal-case
|
37
|
-
#
|
38
|
-
# default: fontcustom
|
39
13
|
|
40
|
-
#font_name: my-font
|
41
|
-
|
42
14
|
# ---------------------------------------------------------------------------- #
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
|
48
|
-
#file_hash: false
|
49
|
-
|
15
|
+
# Project Directories
|
16
|
+
# All paths are relative to PROJECT_ROOT (default: working directory).
|
17
|
+
# When assigned a hash, individual components can have their own paths.
|
18
|
+
# When assigned a string, all components share the same path.
|
50
19
|
# ---------------------------------------------------------------------------- #
|
51
|
-
# CSS Class Prefix
|
52
|
-
#
|
53
|
-
# default: icon-
|
54
20
|
|
55
|
-
|
21
|
+
#input:
|
22
|
+
# vectors: app/assets/fontcustom/vectors
|
23
|
+
# templates: app/assets/fontcustom/templates
|
56
24
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# default: none
|
25
|
+
#output:
|
26
|
+
# fonts: app/assets/fonts
|
27
|
+
# css: app/assets/stylesheets
|
28
|
+
# preview: app/views/styleguide
|
63
29
|
|
64
|
-
#font_face_path: assets/fonts/fontcustom
|
65
30
|
|
66
31
|
# ---------------------------------------------------------------------------- #
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
#debug: true
|
73
|
-
|
32
|
+
# Templates
|
33
|
+
# Shortcuts:
|
34
|
+
# preview, css, scss, bootstrap, bootstrap-scss, bootstrap-ie7,
|
35
|
+
# bootstrap-ie7-scss
|
36
|
+
# Custom templates should be saved in the INPUT[:templates] directory.
|
74
37
|
# ---------------------------------------------------------------------------- #
|
75
|
-
# Verbose
|
76
|
-
# print out all file operations
|
77
|
-
#
|
78
|
-
# default: true
|
79
38
|
|
80
|
-
#
|
39
|
+
#templates: [ scss, preview, my-custom-template.yml ]
|
data/lib/fontcustom/util.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require "yaml"
|
2
|
+
require "thor/core_ext/hash_with_indifferent_access"
|
2
3
|
|
3
4
|
module Fontcustom
|
4
5
|
class Util
|
5
|
-
class << self
|
6
|
+
class << self
|
6
7
|
def check_fontforge
|
7
8
|
fontforge = `which fontforge`
|
8
9
|
if fontforge == "" || fontforge == "fontforge not found"
|
@@ -14,38 +15,120 @@ module Fontcustom
|
|
14
15
|
# Priority: Passed args > config file > default
|
15
16
|
def collect_options(args = {})
|
16
17
|
options = Fontcustom::DEFAULT_OPTIONS.clone
|
17
|
-
|
18
|
+
options[:project_root] = args[:project_root] if args[:project_root]
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
args.delete
|
20
|
+
# Parse fontcustom.yml if it exists
|
21
|
+
# Deletes :config so that it can't overwrite the output of .get_config_path
|
22
|
+
options[:config] = args.delete(:config) if args[:config]
|
22
23
|
options[:config] = get_config_path options
|
23
24
|
|
24
25
|
if options[:config]
|
25
|
-
|
26
|
-
|
27
|
-
config = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
26
|
+
begin
|
27
|
+
config = YAML.load File.open(options[:config])
|
28
28
|
options.merge! config
|
29
|
+
rescue
|
30
|
+
raise Fontcustom::Error, "I couldn't read your configuration file. Please check #{options[:config]} and try again."
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
34
|
+
# Override with passed arguments
|
35
|
+
args.delete(:input) unless args[:input] # allows nil input from CLI
|
32
36
|
options.merge! args
|
33
|
-
options[:
|
34
|
-
|
35
|
-
options[:
|
37
|
+
options[:font_name] = options[:font_name].strip.gsub(/\W/, '-')
|
38
|
+
|
39
|
+
options[:input] = get_input_paths options
|
40
|
+
options[:output] = get_output_paths options
|
41
|
+
options[:templates] = get_templates options
|
36
42
|
options
|
37
43
|
end
|
38
44
|
|
39
|
-
# passed path > input
|
40
45
|
def get_config_path(options)
|
41
|
-
if options[:config]
|
42
|
-
File.join options[:config]
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
if options[:config]
|
47
|
+
config = File.expand_path File.join(options[:project_root], options[:config])
|
48
|
+
|
49
|
+
# :config is the path to fontcustom.yml
|
50
|
+
if File.exists?(config) && ! File.directory?(config)
|
51
|
+
config
|
52
|
+
|
53
|
+
# :config is a dir containing fontcustom.yml
|
54
|
+
elsif File.exists? File.join(config, "fontcustom.yml")
|
55
|
+
File.join config, "fontcustom.yml"
|
56
|
+
|
57
|
+
else
|
58
|
+
raise Fontcustom::Error, "I couldn't find your configuration file. Check #{config} and try again."
|
59
|
+
end
|
47
60
|
else
|
48
|
-
|
61
|
+
# fontcustom.yml is in the project_root
|
62
|
+
if File.exists? File.join(options[:project_root], "fontcustom.yml")
|
63
|
+
File.join options[:project_root], "fontcustom.yml"
|
64
|
+
|
65
|
+
# config/fontcustom.yml is in the project_root
|
66
|
+
elsif File.exists? File.join(options[:project_root], "config", "fontcustom.yml")
|
67
|
+
File.join options[:project_root], "config", "fontcustom.yml"
|
68
|
+
|
69
|
+
else
|
70
|
+
# TODO helpful warning that no config was found
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_input_paths(options)
|
77
|
+
paths = if options[:input].is_a? Hash
|
78
|
+
input = Thor::CoreExt::HashWithIndifferentAccess.new options[:input]
|
79
|
+
raise Fontcustom::Error, "INPUT should be a string or a hash containing a \"vectors\" key." unless input[:vectors]
|
80
|
+
|
81
|
+
input[:vectors] = File.expand_path File.join(options[:project_root], input[:vectors])
|
82
|
+
raise Fontcustom::Error, "INPUT[\"vectors\"] should be a directory. Check #{input[:vectors]} and try again." unless File.directory? input[:vectors]
|
83
|
+
|
84
|
+
if input[:templates]
|
85
|
+
input[:templates] = File.expand_path File.join(options[:project_root], input[:templates])
|
86
|
+
raise Fontcustom::Error, "INPUT[\"templates\"] should be a directory. Check #{input[:templates]} and try again." unless File.directory? input[:templates]
|
87
|
+
else
|
88
|
+
input[:templates] = input[:vectors]
|
89
|
+
end
|
90
|
+
input
|
91
|
+
elsif options[:input].is_a? String
|
92
|
+
input = File.join options[:project_root], options[:input]
|
93
|
+
raise Fontcustom::Error, "INPUT should be a directory. Check #{input} and try again." unless File.directory? input
|
94
|
+
Thor::CoreExt::HashWithIndifferentAccess.new({
|
95
|
+
:vectors => input,
|
96
|
+
:templates => input
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
if Dir[File.join(paths[:vectors], "*.{svg,eps}")].empty?
|
101
|
+
raise Fontcustom::Error, "#{paths[:vectors]} doesn't contain any vectors (*.svg or *.eps files)."
|
102
|
+
end
|
103
|
+
|
104
|
+
paths
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_output_paths(options)
|
108
|
+
if options[:output].is_a? Hash
|
109
|
+
output = Thor::CoreExt::HashWithIndifferentAccess.new options[:output]
|
110
|
+
raise Fontcustom::Error, "OUTPUT should be a string or a hash containing a \"fonts\" key." unless output[:fonts]
|
111
|
+
|
112
|
+
output.each do |key, val|
|
113
|
+
output[key] = File.expand_path File.join(options[:project_root], val)
|
114
|
+
end
|
115
|
+
|
116
|
+
output[:css] ||= output[:fonts]
|
117
|
+
output[:preview] ||= output[:fonts]
|
118
|
+
output
|
119
|
+
else
|
120
|
+
if options[:output].is_a? String
|
121
|
+
output = File.expand_path File.join(options[:project_root], options[:output])
|
122
|
+
raise Fontcustom::Error, "OUTPUT should be a directory, not a file. Check #{output} and try again." if File.exists?(output) && ! File.directory?(output)
|
123
|
+
else
|
124
|
+
# TODO friendly warning that we're defaulting to pwd/:font_name
|
125
|
+
output = File.join options[:project_root], options[:font_name]
|
126
|
+
end
|
127
|
+
Thor::CoreExt::HashWithIndifferentAccess.new({
|
128
|
+
:fonts => output,
|
129
|
+
:css => output,
|
130
|
+
:preview => output
|
131
|
+
})
|
49
132
|
end
|
50
133
|
end
|
51
134
|
|
@@ -54,8 +137,10 @@ module Fontcustom
|
|
54
137
|
#
|
55
138
|
# Could arguably belong in Generator::Template, however, it's nice to
|
56
139
|
# be able to catch template errors before any generator runs.
|
57
|
-
def
|
140
|
+
def get_templates(options)
|
141
|
+
# ensure that preview has plain stylesheet to reference
|
58
142
|
options[:templates] << "css" if options[:templates].include?("preview") && ! options[:templates].include?("css")
|
143
|
+
|
59
144
|
options[:templates].map do |template|
|
60
145
|
case template
|
61
146
|
when "preview"
|
@@ -73,21 +158,13 @@ module Fontcustom
|
|
73
158
|
when "bootstrap-ie7-scss"
|
74
159
|
File.join gem_lib_path, "templates", "_fontcustom-bootstrap-ie7.scss"
|
75
160
|
else
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
File.join options[:input], template
|
80
|
-
else
|
81
|
-
raise Fontcustom::Error, "We couldn't find your custom template #{template}. Double check and try again?"
|
82
|
-
end
|
161
|
+
path = File.join options[:input][:templates], template
|
162
|
+
raise Fontcustom::Error, "We couldn't find your custom template at #{path}. Double check and try again?" unless File.exists? path
|
163
|
+
path
|
83
164
|
end
|
84
165
|
end
|
85
166
|
end
|
86
167
|
|
87
|
-
def clear_file(file)
|
88
|
-
File.open(file, "w") {}
|
89
|
-
end
|
90
|
-
|
91
168
|
def gem_lib_path
|
92
169
|
File.expand_path(File.join(File.dirname(__FILE__)))
|
93
170
|
end
|
data/lib/fontcustom/version.rb
CHANGED
data/lib/fontcustom/watcher.rb
CHANGED
@@ -5,34 +5,50 @@ module Fontcustom
|
|
5
5
|
class Watcher
|
6
6
|
def initialize(opts)
|
7
7
|
@opts = opts
|
8
|
-
@
|
8
|
+
@vector_listener = Listen.to(@opts[:input][:vectors]).relative_paths(true).filter(/\.(eps|svg)$/).change(&callback)
|
9
|
+
|
10
|
+
templates = @opts[:templates].dup
|
11
|
+
templates.delete_if do |template|
|
12
|
+
template.match Util.gem_lib_path
|
13
|
+
end
|
14
|
+
unless templates.empty?
|
15
|
+
templates = templates.map do |template|
|
16
|
+
File.basename template
|
17
|
+
end
|
18
|
+
@template_listener = Listen.to(@opts[:input][:templates]).relative_paths(true).filter(/(#{templates.join("|")})/).change(&callback)
|
19
|
+
end
|
20
|
+
|
9
21
|
@opts[:blocking] = @opts[:blocking] == false ? false : true
|
10
|
-
|
22
|
+
unless @opts[:blocking]
|
23
|
+
@vector_listener = @vector_listener.polling_fallback_message(false)
|
24
|
+
@template_listener = @template_listener.polling_fallback_message(false) if @template_listener
|
25
|
+
end
|
11
26
|
end
|
12
27
|
|
13
28
|
def watch
|
14
|
-
puts "Font Custom is watching your icons at #{@opts[:input]}. Press Ctrl + C to stop."
|
29
|
+
puts "Font Custom is watching your icons at #{@opts[:input][:vectors]}. Press Ctrl + C to stop."
|
15
30
|
compile unless @opts[:skip_first]
|
16
31
|
|
17
32
|
if @opts[:blocking]
|
18
|
-
@
|
33
|
+
@vector_listener.start!
|
34
|
+
@template_listener.start! if @template_listener
|
19
35
|
else
|
20
|
-
@
|
36
|
+
@vector_listener.start
|
37
|
+
@template_listener.start if @template_listener
|
21
38
|
end
|
22
39
|
|
23
40
|
rescue Fontcustom::Error => e
|
24
41
|
show_error e
|
25
42
|
|
26
43
|
# Catches Ctrl + C
|
27
|
-
# TODO Does the listen gem have a better way of handling this?
|
28
44
|
rescue SignalException
|
29
45
|
stop
|
30
46
|
end
|
31
47
|
|
32
48
|
def stop
|
33
|
-
|
49
|
+
@vector_listener.stop
|
50
|
+
@template_listener.stop if @template_listener
|
34
51
|
puts "\nFont Custom is signing off. Good night and good luck."
|
35
|
-
@listener.stop
|
36
52
|
end
|
37
53
|
|
38
54
|
private
|