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
data/lib/juli/util.rb
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'singleton'
|
3
|
+
require 'yaml'
|
4
|
+
require 'juli'
|
5
|
+
|
6
|
+
module Juli
|
7
|
+
module Util
|
8
|
+
def camelize(str)
|
9
|
+
str.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Visitor::#{str} constantize
|
13
|
+
def visitor(str)
|
14
|
+
camelized = camelize(str)
|
15
|
+
if Visitor.const_defined?(camelized)
|
16
|
+
Visitor.const_get(camelize(str))
|
17
|
+
else
|
18
|
+
raise "Visitor #{camelized} is not defined."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
module_function :visitor
|
22
|
+
|
23
|
+
def visitor_list
|
24
|
+
result = []
|
25
|
+
sorted_visitors = Dir.glob(File.join(Juli::LIB, 'visitor/*.rb')).sort
|
26
|
+
for f in sorted_visitors do
|
27
|
+
next if f =~ /^\./
|
28
|
+
result << File.basename(f).gsub(/\.rb$/, '')
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
module_function :visitor_list
|
33
|
+
|
34
|
+
def usage
|
35
|
+
<<EOM
|
36
|
+
USAGE: juli [general_options] COMMAND [command_options] [files]
|
37
|
+
|
38
|
+
general_options:
|
39
|
+
--help
|
40
|
+
--version
|
41
|
+
|
42
|
+
COMMAND (default = gen):
|
43
|
+
init initialize current directory as juli-repo
|
44
|
+
gen generate outputs from files under juli-repo
|
45
|
+
This is the default juli command.
|
46
|
+
sitemap generate sitemap to JULI_REPO/sitemap.shtml
|
47
|
+
recent_update generate reent updates to JULI_REPO/recent_update.shtml
|
48
|
+
|
49
|
+
NOTE: file extention '.shtml' above is the default.
|
50
|
+
you can change it by 'init' command -e option
|
51
|
+
(see below), or by modifying JULI_REPO/.juli/config
|
52
|
+
'ext' entry later anytime.
|
53
|
+
|
54
|
+
tag generate tag-list page. see tag(macro) manual.
|
55
|
+
|
56
|
+
command_options for:
|
57
|
+
init:
|
58
|
+
-o output_top default='../html/'
|
59
|
+
-t template set the template at config (default='default.html').
|
60
|
+
This template name will be used at 'gen' command
|
61
|
+
(described below) to search 1) JULI_REPO/.juli/ or
|
62
|
+
2) lib/juli/template/
|
63
|
+
-e ext generating html file extention (default='.shtml')
|
64
|
+
|
65
|
+
gen:
|
66
|
+
-g generator specify generator as follows(default=html):
|
67
|
+
#{visitor_list.join("\n" + " "*25)}
|
68
|
+
-f force generate
|
69
|
+
-t template_path use the template path rather than juli-config value
|
70
|
+
set at 'juli init -t ...'
|
71
|
+
-o output_path specify output file path. It cannot be set at bulk-mode.
|
72
|
+
default is under the directory defined at .juli/config
|
73
|
+
'output_top' entry.
|
74
|
+
|
75
|
+
Where, JULI_REPO is the directory which 'juli init' is executed.
|
76
|
+
EOM
|
77
|
+
end
|
78
|
+
module_function :usage
|
79
|
+
|
80
|
+
def str_limit(str)
|
81
|
+
str.size > 45 ? str[0..45] + '...' : str
|
82
|
+
end
|
83
|
+
module_function :str_limit
|
84
|
+
|
85
|
+
# trim string just for printing purpose here
|
86
|
+
def str_trim(str)
|
87
|
+
str_limit(str.gsub(/\n/, '\n').gsub(/\r/, '\r'))
|
88
|
+
end
|
89
|
+
module_function :str_trim
|
90
|
+
|
91
|
+
# Similar to Rails underscore() method.
|
92
|
+
#
|
93
|
+
# Example: 'A::B::HelperMethod' -> 'helper_method'
|
94
|
+
def underscore(str)
|
95
|
+
str.gsub(/.*::/,'').
|
96
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
97
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
98
|
+
tr("-", "_").
|
99
|
+
downcase
|
100
|
+
end
|
101
|
+
module_function :underscore
|
102
|
+
|
103
|
+
# find juli-repository root from the specified path.
|
104
|
+
class Repo
|
105
|
+
attr_reader :juli_repo
|
106
|
+
|
107
|
+
def initialize(path = '.')
|
108
|
+
Pathname.new(path).realpath.ascend do |p|
|
109
|
+
p_str = File.join(p, Juli::REPO)
|
110
|
+
if File.directory?(p_str)
|
111
|
+
@juli_repo = p
|
112
|
+
return
|
113
|
+
end
|
114
|
+
end
|
115
|
+
raise "cannot find juli repository root."
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# fullpath of juli-repository
|
120
|
+
#
|
121
|
+
# it is enough to have one value in whole juli modules so
|
122
|
+
# SINGLETON-pattern is used.
|
123
|
+
def juli_repo(path='.')
|
124
|
+
$_repo ||= Repo.new(path)
|
125
|
+
$_repo.juli_repo
|
126
|
+
end
|
127
|
+
module_function :juli_repo
|
128
|
+
|
129
|
+
# config with hard-coded default
|
130
|
+
class Config
|
131
|
+
DEFAULT = {
|
132
|
+
'ext' => '.shtml',
|
133
|
+
'output_top' => '../html',
|
134
|
+
'show_indent_toggle_button' => true,
|
135
|
+
'template' => 'default.html',
|
136
|
+
}
|
137
|
+
|
138
|
+
include Singleton
|
139
|
+
class Error < Exception; end
|
140
|
+
attr_reader :conf
|
141
|
+
|
142
|
+
def initialize
|
143
|
+
path = File.join(Juli::Util::juli_repo, Juli::REPO, 'config')
|
144
|
+
hash = File.exist?(path) ?
|
145
|
+
YAML::load(ERB.new(File.read(path)).result) :
|
146
|
+
{}
|
147
|
+
|
148
|
+
# YAML::load('') returns false so that set empty hash
|
149
|
+
hash = {} if hash == false
|
150
|
+
@conf = DEFAULT.dup.merge(hash)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# return REPO/config hash
|
155
|
+
def conf
|
156
|
+
Config.instance.conf
|
157
|
+
end
|
158
|
+
module_function :conf
|
159
|
+
|
160
|
+
# mkdir for out_file if necessary
|
161
|
+
def mkdir(path)
|
162
|
+
dir = File.dirname(path)
|
163
|
+
if !File.directory?(dir)
|
164
|
+
FileUtils.mkdir_p(dir)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
module_function :mkdir
|
168
|
+
|
169
|
+
# input filename to wikiname
|
170
|
+
#
|
171
|
+
# === INPUTS
|
172
|
+
# in_filename:: juli repo file path
|
173
|
+
#
|
174
|
+
# === EXAMPLE
|
175
|
+
# diary/2010/12/31.txt -> diary/2010/12/31
|
176
|
+
def to_wikiname(in_filename)
|
177
|
+
in_filename.gsub(/\.[^\.]*$/,'')
|
178
|
+
end
|
179
|
+
module_function :to_wikiname
|
180
|
+
|
181
|
+
# === INPUTS
|
182
|
+
# in_filename:: relative path under repository
|
183
|
+
# o_opt:: output path which -o command-line option specifies
|
184
|
+
#
|
185
|
+
# === RETURN
|
186
|
+
# full path of out filename. if o_opt is specified,
|
187
|
+
# it is used.
|
188
|
+
#
|
189
|
+
# === EXAMPLE
|
190
|
+
# diary/2010/12/31.txt -> OUTPUT_TOP/diary/2010/12/31.shtml
|
191
|
+
#
|
192
|
+
def out_filename(in_filename, o_opt = nil)
|
193
|
+
o_opt ||
|
194
|
+
File.join(conf['output_top'],
|
195
|
+
to_wikiname(in_filename) + conf['ext'])
|
196
|
+
end
|
197
|
+
module_function :out_filename
|
198
|
+
|
199
|
+
# === INPUTS
|
200
|
+
# out_filename:: relative path under OUTPUT_TOP
|
201
|
+
#
|
202
|
+
# === RETURN
|
203
|
+
# relative path of in-filename, but **no extention**.
|
204
|
+
#
|
205
|
+
# === EXAMPLE
|
206
|
+
# diary/2010/12/31.shtml -> 31
|
207
|
+
def in_filename(out_filename)
|
208
|
+
File.join(File.dirname(out_filename),
|
209
|
+
File.basename(out_filename).gsub(/\.[^\.]*$/,''))
|
210
|
+
end
|
211
|
+
module_function :in_filename
|
212
|
+
|
213
|
+
# find erb template under the following order:
|
214
|
+
#
|
215
|
+
# if t_opt ('-t' command-line option arg) is specified:
|
216
|
+
# 1st) template_path in absolute or relative from current dir, or
|
217
|
+
# 2nd) -t template_path in JULI_REPO/.juli/, or
|
218
|
+
# 3rd) -t template_path in lib/juli/template/
|
219
|
+
# otherwise, error
|
220
|
+
# else:
|
221
|
+
# 4th) {template} in JULI_REPO/.juli/, or
|
222
|
+
# 5th) {template} in lib/juli/template.
|
223
|
+
# otherwise, error
|
224
|
+
#
|
225
|
+
# Where, {template} means conf['template']
|
226
|
+
#
|
227
|
+
# === INPUTS
|
228
|
+
# template:: template name
|
229
|
+
# t_opt:: template name which -t command-line option specifies
|
230
|
+
def find_template(template, t_opt = nil)
|
231
|
+
if t_opt
|
232
|
+
if File.exist?(t_opt)
|
233
|
+
t_opt
|
234
|
+
else
|
235
|
+
find_template_sub(t_opt)
|
236
|
+
end
|
237
|
+
else
|
238
|
+
find_template_sub(template)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
module_function :find_template
|
242
|
+
|
243
|
+
private
|
244
|
+
# find template 't' in dirs
|
245
|
+
def find_template_sub(t)
|
246
|
+
for path in [File.join(juli_repo, Juli::REPO), Juli::TEMPLATE_PATH] do
|
247
|
+
template = File.join(path, t)
|
248
|
+
return template if File.exist?(template)
|
249
|
+
end
|
250
|
+
raise Errno::ENOENT, "no #{t} found"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
require 'juli/util/juli_i18n'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
module Juli
|
4
|
+
module Util
|
5
|
+
# Locale is determined by 'locale' entry in .juli/config (default = en).
|
6
|
+
#
|
7
|
+
# Translation file is used at the following priority:
|
8
|
+
#
|
9
|
+
# 1. .juli/LC.yml
|
10
|
+
# 1. LIB/juli/template/locale/LC.yml
|
11
|
+
#
|
12
|
+
# Where, LC is the value of 'locale' config (default = en), and
|
13
|
+
# LIB is ruby library directory (e.g. /usr/lib/lib/ruby/site_ruby/1.9.1/).
|
14
|
+
class JuliI18n
|
15
|
+
def initialize(conf, juli_repo)
|
16
|
+
I18n.locale = conf['locale'] || :en
|
17
|
+
|
18
|
+
for candidate_dir in [
|
19
|
+
File.join(juli_repo, Juli::REPO),
|
20
|
+
File.join(Juli::TEMPLATE_PATH, 'locale')
|
21
|
+
] do
|
22
|
+
locale_yml = File.join(candidate_dir, "#{I18n.locale}.yml")
|
23
|
+
if File.exist?(locale_yml)
|
24
|
+
I18n.load_path = [locale_yml]
|
25
|
+
return
|
26
|
+
end
|
27
|
+
end
|
28
|
+
raise Errno::ENOENT, "no #{I18n.locale}.yml found"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/juli/version.rb
ADDED
data/lib/juli/visitor.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# import all of visitor/*.rb files
|
2
|
+
|
3
|
+
module Juli
|
4
|
+
# Namespace for visitors.
|
5
|
+
module Visitor
|
6
|
+
# since slidy depends on html, order of 'require' is important
|
7
|
+
require 'juli/visitor/html'
|
8
|
+
require 'juli/visitor/slidy'
|
9
|
+
require 'juli/visitor/takahashi_method'
|
10
|
+
require 'juli/visitor/tree'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,462 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
require 'juli/util'
|
4
|
+
require 'juli/line_parser.tab'
|
5
|
+
require 'juli/absyn'
|
6
|
+
|
7
|
+
module Juli::Visitor
|
8
|
+
# This visits Absyn tree and generates HTML
|
9
|
+
#
|
10
|
+
# Text files under juli-repository must have '.txt' extention.
|
11
|
+
#
|
12
|
+
# === OPTIONS
|
13
|
+
# -f:: force update
|
14
|
+
# -t template:: specify template
|
15
|
+
class Html < Juli::Absyn::Visitor
|
16
|
+
require 'juli/visitor/html/tag_helper'
|
17
|
+
require 'juli/visitor/html/helper'
|
18
|
+
require 'juli/macro'
|
19
|
+
|
20
|
+
include Juli::Util
|
21
|
+
include Juli::Visitor::Html::TagHelper
|
22
|
+
include Juli::Visitor::Html::Helper
|
23
|
+
|
24
|
+
# assign DOM id on header node.
|
25
|
+
#
|
26
|
+
# IdAssigner should be executed before running Html visitor since
|
27
|
+
# ContentsDrawer also refers DOM id. That is the reason why DOM id
|
28
|
+
# assignment is isolated from Html visitor.
|
29
|
+
class IdAssigner < Juli::Absyn::Visitor
|
30
|
+
def initialize(opts={})
|
31
|
+
super
|
32
|
+
@uniq_id_seed = 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_chapter(n)
|
36
|
+
n.dom_id = uniq_id(n.level)
|
37
|
+
n.blocks.accept(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(in_file, root)
|
41
|
+
root.accept(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
# generate uniq_id, and track it for each level to be used later
|
46
|
+
def uniq_id(level)
|
47
|
+
@uniq_id_seed += 1
|
48
|
+
result = sprintf("j%05d", @uniq_id_seed)
|
49
|
+
result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# visits a line of document text and generate:
|
54
|
+
#
|
55
|
+
# * hyperlink on wikiname.
|
56
|
+
# * hyperlink on url like http://...
|
57
|
+
# * macro result
|
58
|
+
class HtmlLine < Juli::LineAbsyn::Visitor
|
59
|
+
include Juli::Util
|
60
|
+
include TagHelper
|
61
|
+
|
62
|
+
def initialize(macros)
|
63
|
+
@_macros = macros
|
64
|
+
end
|
65
|
+
|
66
|
+
def visit_array(n)
|
67
|
+
n.array.inject('') do |result, n|
|
68
|
+
result += n.accept(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_string(n)
|
73
|
+
n.str
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_wikiname(n)
|
77
|
+
decoded = Juli::Wiki.decode(n.str)
|
78
|
+
content_tag(:a, decoded, :class=>'wiki', :href=>decoded + conf['ext'])
|
79
|
+
end
|
80
|
+
|
81
|
+
def visit_url(n)
|
82
|
+
content_tag(:a, n.str, :class=>'url', :href=>n.str)
|
83
|
+
end
|
84
|
+
|
85
|
+
def visit_macro(n)
|
86
|
+
if macro = @_macros[camelize(n.name).to_sym]
|
87
|
+
macro.run(*n.rest.split(' '))
|
88
|
+
else
|
89
|
+
s = "juli ERR: UNKNOWN macro name: '#{n.name}'"
|
90
|
+
STDERR.print s, "\n"
|
91
|
+
s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Html sepecific initialization does:
|
97
|
+
#
|
98
|
+
# 1. create output_top.
|
99
|
+
# 1. copy *.js, *.css files to output_top/
|
100
|
+
#
|
101
|
+
# NOTE: this is executed every juli(1) run with -g html option
|
102
|
+
# (usually 99% is so), so be careful to avoid multiple initialization.
|
103
|
+
def initialize(opts={})
|
104
|
+
super
|
105
|
+
|
106
|
+
# files here will not be deleted even if corresponding *.txt file
|
107
|
+
# doesn't exist.
|
108
|
+
@exception = {
|
109
|
+
'sitemap' + conf['ext'] => 1,
|
110
|
+
'recent_update' + conf['ext'] => 1,
|
111
|
+
}
|
112
|
+
|
113
|
+
register_helper
|
114
|
+
register_macro
|
115
|
+
@html_line_visitor = HtmlLine.new(@_macros)
|
116
|
+
|
117
|
+
if !File.directory?(conf['output_top'])
|
118
|
+
FileUtils.mkdir_p(conf['output_top'])
|
119
|
+
end
|
120
|
+
copy_to_output_top('prototype.js')
|
121
|
+
copy_to_output_top('juli.js')
|
122
|
+
copy_to_output_top('juli.css')
|
123
|
+
end
|
124
|
+
|
125
|
+
# run in bulk-mode. In Html visitor, it sync juli-repository and
|
126
|
+
# OUTPUT_TOP.
|
127
|
+
def run_bulk
|
128
|
+
sync
|
129
|
+
end
|
130
|
+
|
131
|
+
# visit root to generate:
|
132
|
+
#
|
133
|
+
# 1st:: HTML body
|
134
|
+
# 2nd:: whole HTML by ERB.
|
135
|
+
def run_file(in_file, root)
|
136
|
+
@header_sequence = HeaderSequence.new
|
137
|
+
IdAssigner.new.run(in_file, root)
|
138
|
+
|
139
|
+
for key, helper in @_helpers do
|
140
|
+
helper.on_root(in_file, root, self)
|
141
|
+
end
|
142
|
+
for macro_symbol, macro in @_macros do
|
143
|
+
macro.on_root(in_file, root, self)
|
144
|
+
end
|
145
|
+
|
146
|
+
# store to instance var for 'contents' helper
|
147
|
+
@root = root
|
148
|
+
title = File.basename(in_file.gsub(/\.[^.]*$/, ''))
|
149
|
+
prototype = relative_from(in_file, 'prototype.js')
|
150
|
+
javascript = relative_from(in_file, 'juli.js')
|
151
|
+
stylesheet = relative_from(in_file, 'juli.css')
|
152
|
+
sitemap = relative_from(in_file, 'sitemap' + conf['ext'])
|
153
|
+
body = root.accept(self)
|
154
|
+
|
155
|
+
for macro_symbol, macro in @_macros do
|
156
|
+
macro.after_root(in_file, root)
|
157
|
+
end
|
158
|
+
|
159
|
+
erb = ERB.new(File.read(template))
|
160
|
+
out_path = out_filename(in_file, @opts[:o])
|
161
|
+
mkdir(out_path)
|
162
|
+
File.open(out_path, 'w') do |f|
|
163
|
+
f.write(erb.result(binding))
|
164
|
+
end
|
165
|
+
printf("generated: %s\n", out_path)
|
166
|
+
end
|
167
|
+
|
168
|
+
# if str is in list, don't enclose by <p>
|
169
|
+
def visit_str(n)
|
170
|
+
if n.parent && n.parent.parent &&
|
171
|
+
n.parent.parent.is_a?(Juli::Absyn::List)
|
172
|
+
str2html(n.str)
|
173
|
+
else
|
174
|
+
content_tag(:p, paragraph_css) do
|
175
|
+
str2html(n.str)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def visit_verbatim(n)
|
181
|
+
# quote; trim last white spaces at generating phase
|
182
|
+
content_tag(:blockquote,
|
183
|
+
content_tag(:pre, n.str.gsub(/\s+\z/m, '')),
|
184
|
+
blockquote_css)
|
185
|
+
end
|
186
|
+
|
187
|
+
def visit_array(n)
|
188
|
+
n.array.inject(''){|result, child|
|
189
|
+
result += child.accept(self)
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
def visit_chapter(n)
|
194
|
+
header_link(n) +
|
195
|
+
content_tag(:div, :id=>n.dom_id) do
|
196
|
+
n.blocks.accept(self)
|
197
|
+
end + "\n"
|
198
|
+
end
|
199
|
+
|
200
|
+
def visit_ordered_list(n)
|
201
|
+
visit_list(:ol, n)
|
202
|
+
end
|
203
|
+
|
204
|
+
def visit_unordered_list(n)
|
205
|
+
visit_list(:ul, n)
|
206
|
+
end
|
207
|
+
|
208
|
+
def visit_compact_dictionary_list(n)
|
209
|
+
content_tag(:table, :class=>'juli_compact_dictionary') do
|
210
|
+
n.array.inject('') do |result, child|
|
211
|
+
result += child.accept(self)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def visit_compact_dictionary_list_item(n)
|
217
|
+
content_tag(:tr) do
|
218
|
+
content_tag(:td, str2html(n.term) + ':', :nowrap=>true) +
|
219
|
+
content_tag(:td, str2html(n.str))
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def visit_dictionary_list(n)
|
224
|
+
content_tag(:dl, :class=>'juli_dictionary') do
|
225
|
+
n.array.inject('') do |result, child|
|
226
|
+
result += child.accept(self)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def visit_dictionary_list_item(n)
|
232
|
+
content_tag(:dt, str2html(n.term), dt_css) +
|
233
|
+
content_tag(:dd, str2html(n.str), dd_css)
|
234
|
+
end
|
235
|
+
|
236
|
+
def template=(name)
|
237
|
+
@template = name
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
# Similar to Rails underscore() method.
|
242
|
+
#
|
243
|
+
# Example: 'A::B::HelperMethod' -> 'helper_method'
|
244
|
+
def self.to_method(helper_class)
|
245
|
+
Juli::Util::underscore(helper_class.to_s)
|
246
|
+
end
|
247
|
+
|
248
|
+
def copy_to_output_top(file)
|
249
|
+
src = File.join(Juli::TEMPLATE_PATH, file)
|
250
|
+
dest = File.join(conf['output_top'], file)
|
251
|
+
return if File.exist?(dest) && File.stat(dest).mtime >= File.stat(src).mtime
|
252
|
+
|
253
|
+
FileUtils.cp(src, dest, :preserve=>true)
|
254
|
+
end
|
255
|
+
|
256
|
+
# register each XHelper instance in @_helpers hash.
|
257
|
+
def register_helper
|
258
|
+
@_helpers = {}
|
259
|
+
for helper_symbol in Juli::Visitor::Html::Helper.constants do
|
260
|
+
next if helper_symbol == :AbstractHelper
|
261
|
+
helper_class = Juli::Visitor::Html.module_eval(helper_symbol.to_s)
|
262
|
+
helper = helper_class.new
|
263
|
+
helper.set_conf_default(conf)
|
264
|
+
@_helpers[helper_symbol.to_sym] = helper
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# define helper method 'x' from XHelper class to call
|
269
|
+
# @_helpers[:x].run(*args)
|
270
|
+
for helper_symbol in Juli::Visitor::Html::Helper.constants do
|
271
|
+
next if helper_symbol == :AbstractHelper
|
272
|
+
class_eval <<-end_of_dynamic_method, __FILE__, __LINE__ + 1
|
273
|
+
def #{to_method(helper_symbol)}(*args)
|
274
|
+
@_helpers[:#{helper_symbol}.to_sym].run(*args)
|
275
|
+
end
|
276
|
+
end_of_dynamic_method
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
# create Macro object and register it in @_macros hash.
|
281
|
+
#
|
282
|
+
# call set_conf_default() to set conf[key] default value for each macro
|
283
|
+
def register_macro
|
284
|
+
@_macros = {}
|
285
|
+
for macro_symbol in Juli::Macro.constants do
|
286
|
+
next if macro_symbol == :Base
|
287
|
+
macro_class = Juli::Macro.module_eval(macro_symbol.to_s)
|
288
|
+
macro = macro_class.new
|
289
|
+
macro.set_conf_default(conf)
|
290
|
+
@_macros[macro_symbol] = macro
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# synchronize repository and OUTPUT_TOP.
|
295
|
+
#
|
296
|
+
# * if text file, calls sync_txt()
|
297
|
+
# * for others, do rsync(1)
|
298
|
+
#
|
299
|
+
def sync
|
300
|
+
sync_txt
|
301
|
+
sync_others
|
302
|
+
end
|
303
|
+
|
304
|
+
def sync_others
|
305
|
+
Dir.chdir(juli_repo){
|
306
|
+
system 'rsync', '-avuzb',
|
307
|
+
'--exclude', '*.txt',
|
308
|
+
'--exclude', 'html/',
|
309
|
+
'--exclude', '*~',
|
310
|
+
'--exclude', '.juli/',
|
311
|
+
'--exclude', '.git*',
|
312
|
+
'.', conf['output_top']
|
313
|
+
}
|
314
|
+
end
|
315
|
+
|
316
|
+
# synchronize text file between juli-repo and OUTPUT_TOP:
|
317
|
+
#
|
318
|
+
# 1. new file exists, generate it.
|
319
|
+
# 1. repo's file timestamp is newer than the one under OUTPUT_TOP, regenerate it.
|
320
|
+
# 1. correspondent file of OUTPUT_TOP/.../f doesn't exist in repo
|
321
|
+
# and not in @exception list above, delete it.
|
322
|
+
# 1. if -f option is specified, don't check timestamp and always generates.
|
323
|
+
#
|
324
|
+
def sync_txt
|
325
|
+
repo = {}
|
326
|
+
Dir.chdir(juli_repo){
|
327
|
+
Dir.glob('**/*.txt'){|f|
|
328
|
+
repo[f] = 1
|
329
|
+
}
|
330
|
+
}
|
331
|
+
|
332
|
+
# When new file exists, generate it.
|
333
|
+
# When repo's file timestamp is newer than OUTPUT_TOP, regenerate it.
|
334
|
+
for f,v in repo do
|
335
|
+
out_file = out_filename(f, @opts[:o])
|
336
|
+
if !@opts[:f] &&
|
337
|
+
File.exist?(out_file) &&
|
338
|
+
File.stat(out_file).mtime >= File.stat(File.join(juli_repo,f)).mtime
|
339
|
+
#printf("already updated: %s\n", out_file)
|
340
|
+
else
|
341
|
+
Juli::Parser.new.parse(f, self)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# When correspondent file of OUTPUT_TOP/.../f doesn't exist in repo,
|
346
|
+
# and not in @exception list above, delete it.
|
347
|
+
Dir.chdir(conf['output_top']){
|
348
|
+
Dir.glob('**/*' + conf['ext']){|f|
|
349
|
+
next if @exception[f]
|
350
|
+
in_file = File.join(juli_repo, in_filename(f))
|
351
|
+
if !File.exist?(in_file) && !File.exist?(in_file + '.txt')
|
352
|
+
FileUtils.rm(f)
|
353
|
+
printf("%s is deleted since no correspondent source text.\n", f)
|
354
|
+
end
|
355
|
+
}
|
356
|
+
}
|
357
|
+
end
|
358
|
+
|
359
|
+
# draw <hi>... link, where i=2..7
|
360
|
+
#
|
361
|
+
# When conf['show_indent_toggle_button'] is true(=default),
|
362
|
+
# toggle-button to show/hide indent is drown.
|
363
|
+
#
|
364
|
+
# NOTE: <h1> is reserved for title. <h2>, <h3>, ... are used for Juli
|
365
|
+
# formatting '=', '==', ...
|
366
|
+
def header_link(n)
|
367
|
+
id = n.dom_id
|
368
|
+
content_tag("h#{n.level + 1}", :id=>header_id(n)) do
|
369
|
+
content_tag(:span, :class=>'juli_toggle', :onclick=>"Juli.toggle('#{id}');") do
|
370
|
+
if conf['show_indent_toggle_button']
|
371
|
+
content_tag(:span, '[+] ', :id=>"#{id}_p", :class=>'juli_toggle_node', :style=>'display:none;') +
|
372
|
+
content_tag(:span, '[-] ', :id=>"#{id}_m", :class=>'juli_toggle_node')
|
373
|
+
else
|
374
|
+
''
|
375
|
+
end +
|
376
|
+
@header_sequence.gen(n.level) + '. ' + n.str
|
377
|
+
end
|
378
|
+
end + "\n"
|
379
|
+
end
|
380
|
+
|
381
|
+
def visit_list(tag, n, options={})
|
382
|
+
content_tag(tag, options) do
|
383
|
+
n.array.inject('') do |result, child|
|
384
|
+
result += content_tag(:li, list_item_css) do
|
385
|
+
child.accept(self)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# 1. parse str and build Juli::LineAbsyn tree
|
392
|
+
# 1. visit the tree by HtmlLine and generate HTML
|
393
|
+
def str2html(str)
|
394
|
+
Juli::LineParser.new.parse(
|
395
|
+
str,
|
396
|
+
Juli::Wiki.wikinames).accept(@html_line_visitor)
|
397
|
+
end
|
398
|
+
|
399
|
+
def paragraph_css
|
400
|
+
{:class=>'default'}
|
401
|
+
end
|
402
|
+
|
403
|
+
def blockquote_css
|
404
|
+
{}
|
405
|
+
end
|
406
|
+
|
407
|
+
def list_item_css
|
408
|
+
{}
|
409
|
+
end
|
410
|
+
|
411
|
+
# default dictionary list item term part CSS
|
412
|
+
def dt_css
|
413
|
+
{}
|
414
|
+
end
|
415
|
+
|
416
|
+
# default dictionary list item description part CSS
|
417
|
+
def dd_css
|
418
|
+
{}
|
419
|
+
end
|
420
|
+
|
421
|
+
# return specified template.
|
422
|
+
#
|
423
|
+
# 1. If 'template' macro is used, it is used rather than defined at
|
424
|
+
# conf file.
|
425
|
+
# 1. Then, find_template() algorithm is used.
|
426
|
+
#
|
427
|
+
# Total template search logic is described in template(macro) document.
|
428
|
+
def template
|
429
|
+
find_template(@template || conf['template'], @opts[:t])
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# generate '1', '1.1', '1.2', ..., '2', '2.1', ...
|
434
|
+
#
|
435
|
+
# NOTE: When HeaderSequence was located before Html, rdoc generated
|
436
|
+
# wrong document (as Juli::Visitor::HeaderSequence::Html rather than
|
437
|
+
# Juli::Visitor::Html) so HeaderSequence is defined here.
|
438
|
+
class HeaderSequence
|
439
|
+
def initialize
|
440
|
+
@header_number = Array.new(6)
|
441
|
+
@curr_level = 0
|
442
|
+
end
|
443
|
+
|
444
|
+
def reset(level)
|
445
|
+
for i in (level+1)...@header_number.size do
|
446
|
+
@header_number[i] = 0
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
def gen(level)
|
451
|
+
reset(level) if level < @curr_level
|
452
|
+
@header_number[level] = 0 if !@header_number[level]
|
453
|
+
@header_number[level] += 1
|
454
|
+
@curr_level = level
|
455
|
+
h = []
|
456
|
+
for i in 1..(level) do
|
457
|
+
h << @header_number[i].to_s
|
458
|
+
end
|
459
|
+
h.join('.')
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|