juli 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/CODE_OF_CONDUCT.md +13 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.rdoc +39 -0
  7. data/Rakefile +89 -0
  8. data/bin/console +14 -0
  9. data/bin/je +73 -0
  10. data/bin/juli +82 -0
  11. data/bin/juli_tb.rb +76 -0
  12. data/bin/setup +7 -0
  13. data/juli.gemspec +29 -0
  14. data/lib/juli.rb +21 -0
  15. data/lib/juli/absyn.rb +206 -0
  16. data/lib/juli/command.rb +180 -0
  17. data/lib/juli/command/file_entry.rb +12 -0
  18. data/lib/juli/command/recent_update.rb +52 -0
  19. data/lib/juli/command/sitemap.rb +55 -0
  20. data/lib/juli/command/tag.rb +81 -0
  21. data/lib/juli/line_parser.y +212 -0
  22. data/lib/juli/macro.rb +39 -0
  23. data/lib/juli/macro/amazon.rb +33 -0
  24. data/lib/juli/macro/jmap.rb +38 -0
  25. data/lib/juli/macro/photo.rb +161 -0
  26. data/lib/juli/macro/tag.rb +136 -0
  27. data/lib/juli/macro/template.rb +37 -0
  28. data/lib/juli/macro/template_base.rb +44 -0
  29. data/lib/juli/macro/wikipedia.rb +19 -0
  30. data/lib/juli/parser.y +360 -0
  31. data/lib/juli/template/default.html +64 -0
  32. data/lib/juli/template/facebook.html +82 -0
  33. data/lib/juli/template/je-bash-complete +42 -0
  34. data/lib/juli/template/juli.css +173 -0
  35. data/lib/juli/template/juli.js +87 -0
  36. data/lib/juli/template/locale/en.yml +10 -0
  37. data/lib/juli/template/locale/ja.yml +10 -0
  38. data/lib/juli/template/prototype.js +4320 -0
  39. data/lib/juli/template/simple.html +45 -0
  40. data/lib/juli/template/sitemap.html +78 -0
  41. data/lib/juli/template/sitemap_order_by_mtime_DESC.html +78 -0
  42. data/lib/juli/template/slidy.html +126 -0
  43. data/lib/juli/template/sourceforge.html +71 -0
  44. data/lib/juli/template/takahashi_method.html +116 -0
  45. data/lib/juli/util.rb +255 -0
  46. data/lib/juli/util/juli_i18n.rb +32 -0
  47. data/lib/juli/version.rb +3 -0
  48. data/lib/juli/visitor.rb +12 -0
  49. data/lib/juli/visitor/html.rb +462 -0
  50. data/lib/juli/visitor/html/helper.rb +97 -0
  51. data/lib/juli/visitor/html/helper/contents.rb +76 -0
  52. data/lib/juli/visitor/html/helper/fb_comments.rb +68 -0
  53. data/lib/juli/visitor/html/helper/fb_like.rb +37 -0
  54. data/lib/juli/visitor/html/tag_helper.rb +40 -0
  55. data/lib/juli/visitor/slidy.rb +39 -0
  56. data/lib/juli/visitor/takahashi_method.rb +41 -0
  57. data/lib/juli/visitor/tree.rb +135 -0
  58. data/lib/juli/wiki.rb +52 -0
  59. data/sample/protected_photo/2012-04-22/DCIM/101_PANA/P1010441.JPG +0 -0
  60. data/sample/update_public_juli.rb +71 -0
  61. data/setup.rb +1585 -0
  62. 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
@@ -0,0 +1,3 @@
1
+ module Juli
2
+ VERSION = "2.0.0"
3
+ end
@@ -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