easy_html_generator 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +44 -0
- data/LICENSE +22 -0
- data/README.md +332 -0
- data/bin/ehg +52 -0
- data/lib/easy_html_generator.rb +100 -0
- data/lib/easy_html_generator/checksum.rb +39 -0
- data/lib/easy_html_generator/config.rb +147 -0
- data/lib/easy_html_generator/generator.rb +25 -0
- data/lib/easy_html_generator/generator/base.rb +83 -0
- data/lib/easy_html_generator/generator/combine.rb +28 -0
- data/lib/easy_html_generator/generator/compile/coffee.rb +30 -0
- data/lib/easy_html_generator/generator/compile/haml.rb +70 -0
- data/lib/easy_html_generator/generator/compile/haml/context.rb +52 -0
- data/lib/easy_html_generator/generator/compile/haml/helper/activesupport_override.rb +48 -0
- data/lib/easy_html_generator/generator/compile/haml/helper/asset_helper.rb +78 -0
- data/lib/easy_html_generator/generator/compile/haml/layout.rb +35 -0
- data/lib/easy_html_generator/generator/compile/sass.rb +47 -0
- data/lib/easy_html_generator/generator/copy.rb +44 -0
- data/lib/easy_html_generator/generator/delete.rb +20 -0
- data/lib/easy_html_generator/generator/minimize/css.rb +28 -0
- data/lib/easy_html_generator/generator/minimize/html.rb +32 -0
- data/lib/easy_html_generator/generator/minimize/images.rb +33 -0
- data/lib/easy_html_generator/generator/minimize/js.rb +28 -0
- data/lib/easy_html_generator/generator/service/analytics.rb +34 -0
- data/lib/easy_html_generator/generator/service/bower.rb +34 -0
- data/lib/easy_html_generator/generator/service/grunt.rb +24 -0
- data/lib/easy_html_generator/generator/structure.rb +20 -0
- data/lib/easy_html_generator/generators.rb +53 -0
- data/lib/easy_html_generator/project.rb +77 -0
- data/lib/easy_html_generator/rackapp.rb +67 -0
- data/lib/easy_html_generator/workspace.rb +59 -0
- data/src/demo/assets/images/demo.png +0 -0
- data/src/demo/assets/public/robots.txt +0 -0
- data/src/demo/assets/scripts/demo.js.coffee +1 -0
- data/src/demo/assets/scripts/plain.js +0 -0
- data/src/demo/assets/styles/app.css.sass +2 -0
- data/src/demo/assets/styles/plain.css +0 -0
- data/src/demo/lib/generators/project_generator.rb +12 -0
- data/src/demo/lib/helper/projecthelper.rb +5 -0
- data/src/demo/views/index.html.haml +1 -0
- data/src/demo/views/index/_lore_ipsum.haml +1 -0
- data/src/demo/views/layout.haml +8 -0
- data/src/demo/views/plain.html +0 -0
- data/src/shared/assets/images/shared.png +0 -0
- data/src/shared/assets/scripts/shared.js.coffee +1 -0
- data/src/shared/assets/scripts/shared.plain.js +0 -0
- data/src/shared/assets/styles/mixins/_arrows.sass +32 -0
- data/src/shared/assets/styles/mixins/_bootstrap-fixes.sass +12 -0
- data/src/shared/assets/styles/mixins/_buttons.sass +32 -0
- data/src/shared/assets/styles/mixins/_css3.sass +266 -0
- data/src/shared/assets/styles/mixins/_headjs-bootstrap-mediaqueries.sass +42 -0
- data/src/shared/assets/styles/mixins/_helper.sass +70 -0
- data/src/shared/assets/styles/mixins/_normalize.sass +340 -0
- data/src/shared/assets/styles/mixins/_reset.sass +46 -0
- data/src/shared/lib/generators/shared_generator.rb +12 -0
- data/src/shared/lib/helper/shared_helper.rb +16 -0
- data/src/shared/project.yml +127 -0
- data/src/shared/views/404.yml +5 -0
- data/src/template/assets/public/robots.txt +0 -0
- data/src/template/assets/scripts/index.js.coffee +1 -0
- data/src/template/assets/styles/index.css.sass +2 -0
- data/src/template/lib/generators/project_generator.rb +12 -0
- data/src/template/lib/helper/project_helper.rb +5 -0
- data/src/template/project.yml +128 -0
- data/src/template/views/index.html.haml +4 -0
- data/src/template/views/index/_lore_ipsum.haml +2 -0
- data/src/template/views/layout.haml +9 -0
- metadata +402 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'fileutils'
|
3
|
+
require 'haml'
|
4
|
+
require 'easy_html_generator/generator/base'
|
5
|
+
|
6
|
+
# this generator compiles haml files from src folder and copies them
|
7
|
+
# to the dist folder
|
8
|
+
class EasyHtmlGenerator::Generator::Compile::Haml <
|
9
|
+
EasyHtmlGenerator::Generator::Base
|
10
|
+
|
11
|
+
require 'easy_html_generator/generator/compile/haml/context'
|
12
|
+
require 'easy_html_generator/generator/compile/haml/layout'
|
13
|
+
|
14
|
+
def initialize(project, config)
|
15
|
+
super(project, config)
|
16
|
+
@config.src = project.config.paths.src.views
|
17
|
+
@config.dest = project.config.paths.dist.views
|
18
|
+
end
|
19
|
+
|
20
|
+
def do_input(input, layout, context, scope, _input_file)
|
21
|
+
# If the file being processed by Haml contains a yield statement,
|
22
|
+
# the block passed to "render" will be called when it's hit.
|
23
|
+
result = layout.render(context, body_class: scope) do
|
24
|
+
# Render the actual page contents in place of the call to "yield".
|
25
|
+
body = Haml::Engine.new(input, @config.renderer)
|
26
|
+
body.render(context)
|
27
|
+
end
|
28
|
+
|
29
|
+
return result unless @config.minimize
|
30
|
+
|
31
|
+
EasyHtmlGenerator::Generator::Minimize::Html.compress result
|
32
|
+
end
|
33
|
+
|
34
|
+
def should_do_file?(i)
|
35
|
+
# dont check if file changed, because partials could have changed
|
36
|
+
!File.basename(i).start_with? '_'
|
37
|
+
end
|
38
|
+
|
39
|
+
def input_to_output_file(i)
|
40
|
+
super(i).gsub('.html.haml', '.html')
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate
|
44
|
+
return unless @config.enabled
|
45
|
+
|
46
|
+
log_running
|
47
|
+
|
48
|
+
FileUtils.mkdir_p dest_path
|
49
|
+
|
50
|
+
walk_files(File.join(src_path, @config.selector)) do |i, o|
|
51
|
+
next unless should_do_file? i
|
52
|
+
|
53
|
+
compile_file i, o
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def compile_file(src, target)
|
58
|
+
file_name = File.basename src
|
59
|
+
scope = file_name.split('.').first
|
60
|
+
context = Context.new(@project, @config, scope)
|
61
|
+
layout = Layout.layout_from_file(@project.src_path_to(:views),
|
62
|
+
src, @config.renderer)
|
63
|
+
|
64
|
+
begin
|
65
|
+
do_file(src, target, layout, context, scope)
|
66
|
+
rescue StandardError => e
|
67
|
+
raise e, "#{e.message} in #{src} ", e.backtrace
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'action_view'
|
3
|
+
|
4
|
+
helper_path = 'easy_html_generator/generator/compile/haml/helper'
|
5
|
+
require "#{helper_path}/activesupport_override.rb"
|
6
|
+
require "#{helper_path}/asset_helper.rb"
|
7
|
+
|
8
|
+
# this class represents the render context for a haml file
|
9
|
+
class EasyHtmlGenerator::Generator::Compile::Haml::Context
|
10
|
+
# Any properties of this object are available in the Haml templates.
|
11
|
+
attr_reader :project
|
12
|
+
|
13
|
+
include ActionView::Helpers
|
14
|
+
include ActivesupportOverride
|
15
|
+
include AssetHelper
|
16
|
+
|
17
|
+
def initialize(project, config, scope)
|
18
|
+
@config = config
|
19
|
+
@project = project
|
20
|
+
@scope = scope
|
21
|
+
@input_folder = @project.src_path_to :views
|
22
|
+
|
23
|
+
load_helper EasyHtmlGenerator::SHARED_HELPER_PATH
|
24
|
+
load_helper @project.src_path_to :helper
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_helper(folder)
|
28
|
+
Dir.glob("#{folder}/**/*.rb").each do |path|
|
29
|
+
load path
|
30
|
+
file_without_ext = path.split('/')[-1].split('.').first
|
31
|
+
module_name = file_without_ext.classify
|
32
|
+
STDERR.puts " | -> loading haml helper: #{module_name.green}"
|
33
|
+
self.class.send(:include, module_name.constantize)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def render_partial(file_name)
|
38
|
+
file_to_render = "#{@input_folder}/#{file_name}.haml"
|
39
|
+
if @scope
|
40
|
+
# Look for a partial prefixed with the current "scope"
|
41
|
+
# (which is just the name of the primary template being rendered).
|
42
|
+
scope_file = "#{@input_folder}/#{@scope}_#{file_name}.haml"
|
43
|
+
# Use it if it's there.
|
44
|
+
file_to_render = scope_file if File.exist? scope_file
|
45
|
+
end
|
46
|
+
if File.exist? file_to_render
|
47
|
+
Haml::Engine.new(File.read(file_to_render), @config).render self
|
48
|
+
end
|
49
|
+
rescue StandardError => e
|
50
|
+
raise e, "#{e.message} in #{file_name} ", e.backtrace
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# this module overrides some active support methods for working paths etc.
|
4
|
+
module ActivesupportOverride
|
5
|
+
def stylesheet_link_tag(path, media = 'screen')
|
6
|
+
'<link href="' + path_to_css(path) + '" media="' + media +
|
7
|
+
'" rel="stylesheet" type="text/css" />'
|
8
|
+
end
|
9
|
+
|
10
|
+
def javascript_include_tag(path)
|
11
|
+
'<script src="' + path_to_js(path) + '"></script>'
|
12
|
+
end
|
13
|
+
|
14
|
+
def meta_tag(type, content)
|
15
|
+
'<meta name="' + type + '" content="' + content + '" />'
|
16
|
+
end
|
17
|
+
|
18
|
+
def meta_tag_http(type, content)
|
19
|
+
'<meta http-equiv="' + type + '" content="' + content + '" />'
|
20
|
+
end
|
21
|
+
|
22
|
+
def link_tag(source, relation, type = '')
|
23
|
+
type = " type='#{type}'" if type
|
24
|
+
'<link rel="' + relation + '" href="' + source + '"' + type + '/>'
|
25
|
+
end
|
26
|
+
|
27
|
+
def path_to_css(path)
|
28
|
+
return path if external_path?(path)
|
29
|
+
test = @project.uri_path_to(:styles, path)
|
30
|
+
file_exists?(test) ? test : path
|
31
|
+
end
|
32
|
+
|
33
|
+
def path_to_js(path)
|
34
|
+
return path if external_path? path
|
35
|
+
test = @project.uri_path_to(:scripts, path)
|
36
|
+
file_exists?(test) ? test : path
|
37
|
+
end
|
38
|
+
|
39
|
+
def path_to_image(path)
|
40
|
+
return path if external_path? path
|
41
|
+
test = @project.uri_path_to(:images, path)
|
42
|
+
file_exists?(test) ? test : path
|
43
|
+
end
|
44
|
+
|
45
|
+
# def url_for(options = '')
|
46
|
+
# return options
|
47
|
+
# end
|
48
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'coffee-script'
|
3
|
+
require 'uglifier'
|
4
|
+
|
5
|
+
# this module exposes additional features for haml files
|
6
|
+
module AssetHelper
|
7
|
+
include ActionView::Context
|
8
|
+
|
9
|
+
def external_path?(path)
|
10
|
+
path.start_with?('//') || path.start_with?('http')
|
11
|
+
end
|
12
|
+
|
13
|
+
def file_exists?(path)
|
14
|
+
File.exist? File.join(EasyHtmlGenerator::DIST_PATH, path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_coffee(&block)
|
18
|
+
input = capture_haml(&block)
|
19
|
+
content_tag :script do
|
20
|
+
raw @project.generators.compile_coffee.do_input input
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_sass(&block)
|
25
|
+
input = capture_haml(&block)
|
26
|
+
content_tag :style do
|
27
|
+
raw @project.generators.compile_sass.do_input input
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def glyph_icon_classes(icon)
|
32
|
+
"glyphicon glyphicon-#{icon}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def glyph_icon(icon, content = '')
|
36
|
+
content_tag(:span, content, class: glyph_icon_classes(icon))
|
37
|
+
end
|
38
|
+
|
39
|
+
def headjs_javascript_include_tag(tag, path)
|
40
|
+
content_tag :script do
|
41
|
+
raw "head.load({'#{tag}': '#{path_to_js(path)}'});"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def headjs_stylesheet_link_tag(tag, path)
|
46
|
+
headjs_javascript_include_tag(tag, path_to_css(path))
|
47
|
+
end
|
48
|
+
|
49
|
+
def inline_stylesheet_link_tag(path)
|
50
|
+
return path if external_path?(path)
|
51
|
+
|
52
|
+
styles_path = @project.dist_path_to(:styles, path)
|
53
|
+
if File.exists? styles_path
|
54
|
+
path = styles_path
|
55
|
+
else
|
56
|
+
path = File.join(@project.dist_path, path)
|
57
|
+
end
|
58
|
+
|
59
|
+
content_tag :style do
|
60
|
+
raw @project.generators.minimize_css.do_input File.read(path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def inline_javascript_include_tag(path)
|
65
|
+
return path if external_path?(path)
|
66
|
+
|
67
|
+
scripts_path = @project.dist_path_to(:scripts, path)
|
68
|
+
if File.exists? scripts_path
|
69
|
+
path = scripts_path
|
70
|
+
else
|
71
|
+
path = File.join(@project.dist_path, path)
|
72
|
+
end
|
73
|
+
|
74
|
+
content_tag :script do
|
75
|
+
raw @project.generators.minimize_js.do_input File.read(path)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# this class helps with haml layout files
|
4
|
+
class EasyHtmlGenerator::Generator::Compile::Haml::Layout
|
5
|
+
@layouts = {}
|
6
|
+
|
7
|
+
def self.layout_from_file(haml_folder, template, haml_config)
|
8
|
+
first_line = File.readlines(template).first
|
9
|
+
|
10
|
+
if first_line.include? '-# ehg layout:'
|
11
|
+
name = first_line.sub('-# ehg layout:', '').strip
|
12
|
+
else
|
13
|
+
name = 'layout'
|
14
|
+
end
|
15
|
+
|
16
|
+
path = "#{haml_folder}#{name}.haml"
|
17
|
+
path = "#{haml_folder}layout.haml" unless File.exist? path
|
18
|
+
|
19
|
+
load_from_file(path, haml_config) if should_load_from_file path
|
20
|
+
|
21
|
+
@layouts[path]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.should_load_from_file(layout_file)
|
25
|
+
return true unless @layouts.key? layout_file
|
26
|
+
EasyHtmlGenerator::Checksum.file_changed? layout_file
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.load_from_file(layout_file, haml_config)
|
30
|
+
fail "layout not found '#{path}'" unless File.exist? layout_file
|
31
|
+
@layouts[layout_file] = Haml::Engine.new(File.read(layout_file),
|
32
|
+
haml_config)
|
33
|
+
EasyHtmlGenerator::Checksum.store_file(layout_file)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'sass'
|
3
|
+
|
4
|
+
require 'easy_html_generator'
|
5
|
+
require 'easy_html_generator/generator/base'
|
6
|
+
|
7
|
+
# this generator compiles sass files from src folder and copies them
|
8
|
+
# to the dist folder
|
9
|
+
class EasyHtmlGenerator::Generator::Compile::Sass <
|
10
|
+
EasyHtmlGenerator::Generator::Base
|
11
|
+
|
12
|
+
def self.add_load_path(path)
|
13
|
+
::Sass.load_paths << path unless ::Sass.load_paths.include?(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
add_load_path EasyHtmlGenerator::SHARED_STYLES_PATH
|
17
|
+
|
18
|
+
def initialize(project, config)
|
19
|
+
super(project, config)
|
20
|
+
|
21
|
+
self.class.add_load_path "#{@project.src_path_to :styles}"
|
22
|
+
|
23
|
+
@config.src = project.config.paths.src.styles
|
24
|
+
@config.dest = project.config.paths.dist.styles
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_input(input, src='inline')
|
28
|
+
renderer = ::Sass::Engine.new(input)
|
29
|
+
result = renderer.render
|
30
|
+
|
31
|
+
return result unless @config.minimize
|
32
|
+
|
33
|
+
EasyHtmlGenerator::Generator::Minimize::Css.compress result
|
34
|
+
rescue StandardError => e
|
35
|
+
raise e, "#{e.message} in #{src} ", e.backtrace
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def should_do_file?(i)
|
40
|
+
# dont check if file changed, because partials could have changed
|
41
|
+
!File.basename(i).start_with? '_'
|
42
|
+
end
|
43
|
+
|
44
|
+
def input_to_output_file(i)
|
45
|
+
super(i).gsub('.css.sass', '.css')
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'fileutils'
|
3
|
+
require 'easy_html_generator/generator/base'
|
4
|
+
|
5
|
+
# this generator copies files from src ro dist folder
|
6
|
+
class EasyHtmlGenerator::Generator::Copy < EasyHtmlGenerator::Generator::Base
|
7
|
+
def generate
|
8
|
+
return unless @config.enabled
|
9
|
+
|
10
|
+
log_running
|
11
|
+
|
12
|
+
@config.dirs.each do |config|
|
13
|
+
src_path = "#{@project.src_path}/#{config.source}"
|
14
|
+
dest_path = "#{@project.dist_path}/#{config.target}"
|
15
|
+
|
16
|
+
self.class.copy_r(src_path, dest_path, config.selector)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# cannot use copy_entry or cp_r with symbolic existent links in target
|
21
|
+
# FileUtils::copy_entry(src_dir, output_folder, true, false, true)
|
22
|
+
# if File.directory? src_dir
|
23
|
+
def self.copy_r(src_path, dest_path, selector)
|
24
|
+
return unless File.exist? src_path
|
25
|
+
|
26
|
+
FileUtils.mkdir_p dest_path
|
27
|
+
|
28
|
+
Dir[File.join(src_path, selector)].each do |input_file|
|
29
|
+
next if input_file.empty?
|
30
|
+
|
31
|
+
output_file = input_file.sub(src_path, dest_path)
|
32
|
+
|
33
|
+
copy(input_file, output_file)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.copy(input_file, output_file)
|
38
|
+
if File.directory? input_file
|
39
|
+
FileUtils.mkdir_p(output_file) unless File.exist? output_file
|
40
|
+
else
|
41
|
+
FileUtils.copy input_file, output_file
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'fileutils'
|
3
|
+
require 'easy_html_generator/generator/base'
|
4
|
+
|
5
|
+
# this generator deletes in the dest folder
|
6
|
+
class EasyHtmlGenerator::Generator::Delete < EasyHtmlGenerator::Generator::Base
|
7
|
+
def generate
|
8
|
+
return unless @config.enabled
|
9
|
+
|
10
|
+
log_running
|
11
|
+
|
12
|
+
@config.files.each do |file_pattern|
|
13
|
+
Dir[File.join(@project.dist_path, file_pattern)].each do |file|
|
14
|
+
EasyHtmlGenerator::Checksum.invalidate_file file
|
15
|
+
|
16
|
+
FileUtils.rm_rf file
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'cssminify'
|
3
|
+
require 'easy_html_generator/generator/base'
|
4
|
+
|
5
|
+
# this generator minifies css files from src folder and copies them
|
6
|
+
# to the dist folder
|
7
|
+
class EasyHtmlGenerator::Generator::Minimize::Css <
|
8
|
+
EasyHtmlGenerator::Generator::Base
|
9
|
+
|
10
|
+
def initialize(project, config)
|
11
|
+
super(project, config)
|
12
|
+
|
13
|
+
@config.src = project.config.paths.src.styles
|
14
|
+
@config.dest = project.config.paths.dist.styles
|
15
|
+
end
|
16
|
+
|
17
|
+
def do_input(input, *_args)
|
18
|
+
self.class.compress input
|
19
|
+
end
|
20
|
+
|
21
|
+
def input_to_output_file(i)
|
22
|
+
super(i).gsub('.css', "#{@config.prefix_extension}.css")
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.compress(input)
|
26
|
+
CSSminify.compress input
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'htmlcompressor'
|
3
|
+
require 'easy_html_generator/generator/base'
|
4
|
+
|
5
|
+
# this generator minifies html files from src folder and copies them
|
6
|
+
# to the dist folder
|
7
|
+
class EasyHtmlGenerator::Generator::Minimize::Html <
|
8
|
+
EasyHtmlGenerator::Generator::Base
|
9
|
+
|
10
|
+
def initialize(project, config)
|
11
|
+
super(project, config)
|
12
|
+
|
13
|
+
@config.src = project.config.paths.src.views
|
14
|
+
@config.dest = project.config.paths.dist.views
|
15
|
+
end
|
16
|
+
|
17
|
+
def do_input(input, *_args)
|
18
|
+
self.class.compress input
|
19
|
+
end
|
20
|
+
|
21
|
+
def input_to_output_file(i)
|
22
|
+
super(i).gsub('.html', "#{@config.prefix_extension}.html")
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.compressor
|
26
|
+
@compressor ||= HtmlCompressor::Compressor.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.compress(input)
|
30
|
+
compressor.compress input
|
31
|
+
end
|
32
|
+
end
|