juli 2.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 +7 -0
- data/.gitignore +26 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.rdoc +39 -0
- data/Rakefile +89 -0
- data/bin/console +14 -0
- data/bin/je +73 -0
- data/bin/juli +82 -0
- data/bin/juli_tb.rb +76 -0
- data/bin/setup +7 -0
- data/juli.gemspec +29 -0
- data/lib/juli.rb +21 -0
- data/lib/juli/absyn.rb +206 -0
- data/lib/juli/command.rb +180 -0
- data/lib/juli/command/file_entry.rb +12 -0
- data/lib/juli/command/recent_update.rb +52 -0
- data/lib/juli/command/sitemap.rb +55 -0
- data/lib/juli/command/tag.rb +81 -0
- data/lib/juli/line_parser.y +212 -0
- data/lib/juli/macro.rb +39 -0
- data/lib/juli/macro/amazon.rb +33 -0
- data/lib/juli/macro/jmap.rb +38 -0
- data/lib/juli/macro/photo.rb +161 -0
- data/lib/juli/macro/tag.rb +136 -0
- data/lib/juli/macro/template.rb +37 -0
- data/lib/juli/macro/template_base.rb +44 -0
- data/lib/juli/macro/wikipedia.rb +19 -0
- data/lib/juli/parser.y +360 -0
- data/lib/juli/template/default.html +64 -0
- data/lib/juli/template/facebook.html +82 -0
- data/lib/juli/template/je-bash-complete +42 -0
- data/lib/juli/template/juli.css +173 -0
- data/lib/juli/template/juli.js +87 -0
- data/lib/juli/template/locale/en.yml +10 -0
- data/lib/juli/template/locale/ja.yml +10 -0
- data/lib/juli/template/prototype.js +4320 -0
- data/lib/juli/template/simple.html +45 -0
- data/lib/juli/template/sitemap.html +78 -0
- data/lib/juli/template/sitemap_order_by_mtime_DESC.html +78 -0
- data/lib/juli/template/slidy.html +126 -0
- data/lib/juli/template/sourceforge.html +71 -0
- data/lib/juli/template/takahashi_method.html +116 -0
- data/lib/juli/util.rb +255 -0
- data/lib/juli/util/juli_i18n.rb +32 -0
- data/lib/juli/version.rb +3 -0
- data/lib/juli/visitor.rb +12 -0
- data/lib/juli/visitor/html.rb +462 -0
- data/lib/juli/visitor/html/helper.rb +97 -0
- data/lib/juli/visitor/html/helper/contents.rb +76 -0
- data/lib/juli/visitor/html/helper/fb_comments.rb +68 -0
- data/lib/juli/visitor/html/helper/fb_like.rb +37 -0
- data/lib/juli/visitor/html/tag_helper.rb +40 -0
- data/lib/juli/visitor/slidy.rb +39 -0
- data/lib/juli/visitor/takahashi_method.rb +41 -0
- data/lib/juli/visitor/tree.rb +135 -0
- data/lib/juli/wiki.rb +52 -0
- data/sample/protected_photo/2012-04-22/DCIM/101_PANA/P1010441.JPG +0 -0
- data/sample/update_public_juli.rb +71 -0
- data/setup.rb +1585 -0
- metadata +211 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'juli/macro/template_base'
|
2
|
+
|
3
|
+
module Juli
|
4
|
+
module Macro
|
5
|
+
# generate Amazon link
|
6
|
+
#
|
7
|
+
# Amazon link template can be defined at 'amazon' entry in
|
8
|
+
# JULI_REPO/.juli/config.
|
9
|
+
#
|
10
|
+
# if it is not defined, then default template here is used.
|
11
|
+
class Amazon < TemplateBase
|
12
|
+
DEFAULT_TEMPLATE = <<-EOS
|
13
|
+
<iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=wells00-22&o=9&p=8&l=as1&asins=%{asins}&ref=tf_til&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr"
|
14
|
+
style="float:right; width:120px;height:240px;"
|
15
|
+
scrolling="no" marginwidth="0" marginheight="0" frameborder="0"
|
16
|
+
></iframe>
|
17
|
+
EOS
|
18
|
+
|
19
|
+
def self.conf_template
|
20
|
+
<<EOM
|
21
|
+
# Amazon association link with any ASIN can be rendered
|
22
|
+
# at any location in juli text. Its template is as follows.
|
23
|
+
# This HTML is just an example so that you can change as you like.
|
24
|
+
# '%{asins}' in the template will be replaced by actual ASIN:
|
25
|
+
#
|
26
|
+
#amazon: '<iframe src="http://rcm-jp.amazon.co.jp/e/cm?o=9&p=8&l=as1&asins=%{asins}&ref=tf_til&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr" style="float:right; width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>'
|
27
|
+
EOM
|
28
|
+
end
|
29
|
+
|
30
|
+
def place_holder; 'asins'; end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
require 'juli/macro/template_base'
|
3
|
+
|
4
|
+
module Juli
|
5
|
+
module Macro
|
6
|
+
# generate Map HTML.
|
7
|
+
#
|
8
|
+
# The purpose of this macro is to provide I/F for map.
|
9
|
+
# When map-site(like google) service is discontinued, or
|
10
|
+
# URL is changed, it is enough to change:
|
11
|
+
#
|
12
|
+
# 1. .juli/config jmap entry or
|
13
|
+
# 1. this macro implementation.
|
14
|
+
#
|
15
|
+
# There is no need to modify all of wiki pages which use 'jmap' macro.
|
16
|
+
#
|
17
|
+
# 'J' of jmap stands for Juli. it is because 'map' in ruby is quite common
|
18
|
+
# method so that necessary to avoid name confusion.
|
19
|
+
#
|
20
|
+
# Currently, Google map is used.
|
21
|
+
class Jmap < TemplateBase
|
22
|
+
# Thank you, http://mapki.com/wiki/Google_Map_Parameters !!
|
23
|
+
DEFAULT_TEMPLATE = '<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps?q=loc:%{coord}&num=1&ie=UTF8&t=m&z=14&output=embed"></iframe><br /><small><a href="http://maps.google.com/maps?q=loc:%{coord}&num=1&ie=UTF8&t=m&z=14&source=embed" style="color:#0000FF;text-align:left">View Larger Map</a></small>'
|
24
|
+
|
25
|
+
def self.conf_template
|
26
|
+
<<EOM
|
27
|
+
# HTML template to draw map. If not set, default defined at
|
28
|
+
# Juli::Macro::Jmap::DEFAULT_TEMPLATE is used.
|
29
|
+
# %{coord} in the template wiil be replaced to the actual 1st parameter.
|
30
|
+
#
|
31
|
+
#jmap: '#{DEFAULT_TEMPLATE}'
|
32
|
+
EOM
|
33
|
+
end
|
34
|
+
|
35
|
+
def place_holder; 'coord'; end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'RMagick'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
module Juli
|
7
|
+
module Macro
|
8
|
+
# embed photo(image) in juli wiki text with minimum maintenance cost
|
9
|
+
#
|
10
|
+
# See 'doc/photo(macro).txt' for the detail.
|
11
|
+
class Photo < Base
|
12
|
+
include Juli::Visitor::Html::TagHelper
|
13
|
+
|
14
|
+
PUBLIC_PHOTO_DIR_DEFAULT = 'public_photo'
|
15
|
+
SEED_DEFAULT = '-- Juli seed default!! --'
|
16
|
+
CONF_DEFAULT = {
|
17
|
+
'mount' => '/home/YOUR_NAME/Photos',
|
18
|
+
'small' => {
|
19
|
+
'width' => 512, # default small width in pixel
|
20
|
+
'style' => 'float: right'
|
21
|
+
},
|
22
|
+
'large' => {
|
23
|
+
'width' => 1024 # default large width in pixel
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
class DirNameConflict < Juli::JuliError; end
|
28
|
+
class ConfigNoMount < Juli::JuliError; end
|
29
|
+
|
30
|
+
def self.conf_template
|
31
|
+
<<EOM
|
32
|
+
# Photo macro setup sample is as follows.
|
33
|
+
#
|
34
|
+
#photo:
|
35
|
+
# mount: '#{CONF_DEFAULT['mount']}'
|
36
|
+
# small:
|
37
|
+
# width: #{CONF_DEFAULT['small']['width']}
|
38
|
+
# style: '#{CONF_DEFAULT['small']['style']}'
|
39
|
+
# large:
|
40
|
+
# width: #{CONF_DEFAULT['large']['width']}
|
41
|
+
EOM
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_conf_default(conf)
|
45
|
+
set_conf_default_sub(conf, 'photo', CONF_DEFAULT)
|
46
|
+
end
|
47
|
+
|
48
|
+
# rotate image to fit orientation
|
49
|
+
def rotate(img)
|
50
|
+
exif = img.get_exif_by_entry(:Orientation)
|
51
|
+
return img if !(exif && exif[0] && exif[0][0] == :Orientation)
|
52
|
+
case exif[0][1]
|
53
|
+
when '1' # Normal
|
54
|
+
img
|
55
|
+
when '6' # 90 degree
|
56
|
+
img.rotate(90) # 90[deg] to clock direction
|
57
|
+
when '8' # 270 degree
|
58
|
+
img.rotate(-90) # 90[deg] to reversed-clock direction
|
59
|
+
else
|
60
|
+
img
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# public photo directory is used to:
|
65
|
+
#
|
66
|
+
# * store converted photo from original one
|
67
|
+
# * protect private photo in 'mount' directory from public web access
|
68
|
+
# by copying (with conversion) to it on demand.
|
69
|
+
#
|
70
|
+
# === INPUTS
|
71
|
+
# url:: when true, return url, else, return physical file-system path
|
72
|
+
def public_photo_dir(url = true)
|
73
|
+
dir = File.join(conf['output_top'], PUBLIC_PHOTO_DIR_DEFAULT)
|
74
|
+
raise DirNameConflict if File.file?(dir)
|
75
|
+
|
76
|
+
if !File.directory?(dir)
|
77
|
+
FileUtils.mkdir(dir)
|
78
|
+
end
|
79
|
+
url ? PUBLIC_PHOTO_DIR_DEFAULT : dir
|
80
|
+
end
|
81
|
+
|
82
|
+
# simplify path to the non-directory name with size.
|
83
|
+
#
|
84
|
+
# === Example
|
85
|
+
# path:: a/b/c.jpg
|
86
|
+
# photo_name:: a_b_c_#{size}.jpg
|
87
|
+
def photo_name(path, size)
|
88
|
+
flat = path.gsub(/\//, '_')
|
89
|
+
sprintf("%s_%s%s",
|
90
|
+
File.basename(flat, '.*'), size, File.extname(flat))
|
91
|
+
end
|
92
|
+
|
93
|
+
# cached photo path
|
94
|
+
#
|
95
|
+
# === INPUTS
|
96
|
+
# path:: photo-macro path argument
|
97
|
+
# size:: :small, or :large
|
98
|
+
# url:: when true, return url, else, return physical file-system path
|
99
|
+
def photo_path(path, size, url = true)
|
100
|
+
File.join(public_photo_dir(url), photo_name(path, size))
|
101
|
+
end
|
102
|
+
|
103
|
+
# create resized, rotated, and 'exif' eliminated cache when:
|
104
|
+
# 1. not already created, or
|
105
|
+
# 1. cache is obsolete
|
106
|
+
#
|
107
|
+
# and return the path.
|
108
|
+
#
|
109
|
+
# === INPUTS
|
110
|
+
# path:: photo-macro path argument
|
111
|
+
# size:: :small, or :large
|
112
|
+
# url:: when true, return url, else, return physical file-system path
|
113
|
+
def intern(path, size = :small, url = true)
|
114
|
+
protected_path = File.join(conf_photo['mount'], path)
|
115
|
+
public_phys_path = photo_path(path, size, false)
|
116
|
+
if !File.exist?(public_phys_path) ||
|
117
|
+
File::Stat.new(public_phys_path).mtime < File::Stat.new(protected_path).mtime
|
118
|
+
|
119
|
+
img = Magick::ImageList.new(protected_path)
|
120
|
+
width = (s = conf_photo[size.to_s]) && s['width']
|
121
|
+
img.resize_to_fit!(width, img.rows * width / img.columns)
|
122
|
+
self.rotate(img).
|
123
|
+
strip!.
|
124
|
+
write(public_phys_path).destroy!
|
125
|
+
end
|
126
|
+
photo_path(path, size, url)
|
127
|
+
end
|
128
|
+
|
129
|
+
# return <img...> HTML tag for the photo with this macro features.
|
130
|
+
def run(*args)
|
131
|
+
path = args[0].gsub(/\.\./, '') # sanitize '..'
|
132
|
+
style = conf_photo['small']['style']
|
133
|
+
small_url = intern(path)
|
134
|
+
large_url = intern(path, :large)
|
135
|
+
content_tag(:a, :href=>large_url) do
|
136
|
+
tag(:img,
|
137
|
+
:src => intern(path),
|
138
|
+
:class => 'juli_photo_small',
|
139
|
+
:style => style)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def conf_photo
|
144
|
+
@conf_photo ||= conf['photo']
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
def set_conf_default_sub(hash, key, val)
|
149
|
+
case val
|
150
|
+
when Hash
|
151
|
+
hash[key] = {} if !hash[key]
|
152
|
+
for k, v in val do
|
153
|
+
set_conf_default_sub(hash[key], k, v)
|
154
|
+
end
|
155
|
+
else
|
156
|
+
hash[key] = val if !hash[key]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
require 'sdbm'
|
4
|
+
|
5
|
+
module Juli
|
6
|
+
module Macro
|
7
|
+
# Register tags of this document for tag-search.
|
8
|
+
#
|
9
|
+
# See 'doc/tag(macro).txt' for the detail how to use it.
|
10
|
+
# Here is the implementation document.
|
11
|
+
#
|
12
|
+
# === Tag-DB ER-chart
|
13
|
+
# tag <--->> tag_page <<---> page
|
14
|
+
#
|
15
|
+
# * tag DB value counts number of wikipages
|
16
|
+
#
|
17
|
+
# === FILES
|
18
|
+
# JURI_REPO/.juli/tag.sdbm:: tag DB
|
19
|
+
# JURI_REPO/.juli/page.sdbm:: page DB
|
20
|
+
# JURI_REPO/.juli/tag_page.sdbm:: tag-page intersection DB
|
21
|
+
class Tag < Base
|
22
|
+
SEPARATOR = '_, _'
|
23
|
+
NO_TAG = '_no_tag_'
|
24
|
+
|
25
|
+
attr_accessor :tag_db, :page_db, :tag_page_db
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
super
|
29
|
+
|
30
|
+
repo_dir = File.join(juli_repo, Juli::REPO)
|
31
|
+
@tag_db = SDBM.open(File.join(repo_dir, 'tag.sdbm'))
|
32
|
+
@page_db = SDBM.open(File.join(repo_dir, 'page.sdbm'))
|
33
|
+
@tag_page_db = SDBM.open(File.join(repo_dir, 'tag_page.sdbm'))
|
34
|
+
end
|
35
|
+
|
36
|
+
# register page
|
37
|
+
def on_root(file, root, visitor = nil)
|
38
|
+
@wikiname = Juli::Util::to_wikiname(file)
|
39
|
+
@page_db[@wikiname] = '1'
|
40
|
+
@tag_exists = false
|
41
|
+
end
|
42
|
+
|
43
|
+
def run(*args)
|
44
|
+
for tag in args do
|
45
|
+
@tag_exists = true
|
46
|
+
|
47
|
+
# +1 on tag
|
48
|
+
@tag_db[tag] = ((@tag_db[tag] || '0').to_i + 1).to_s
|
49
|
+
if @wikiname
|
50
|
+
@tag_page_db[tag_page_key(tag, @wikiname)] = '1'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
''
|
54
|
+
end
|
55
|
+
|
56
|
+
# follow-up process to register 'no-tag' if there is no tag in the
|
57
|
+
# file.
|
58
|
+
def after_root(file, root)
|
59
|
+
key = sprintf("%s%s%s", @wikiname, SEPARATOR, NO_TAG)
|
60
|
+
if @tag_exists
|
61
|
+
@tag_page_db.delete(key)
|
62
|
+
else
|
63
|
+
@tag_page_db[key] = '1'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# value in sdbm in ruby 1.9 looks not to support encoding
|
68
|
+
# (in other words, always set to ASCII-8BIT) so that
|
69
|
+
# enforce to set it to UTF-8.
|
70
|
+
def to_utf8(v)
|
71
|
+
v.force_encoding(Encoding::UTF_8)
|
72
|
+
end
|
73
|
+
|
74
|
+
# return pages associated with key
|
75
|
+
def pages(key)
|
76
|
+
result = []
|
77
|
+
for tag_page, val in @tag_page_db do
|
78
|
+
if to_utf8(tag_page) =~ /^(.*)#{SEPARATOR}#{key}$/
|
79
|
+
result << $1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
def max_tag_weight
|
86
|
+
@tag_db.values.map{|v| v.to_i}.max || 1
|
87
|
+
end
|
88
|
+
|
89
|
+
# return 0..10 in tag weight v.s. max-weight
|
90
|
+
def tag_weight_ratio(key)
|
91
|
+
v = (@tag_db[key] || '0').to_i
|
92
|
+
(v * 10 / max_tag_weight).to_i
|
93
|
+
end
|
94
|
+
|
95
|
+
# delete entry from DB
|
96
|
+
def delete_page(file)
|
97
|
+
wikiname = Juli::Util::to_wikiname(file)
|
98
|
+
@page_db.delete(wikiname)
|
99
|
+
|
100
|
+
tag_on_the_file = {}
|
101
|
+
for tag, val in @tag_db.keys do
|
102
|
+
if @tag_page_db[tag_page_key(tag, wikiname)]
|
103
|
+
tag_on_the_file[tag] = 1
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# -1 on tag
|
108
|
+
for tag in tag_on_the_file.keys do
|
109
|
+
@tag_db[tag] = ((@tag_db[tag] || '1').to_i - 1).to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# print DB info; debugging purpose. How to use:
|
114
|
+
#
|
115
|
+
# $ test/console
|
116
|
+
# > Dir.chdir('../test/repo')
|
117
|
+
# > include Juli::Util
|
118
|
+
# > t = Juli::Macro::Tag.new
|
119
|
+
# > t.dump
|
120
|
+
def dump
|
121
|
+
for db in %w(tag_db page_db tag_page_db) do
|
122
|
+
printf("%s\n", db)
|
123
|
+
for key, val in instance_variable_get('@' + db) do
|
124
|
+
printf(" %s\t%s\n", key, val)
|
125
|
+
end
|
126
|
+
print "\n"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def tag_page_key(tag, wikiname)
|
132
|
+
sprintf("%s%s%s", wikiname, SEPARATOR, tag)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
require 'sdbm'
|
4
|
+
|
5
|
+
module Juli
|
6
|
+
module Macro
|
7
|
+
# set ERB template.
|
8
|
+
#
|
9
|
+
# ERB template, which is used on generating HTML from juli-formatted text,
|
10
|
+
# can be specified by:
|
11
|
+
#
|
12
|
+
# 1. juli(1) command line -t option.
|
13
|
+
# 1. this macro
|
14
|
+
# 1. .juli/config template directive.
|
15
|
+
# 1. lib/juli/template
|
16
|
+
#
|
17
|
+
# See 'doc/template(macro).txt' for the detail how to use it.
|
18
|
+
# Here is the implementation document.
|
19
|
+
#
|
20
|
+
# NOTE: Template class is <b>totally different</b> from TemplateBase.
|
21
|
+
# Template is to specify ERB template, while TemplateBase is the
|
22
|
+
# base class to provide HTML flagment replacement in a juli document.
|
23
|
+
class Template < Base
|
24
|
+
# save visitor for later use at run()
|
25
|
+
def on_root(file, root, visitor = nil)
|
26
|
+
@visitor = visitor
|
27
|
+
end
|
28
|
+
|
29
|
+
def run(*args)
|
30
|
+
if @visitor.respond_to?('template=')
|
31
|
+
@visitor.template = args[0]
|
32
|
+
end
|
33
|
+
''
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Juli
|
2
|
+
module Macro
|
3
|
+
# Base class for HTML template related macros.
|
4
|
+
#
|
5
|
+
# Derived class can provide HTML template replacement with minimum
|
6
|
+
# implementation. Please see Wikipedia case as an example.
|
7
|
+
class TemplateBase < Base
|
8
|
+
DEFAULT_TEMPLATE = ''
|
9
|
+
|
10
|
+
def self.conf_template
|
11
|
+
''
|
12
|
+
end
|
13
|
+
|
14
|
+
# return key string used for conf-key
|
15
|
+
#
|
16
|
+
# Please overwrite this method if it is not just underscore-ed.
|
17
|
+
def conf_key
|
18
|
+
Juli::Util::underscore(self.class.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
# set default value in conf if no .juli/conf defined.
|
22
|
+
#
|
23
|
+
# Please overwrite this method when this implementation is not your
|
24
|
+
# case.
|
25
|
+
def set_conf_default(conf)
|
26
|
+
if !conf[conf_key]
|
27
|
+
conf[conf_key] = self.class::DEFAULT_TEMPLATE
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# return string used to be replaced with %{...} in conf[conf_key] string.
|
32
|
+
#
|
33
|
+
# Please overwrite this method if it is not just underscore-ed.
|
34
|
+
def place_holder
|
35
|
+
conf_key
|
36
|
+
end
|
37
|
+
|
38
|
+
def run(*args)
|
39
|
+
template = conf[conf_key]
|
40
|
+
template.gsub("%{#{place_holder}}", args[0])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|