pseudohikiparser 0.0.4 → 0.0.5.develop
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 +4 -4
- data/README.ja.md +13 -11
- data/README.md +14 -12
- data/bin/{pseudohiki2html.rb → pseudohiki2html} +1 -1
- data/lib/htmlelement/utils.rb +132 -0
- data/lib/htmlelement.rb +1 -1
- data/lib/pseudohiki/blockparser.rb +115 -15
- data/lib/pseudohiki/converter.rb +204 -135
- data/lib/pseudohiki/htmlformat.rb +65 -0
- data/lib/pseudohiki/markdownformat.rb +8 -1
- data/lib/pseudohiki/sinatra_helpers.rb +23 -0
- data/lib/pseudohiki/utils.rb +34 -0
- data/lib/pseudohiki/version.rb +1 -1
- data/lib/pseudohikiparser.rb +2 -0
- data/test/test_blockparser.rb +67 -0
- data/test/test_data/css/test.css +3 -0
- data/test/test_htmlelement_utils.rb +200 -0
- data/test/test_htmlformat.rb +419 -0
- data/test/test_markdownformat.rb +153 -0
- data/test/test_plaintextformat.rb +85 -0
- data/test/test_pseudohiki2html.rb +111 -26
- data/test/test_utils.rb +22 -0
- metadata +14 -10
- data/test/test_latexformat.rb +0 -19
data/lib/pseudohiki/converter.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'erb'
|
6
|
+
require 'pseudohiki/version'
|
6
7
|
require 'pseudohiki/blockparser'
|
7
8
|
require 'pseudohiki/autolink'
|
8
9
|
require 'pseudohiki/htmlformat'
|
@@ -11,6 +12,7 @@ require 'pseudohiki/markdownformat'
|
|
11
12
|
require 'pseudohiki/utils'
|
12
13
|
require 'htmlelement/htmltemplate'
|
13
14
|
require 'htmlelement'
|
15
|
+
require 'htmlelement/utils'
|
14
16
|
|
15
17
|
module PseudoHiki
|
16
18
|
class PageComposer
|
@@ -18,142 +20,189 @@ module PseudoHiki
|
|
18
20
|
|
19
21
|
PlainFormat = PlainTextFormat.create
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
class BaseComposer
|
24
|
+
def initialize(options)
|
25
|
+
@options = options
|
26
|
+
end
|
27
|
+
|
28
|
+
def compose_body(tree)
|
29
|
+
@options.formatter.format(tree)
|
30
|
+
end
|
25
31
|
|
26
|
-
|
27
|
-
|
32
|
+
def create_style(path_to_css_file); "".freeze; end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def toc_item_pat?(node)
|
28
37
|
node.kind_of?(PseudoHiki::BlockParser::HeadingLeaf) and
|
29
38
|
(2..3).include? node.level and
|
30
39
|
node.node_id
|
31
40
|
end
|
32
|
-
end
|
33
41
|
|
34
|
-
|
35
|
-
|
36
|
-
|
42
|
+
def collect_nodes_for_table_of_contents(tree)
|
43
|
+
Utils::NodeCollector.select(tree) {|node| toc_item_pat?(node) }
|
44
|
+
end
|
37
45
|
|
38
|
-
|
39
|
-
|
46
|
+
def to_plain(line)
|
47
|
+
PlainFormat.format(line).to_s
|
48
|
+
end
|
40
49
|
end
|
41
50
|
|
42
|
-
|
43
|
-
|
44
|
-
end
|
51
|
+
class HtmlComposer < BaseComposer
|
52
|
+
TABLE = "table"
|
45
53
|
|
46
|
-
|
47
|
-
|
48
|
-
|
54
|
+
def initialize(options)
|
55
|
+
super(options)
|
56
|
+
@link_manager = setup_link_manager(options)
|
57
|
+
@relative_link = options[:relative_link]
|
49
58
|
end
|
50
59
|
|
51
|
-
|
52
|
-
|
60
|
+
def compose_body(tree)
|
61
|
+
super(tree).tap do |html|
|
62
|
+
if @relative_link and @link_manager
|
63
|
+
@link_manager.use_relative_path_for_in_domain_links(html)
|
64
|
+
end
|
53
65
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
66
|
+
assign_table_header_scope(html) if @options[:accessibility]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_table_of_contents(tree)
|
71
|
+
@options.formatter.format(create_toc_tree(tree)).tap do |toc|
|
72
|
+
toc.traverse do |element|
|
73
|
+
if element.kind_of? HtmlElement and element.tagname == "a"
|
74
|
+
element["title"] = "toc_item: " + element.children.join.chomp
|
75
|
+
end
|
59
76
|
end
|
60
77
|
end
|
61
78
|
end
|
62
|
-
end
|
63
79
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
80
|
+
def create_main(toc, body, h1)
|
81
|
+
return nil unless @options[:toc]
|
82
|
+
main = formatter.create_element("section").tap do |element|
|
83
|
+
element["id"] = "main"
|
84
|
+
element.push h1 unless h1.empty?
|
85
|
+
element.push create_toc_container(toc)
|
86
|
+
element.push create_contents_container(body)
|
87
|
+
end
|
70
88
|
end
|
71
|
-
BlockParser.parse(toc_lines)
|
72
|
-
end
|
73
89
|
|
74
|
-
|
75
|
-
|
76
|
-
|
90
|
+
def create_style(path_to_css_file)
|
91
|
+
style = formatter.create_element("style").tap do |element|
|
92
|
+
element["type"] = "text/css"
|
93
|
+
open(File.expand_path(path_to_css_file)) do |css_file|
|
94
|
+
element.push css_file.read
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
77
98
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
99
|
+
private
|
100
|
+
|
101
|
+
def setup_link_manager(options)
|
102
|
+
if options[:domain_name]
|
103
|
+
domain_name = @options[:domain_name]
|
104
|
+
alternative_names = @options[:alternative_domain_names]
|
105
|
+
HtmlElement::Utils::LinkManager.new(domain_name, alternative_names)
|
106
|
+
end
|
84
107
|
end
|
85
108
|
|
86
|
-
|
87
|
-
|
109
|
+
def assign_table_header_scope(html)
|
110
|
+
HtmlElement::Utils.collect_elements_by_name(html, TABLE).each do |table|
|
111
|
+
HtmlElement::Utils::TableManager.assign_scope(table)
|
112
|
+
end
|
113
|
+
end
|
88
114
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
return create_gfm_table_of_contents(tree) if gfm_chosen
|
93
|
-
return create_plain_table_of_contents(tree) unless @options.html_template
|
94
|
-
create_html_table_of_contents(tree)
|
95
|
-
end
|
115
|
+
def formatter
|
116
|
+
@formatter ||= @options.html_template.new
|
117
|
+
end
|
96
118
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
119
|
+
def create_toc_tree(tree, newline=nil)
|
120
|
+
toc_lines = collect_nodes_for_table_of_contents(tree).map do |line|
|
121
|
+
format("%s[[%s|#%s]]#{newline}",
|
122
|
+
'*' * line.level,
|
123
|
+
to_plain(line).lstrip,
|
124
|
+
line.node_id.upcase)
|
125
|
+
end
|
126
|
+
BlockParser.parse(toc_lines)
|
127
|
+
end
|
128
|
+
|
129
|
+
def create_toc_container(toc)
|
130
|
+
formatter.create_element("section").tap do |elm|
|
131
|
+
elm["id"] = "toc"
|
132
|
+
title = @options[:toc]
|
133
|
+
elm.push formatter.create_element("h2", title) unless title.empty?
|
134
|
+
elm.push toc
|
135
|
+
end
|
136
|
+
end
|
104
137
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
contents.unshift toc_title
|
138
|
+
def create_contents_container(body)
|
139
|
+
formatter.create_element("section").tap do |elm|
|
140
|
+
elm["id"] = "contents"
|
141
|
+
elm.push body
|
142
|
+
end
|
111
143
|
end
|
112
|
-
contents.unshift h1 unless h1.empty?
|
113
|
-
contents.join($/)
|
114
144
|
end
|
115
145
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
146
|
+
class PlainComposer < BaseComposer
|
147
|
+
def create_table_of_contents(tree)
|
148
|
+
toc_lines = collect_nodes_for_table_of_contents(tree).map do |toc_node|
|
149
|
+
('*' * toc_node.level) + to_plain(toc_node)
|
150
|
+
end
|
151
|
+
|
152
|
+
@options.formatter.format(BlockParser.parse(toc_lines))
|
123
153
|
end
|
124
|
-
end
|
125
154
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
title = @options[:toc]
|
130
|
-
|
131
|
-
|
155
|
+
def create_main(toc, body, h1)
|
156
|
+
contents = [body]
|
157
|
+
contents.unshift toc unless toc.empty?
|
158
|
+
if title = @options[:toc]
|
159
|
+
toc_title = @options.formatter.format(BlockParser.parse("!!" + title))
|
160
|
+
contents.unshift toc_title
|
161
|
+
end
|
162
|
+
contents.unshift h1 unless h1.empty?
|
163
|
+
contents.join($/)
|
132
164
|
end
|
133
165
|
end
|
134
166
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
167
|
+
class GfmComposer < PlainComposer
|
168
|
+
def create_table_of_contents(tree)
|
169
|
+
toc_lines = collect_nodes_for_table_of_contents(tree).map do |toc_node|
|
170
|
+
format("%s[[%s|#%s]]#{$/}",
|
171
|
+
'*' * toc_node.level,
|
172
|
+
to_plain(toc_node).strip,
|
173
|
+
gfm_id(toc_node))
|
174
|
+
end
|
175
|
+
|
176
|
+
@options.formatter.format(BlockParser.parse(toc_lines))
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def gfm_id(heading_node)
|
182
|
+
MarkDownFormat.convert_into_gfm_id_format(to_plain(heading_node).strip)
|
139
183
|
end
|
140
184
|
end
|
141
185
|
|
142
|
-
def
|
143
|
-
|
144
|
-
|
186
|
+
def initialize(options)
|
187
|
+
@options = options
|
188
|
+
@composer = select_composer.new(options)
|
145
189
|
end
|
146
190
|
|
147
|
-
def
|
148
|
-
|
149
|
-
|
150
|
-
open(File.expand_path(path_to_css_file)) do |css_file|
|
151
|
-
element.push css_file.read
|
152
|
-
end
|
153
|
-
end
|
191
|
+
def select_composer
|
192
|
+
return GfmComposer if @options[:html_version].version == "gfm"
|
193
|
+
@options.html_template ? HtmlComposer : PlainComposer
|
154
194
|
end
|
155
195
|
|
156
|
-
def
|
196
|
+
def create_table_of_contents(tree)
|
197
|
+
return "" unless @options[:toc]
|
198
|
+
@composer.create_table_of_contents(tree)
|
199
|
+
end
|
200
|
+
|
201
|
+
def split_main_heading(input_lines)
|
202
|
+
return "" unless @options[:split_main_heading]
|
203
|
+
h1_pos = input_lines.find_index {|line| /^![^!]/o =~ line }
|
204
|
+
return "" unless h1_pos
|
205
|
+
tree = BlockParser.parse([input_lines.delete_at(h1_pos)])
|
157
206
|
@options.formatter.format(tree)
|
158
207
|
end
|
159
208
|
|
@@ -162,9 +211,9 @@ module PseudoHiki
|
|
162
211
|
css = @options[:css]
|
163
212
|
tree = BlockParser.parse(input_lines)
|
164
213
|
toc = create_table_of_contents(tree)
|
165
|
-
body = compose_body(tree)
|
214
|
+
body = @composer.compose_body(tree)
|
166
215
|
title = @options.title
|
167
|
-
main = create_main(toc, body, h1)
|
216
|
+
main = @composer.create_main(toc, body, h1)
|
168
217
|
choose_template(main, body, binding)
|
169
218
|
end
|
170
219
|
|
@@ -174,7 +223,7 @@ module PseudoHiki
|
|
174
223
|
else
|
175
224
|
html = @options.create_html_template_with_current_options
|
176
225
|
embed_css = @options[:embed_css]
|
177
|
-
html.head.push create_style(embed_css) if embed_css
|
226
|
+
html.head.push @composer.create_style(embed_css) if embed_css
|
178
227
|
html.push main || body
|
179
228
|
end
|
180
229
|
|
@@ -238,6 +287,7 @@ module PseudoHiki
|
|
238
287
|
attr_reader :input_file_basename
|
239
288
|
|
240
289
|
def self.remove_bom(input=ARGF)
|
290
|
+
return if input == ARGF and input.filename == "-"
|
241
291
|
bom = input.read(3)
|
242
292
|
input.rewind unless BOM == bom
|
243
293
|
end
|
@@ -323,17 +373,33 @@ instead of \"#{given_opt}\"."
|
|
323
373
|
end
|
324
374
|
end
|
325
375
|
|
326
|
-
def
|
376
|
+
def setup_ruby_encoding(given_opt)
|
327
377
|
return nil unless String.new.respond_to? :encoding
|
328
378
|
external, internal = given_opt.split(/:/o, 2)
|
329
379
|
Encoding.default_external = external if external and not external.empty?
|
330
380
|
Encoding.default_internal = internal if internal and not internal.empty?
|
331
381
|
end
|
332
382
|
|
333
|
-
def
|
383
|
+
def parse_opt_setup_ruby_encoding(opt)
|
384
|
+
opt.on("-E [ex[:in]]", "--encoding [=ex[:in]]",
|
385
|
+
"Specify the default external and internal character encodings \
|
386
|
+
(same as the option of MRI)") do |given_opt|
|
387
|
+
setup_ruby_encoding(given_opt)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def assign_opt_value(opt, key, short, long, description)
|
392
|
+
opt.on(short, long, description) {|val| self[key] = val }
|
393
|
+
end
|
394
|
+
|
395
|
+
def setup_command_line_options
|
334
396
|
OptionParser.new("USAGE: #{File.basename($0)} [OPTION]... [FILE]...
|
335
397
|
Convert texts written in a Hiki-like notation into another format.") do |opt|
|
336
|
-
opt.
|
398
|
+
opt.version = PseudoHiki::VERSION
|
399
|
+
|
400
|
+
parse_opt_setup_ruby_encoding(opt)
|
401
|
+
|
402
|
+
opt.on("-f [format_version]", "--format-version [=format_version]",
|
337
403
|
"Choose a formart for the output. Available options: \
|
338
404
|
html4, xhtml1, html5, plain, plain_verbose, markdown or gfm \
|
339
405
|
(default: #{self[:html_version].version})") do |version|
|
@@ -352,12 +418,6 @@ html4, xhtml1, html5, plain, plain_verbose, markdown or gfm \
|
|
352
418
|
set_html_encoding(given_opt)
|
353
419
|
end
|
354
420
|
|
355
|
-
opt.on("-E [ex[:in]]", "--encoding [=ex[:in]]",
|
356
|
-
"Specify the default external and internal character encodings \
|
357
|
-
(same as the option of MRI") do |given_opt|
|
358
|
-
set_encoding(given_opt)
|
359
|
-
end
|
360
|
-
|
361
421
|
# use '-w' to avoid the conflict with the short option for '[-t]emplate'
|
362
422
|
opt.on("-w [(window) title]", "--title [=title]",
|
363
423
|
"Set the value of the <title> element \
|
@@ -365,17 +425,15 @@ html4, xhtml1, html5, plain, plain_verbose, markdown or gfm \
|
|
365
425
|
self[:title] = title if value_given?(title)
|
366
426
|
end
|
367
427
|
|
368
|
-
opt
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
end
|
428
|
+
assign_opt_value(opt, :css, "-c [css]",
|
429
|
+
"--css [=css]",
|
430
|
+
"Set the path to a css file to be used \
|
431
|
+
(default: #{self[:css]})")
|
373
432
|
|
374
|
-
opt
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
end
|
433
|
+
assign_opt_value(opt, :embed_css, "-C [path_to_css_file]",
|
434
|
+
"--embed-css [=path_to_css_file]",
|
435
|
+
"Set the path to a css file to embed \
|
436
|
+
(default: not to embed)")
|
379
437
|
|
380
438
|
opt.on("-b [base]", "--base [=base]",
|
381
439
|
"Specify the value of href attribute of the <base> element \
|
@@ -396,22 +454,17 @@ inside (default: not specified)") do |template|
|
|
396
454
|
@need_output_file = true
|
397
455
|
end
|
398
456
|
|
399
|
-
opt
|
400
|
-
|
401
|
-
(default: false)")
|
402
|
-
self[:force] = force
|
403
|
-
end
|
457
|
+
assign_opt_value(opt, :force, "-F", "--force",
|
458
|
+
"Force to apply command line options. \
|
459
|
+
(default: false)")
|
404
460
|
|
405
|
-
opt
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
end
|
461
|
+
assign_opt_value(opt, :toc, "-m [contents-title]",
|
462
|
+
"--table-of-contents [=contents-title]",
|
463
|
+
"Include the list of h2 and/or h3 headings with ids. \
|
464
|
+
(default: nil)")
|
410
465
|
|
411
|
-
opt
|
412
|
-
"Split the first h1 element")
|
413
|
-
self[:split_main_heading] = should_be_split
|
414
|
-
end
|
466
|
+
assign_opt_value(opt, :split_main_heading, "-s", "--split-main-heading",
|
467
|
+
"Split the first h1 element")
|
415
468
|
|
416
469
|
opt.on("-W", "--with-wikiname",
|
417
470
|
"Use WikiNames") do |with_wikiname|
|
@@ -421,7 +474,21 @@ inside (default: not specified)") do |template|
|
|
421
474
|
end
|
422
475
|
end
|
423
476
|
|
424
|
-
opt.
|
477
|
+
opt.on("-d [domain_name(s)]", "--domain-name [=domain_name(s)]",
|
478
|
+
"Specify domain name(s)") do |domain_name|
|
479
|
+
names = domain_name.split(/;\s*/)
|
480
|
+
self[:domain_name] = names.shift
|
481
|
+
self[:alternative_domain_names] = names
|
482
|
+
end
|
483
|
+
|
484
|
+
assign_opt_value(opt, :relative_link, "-r", "--relative-links-in-html",
|
485
|
+
"Replace absolute paths with relative ones. \
|
486
|
+
*** THIS OPTION IS EXPERIMENTAL ***")
|
487
|
+
|
488
|
+
assign_opt_value(opt, :accessibility, "-a", "--accessibility",
|
489
|
+
"A bit improved accessibility")
|
490
|
+
|
491
|
+
opt
|
425
492
|
end
|
426
493
|
end
|
427
494
|
|
@@ -436,8 +503,10 @@ inside (default: not specified)") do |template|
|
|
436
503
|
end
|
437
504
|
end
|
438
505
|
|
439
|
-
def
|
440
|
-
|
506
|
+
def parse_command_line_options
|
507
|
+
opt = setup_command_line_options
|
508
|
+
yield opt if block_given?
|
509
|
+
opt.parse!
|
441
510
|
check_argv
|
442
511
|
@default_title = @input_file_basename
|
443
512
|
end
|
@@ -78,6 +78,7 @@ module PseudoHiki
|
|
78
78
|
|
79
79
|
def visit(tree)
|
80
80
|
htmlelement = create_element(tree)
|
81
|
+
decorate(htmlelement, tree)
|
81
82
|
push_visited_results(htmlelement, tree)
|
82
83
|
htmlelement
|
83
84
|
end
|
@@ -96,6 +97,21 @@ module PseudoHiki
|
|
96
97
|
chunks.push tree
|
97
98
|
end
|
98
99
|
|
100
|
+
def decorate(htmlelement, tree)
|
101
|
+
each_decorator(htmlelement, tree) do |elm, decorator|
|
102
|
+
elm[CLASS] = HtmlElement.escape(decorator[CLASS].id) if decorator[CLASS]
|
103
|
+
if id_item = decorator[ID] || decorator[:id]
|
104
|
+
elm[ID] = HtmlElement.escape(id_item.id).upcase
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def each_decorator(element, tree)
|
110
|
+
return unless element.kind_of? HtmlElement
|
111
|
+
return unless tree.kind_of? BlockParser::BlockNode and tree.decorator
|
112
|
+
tree.decorator.tap {|decorator| yield element, decorator }
|
113
|
+
end
|
114
|
+
|
99
115
|
class ListLeafNodeFormatter < self
|
100
116
|
def create_element(tree)
|
101
117
|
super(tree).tap do |elm|
|
@@ -121,6 +137,7 @@ module PseudoHiki
|
|
121
137
|
[EnumNode, "ol"],
|
122
138
|
[TableLeaf, "tr"],
|
123
139
|
[VerbatimNode, "pre"],
|
140
|
+
[SectioningNode, "section"],
|
124
141
|
[CommentOutNode, nil],
|
125
142
|
[HeadingNode, "section"],
|
126
143
|
[DescLeaf, DT],
|
@@ -189,6 +206,23 @@ module PseudoHiki
|
|
189
206
|
|
190
207
|
# for BlockParser
|
191
208
|
|
209
|
+
class << Formatter[TableNode]
|
210
|
+
def decorate(htmlelement, tree)
|
211
|
+
each_decorator(htmlelement, tree) do |elm, decorator|
|
212
|
+
visited_value(decorator["summary"]) do |summary|
|
213
|
+
htmlelement["summary"] = HtmlElement.escape(summary.join.chomp)
|
214
|
+
end
|
215
|
+
visited_value(decorator["caption"]) do |caption|
|
216
|
+
htmlelement.push @generator.create("caption", caption)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def visited_value(decorator_item)
|
222
|
+
yield visited_result(decorator_item.value) if decorator_item
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
192
226
|
class << Formatter[VerbatimNode]
|
193
227
|
def visit(tree)
|
194
228
|
contents = add_link(@generator.escape(tree.join))
|
@@ -203,6 +237,37 @@ module PseudoHiki
|
|
203
237
|
end
|
204
238
|
end
|
205
239
|
|
240
|
+
class << Formatter[SectioningNode]
|
241
|
+
ID_MARK = "#"
|
242
|
+
|
243
|
+
alias :orig_create_element :create_element
|
244
|
+
|
245
|
+
def section_with_id(tree, node_id)
|
246
|
+
orig_create_element(tree).tap do |elm|
|
247
|
+
elm[ID] = node_id[1..-1] # remove the first charactor from node_id
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def section_for_class(tree, node_id)
|
252
|
+
if HtmlElement::HTML5_TAGS.include? node_id
|
253
|
+
@generator.create(node_id)
|
254
|
+
else
|
255
|
+
orig_create_element(tree).tap do |elm|
|
256
|
+
elm[CLASS] = elm[CLASS] ? "#{elm[CLASS]} #{node_id}" : node_id
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def create_element(tree)
|
262
|
+
node_id = tree.node_id
|
263
|
+
if node_id.start_with? ID_MARK
|
264
|
+
section_with_id(tree, node_id)
|
265
|
+
else
|
266
|
+
section_for_class(tree, node_id)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
206
271
|
class << Formatter[CommentOutNode]
|
207
272
|
def visit(tree); BLANK; end
|
208
273
|
end
|
@@ -275,13 +275,14 @@ module PseudoHiki
|
|
275
275
|
class VerbatimNodeFormatter < self
|
276
276
|
def visit(tree)
|
277
277
|
element = super(tree)
|
278
|
+
@language_name = language_name(tree)
|
278
279
|
return gfm_verbatim(element) if @options.gfm_style
|
279
280
|
md_verbatim(element)
|
280
281
|
end
|
281
282
|
|
282
283
|
def gfm_verbatim(element)
|
283
284
|
element.tap do |lines|
|
284
|
-
lines.unshift "```#{$/}"
|
285
|
+
lines.unshift "```#{@language_name + $/}"
|
285
286
|
lines.push "```#{$/ * 2}"
|
286
287
|
end
|
287
288
|
end
|
@@ -289,6 +290,12 @@ module PseudoHiki
|
|
289
290
|
def md_verbatim(element)
|
290
291
|
element.join.gsub(/^/o, " ").sub(/ \Z/o, "").concat $/
|
291
292
|
end
|
293
|
+
|
294
|
+
def language_name(tree)
|
295
|
+
tree.decorator.tap do |decorator|
|
296
|
+
return decorator ? decorator["code"].id : ""
|
297
|
+
end
|
298
|
+
end
|
292
299
|
end
|
293
300
|
|
294
301
|
class QuoteNodeFormatter < self
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
module Sinatra
|
5
|
+
module PseudoHikiParserHelpers
|
6
|
+
XHTML5_CONTENT_TYPE = 'application/xhtml+xml'
|
7
|
+
def phiki(hiki_data, &block)
|
8
|
+
case content_type
|
9
|
+
when XHTML5_CONTENT_TYPE
|
10
|
+
PseudoHiki::Format.to_html5(hiki_data, &block)
|
11
|
+
else
|
12
|
+
PseudoHiki::Format.to_xhtml(hiki_data, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Base
|
18
|
+
helpers PseudoHikiParserHelpers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
rescue
|
22
|
+
#Sinatra is not available
|
23
|
+
end
|
data/lib/pseudohiki/utils.rb
CHANGED
@@ -29,5 +29,39 @@ module PseudoHiki
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
class TableManager
|
34
|
+
TH, COL, ROW = %w(th col row)
|
35
|
+
|
36
|
+
def guess_header_scope(table)
|
37
|
+
col_scope?(table) or row_scope?(table)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def col_scope?(table)
|
43
|
+
table.each_with_index do |row, i|
|
44
|
+
row.each do |cell|
|
45
|
+
return if cell.rowspan > 1 or cell.colspan > 1
|
46
|
+
# The first row sould be consist of <th> elements
|
47
|
+
# and other rows should not include <th> elements
|
48
|
+
return unless (i == 0) == (cell.cell_type == TH)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
COL
|
52
|
+
end
|
53
|
+
|
54
|
+
def row_scope?(table)
|
55
|
+
table.each do |row|
|
56
|
+
row.each_with_index do |cell, j|
|
57
|
+
return if cell.rowspan > 1 or cell.colspan > 1
|
58
|
+
# The first column sould be consist of <th> elements
|
59
|
+
# and other columns should not include <th> elements
|
60
|
+
return unless (j == 0) == (cell.cell_type == TH)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
ROW
|
64
|
+
end
|
65
|
+
end
|
32
66
|
end
|
33
67
|
end
|
data/lib/pseudohiki/version.rb
CHANGED