pseudohikiparser 0.0.4 → 0.0.5.develop

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- def initialize(options)
22
- @options = options
23
- @is_toc_item_pat = proc_for_is_toc_item_pat
24
- end
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
- def proc_for_is_toc_item_pat
27
- proc do |node|
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
- def formatter
35
- @formatter ||= @options.html_template.new
36
- end
42
+ def collect_nodes_for_table_of_contents(tree)
43
+ Utils::NodeCollector.select(tree) {|node| toc_item_pat?(node) }
44
+ end
37
45
 
38
- def to_plain(line)
39
- PlainFormat.format(line).to_s
46
+ def to_plain(line)
47
+ PlainFormat.format(line).to_s
48
+ end
40
49
  end
41
50
 
42
- def collect_nodes_for_table_of_contents(tree)
43
- Utils::NodeCollector.select(tree) {|node| @is_toc_item_pat.call(node) }
44
- end
51
+ class HtmlComposer < BaseComposer
52
+ TABLE = "table"
45
53
 
46
- def create_plain_table_of_contents(tree)
47
- toc_lines = collect_nodes_for_table_of_contents(tree).map do |toc_node|
48
- ('*' * toc_node.level) + to_plain(toc_node)
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
- @options.formatter.format(BlockParser.parse(toc_lines))
52
- end
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
- def create_html_table_of_contents(tree)
55
- @options.formatter.format(create_html_toc_tree(tree)).tap do |toc|
56
- toc.traverse do |element|
57
- if element.kind_of? HtmlElement and element.tagname == "a"
58
- element["title"] = "toc_item: " + element.children.join.chomp
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
- def create_html_toc_tree(tree, newline=nil)
65
- toc_lines = collect_nodes_for_table_of_contents(tree).map do |line|
66
- format("%s[[%s|#%s]]#{newline}",
67
- '*' * line.level,
68
- to_plain(line).lstrip,
69
- line.node_id.upcase)
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
- def gfm_id(heading_node)
75
- MarkDownFormat.convert_into_gfm_id_format(to_plain(heading_node).strip)
76
- end
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
- def create_gfm_table_of_contents(tree)
79
- toc_lines = collect_nodes_for_table_of_contents(tree).map do |toc_node|
80
- format("%s[[%s|#%s]]#{$/}",
81
- '*' * toc_node.level,
82
- to_plain(toc_node).strip,
83
- gfm_id(toc_node))
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
- @options.formatter.format(BlockParser.parse(toc_lines))
87
- end
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
- def create_table_of_contents(tree)
90
- return "" unless @options[:toc]
91
- gfm_chosen = @options[:html_version].version == "gfm"
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
- def split_main_heading(input_lines)
98
- return "" unless @options[:split_main_heading]
99
- h1_pos = input_lines.find_index {|line| /^![^!]/o =~ line }
100
- return "" unless h1_pos
101
- tree = BlockParser.parse([input_lines.delete_at(h1_pos)])
102
- @options.formatter.format(tree)
103
- end
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
- def create_plain_main(toc, body, h1)
106
- contents = [body]
107
- contents.unshift toc unless toc.empty?
108
- if title = @options[:toc]
109
- toc_title = @options.formatter.format(BlockParser.parse("!!" + title))
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
- def create_html_main(toc, body, h1)
117
- return nil unless @options[:toc]
118
- main = formatter.create_element("section").tap do |element|
119
- element["id"] = "main"
120
- element.push h1 unless h1.empty?
121
- element.push create_html_toc_container(toc)
122
- element.push create_html_contents_container(body)
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
- def create_html_toc_container(toc)
127
- formatter.create_element("section").tap do |elm|
128
- elm["id"] = "toc"
129
- title = @options[:toc]
130
- elm.push formatter.create_element("h2", title) unless title.empty?
131
- elm.push toc
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
- def create_html_contents_container(body)
136
- formatter.create_element("section").tap do |elm|
137
- elm["id"] = "contents"
138
- elm.push body
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 create_main(toc, body, h1)
143
- return create_plain_main(toc, body, h1) unless @options.html_template
144
- create_html_main(toc, body, h1)
186
+ def initialize(options)
187
+ @options = options
188
+ @composer = select_composer.new(options)
145
189
  end
146
190
 
147
- def create_style(path_to_css_file)
148
- style = formatter.create_element("style").tap do |element|
149
- element["type"] = "text/css"
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 compose_body(tree)
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 set_encoding(given_opt)
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 parse_command_line_options
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.on("-f [html_version]", "--format-version [=format_version]",
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.on("-c [css]", "--css [=css]",
369
- "Set the path to a css file to be used \
370
- (default: #{self[:css]})") do |css|
371
- self[:css] = css
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.on("-C [path_to_css_file]", "--embed-css [=path_to_css_file]",
375
- "Set the path to a css file to embed \
376
- (default: not to embed)") do |path_to_css_file|
377
- self[:embed_css] = path_to_css_file
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.on("-F", "--force",
400
- "Force to apply command line options. \
401
- (default: false)") do |force|
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.on("-m [contents-title]", "--table-of-contents [=contents-title]",
406
- "Include the list of h2 and/or h3 headings with ids. \
407
- (default: nil)") do |toc_title|
408
- self[:toc] = toc_title
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.on("-s", "--split-main-heading",
412
- "Split the first h1 element") do |should_be_split|
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.parse!
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 set_options_from_command_line
440
- parse_command_line_options
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module PseudoHiki
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5.develop"
3
3
  end
@@ -150,3 +150,5 @@ module PseudoHiki
150
150
  end
151
151
  end
152
152
  end
153
+
154
+ require 'pseudohiki/sinatra_helpers' if defined? Sinatra