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.
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