Browse files

Fixed the w3c validation bug!

Unfortuantely, this breaks text output.  I'll have to fix that sometime soon,
but it will be a good opportunity to implement text wrapping anyway.

This was annoyingly difficult.  There are two main changes: First, a new
subclass of ListBuilder, ItemBuilder is now used to wrap the inner content of
lists--the contained phrases in most cases.  This list builder determines when
other ListBuilders can be appended to the content, since sub-lists should be
nested under items.

Second, all of the HTML output now goes through ListBuilder and to_html is no
longer called on the parsed SyntaxNodes.  This is because ListBuilder needed to
add item tags (e.g., <li>) to each level in addition to the list type tags
(<ul>).  Instead of adding parameters to ListBuilder to make this work,
ListBuilder is now subclassed for each list type and those subclasses take care
of knowing how to render the correct HTML tags.  This will probably work out
well when I go back to reimplement the text formatting.
  • Loading branch information...
1 parent e2b94d9 commit 913d6c8eeac0cd4b1f1f0edb194dcb91a389dcde @rdblue committed Nov 13, 2010
Showing with 90 additions and 52 deletions.
  1. +78 −47 lib/marker/lists.rb
  2. +12 −5 test/lists_test.rb
View
125 lib/marker/lists.rb
@@ -9,54 +9,49 @@
module Marker #:nodoc:
# used to collect list lines into lists structured hierarchically, like in HTML
class ListBuilder
- attr_reader :tag, :contents, :attrs
+ attr_reader :contents
- def initialize( contents, tag, attrs = {} )
- @contents = [contents].flatten
- @tag = tag
- @attrs = attrs
+ def initialize( contents = nil )
+ @contents = ( contents ? [contents] : [] )
end
- # returns true if the l has the same tag
- def ===( l )
- ( l.is_a? self.class and tag == l.tag )
+ # returns true if the other has the same type
+ def ===( other )
+ ( other.is_a? self.class )
end
- def <<( l )
+ # append a new child to contents. if that child can be combined with the
+ # last child already known, then we add them together.
+ def <<( other )
last = contents.last
- if last and last === l
- last += l
+ if last and last === other
+ last += other
else
- contents << l
+ contents << other
end
self
end
- def +( l )
- l.contents.each { |i| self << i } if self === l
+ def +( other )
+ other.contents.each { |item| self << item } if self === other
self
end
def to_html( options = {} )
- if tag
- "<#{tag}" +
- ( attrs.any?? ' ' : '' ) +
- attrs.map { |k, v|
- "#{k}='#{v}'"
- }.join(' ') +
- ">" +
- contents.map { |i|
- i.to_html(options)
- }.join +
- "</#{tag}>"
- else
- contents.map { |i|
- i.to_html(options)
- }.join
- end
+ contents_to_html( options )
+ end
+
+ def contents_to_html( options = {} )
+ contents.map { |item|
+ item_to_html( item, options )
+ }.join
+ end
+
+ def item_to_html( item, options = {} )
+ item.to_html( options )
end
def to_s( options = {} )
@@ -71,29 +66,46 @@ def to_s( options = {} )
end
end
+ # a ListBuilder to encapsulate raw list item contents
+ class ItemBuilder < ListBuilder
+ def ===( other )
+ other.is_a? ListBuilder and not other.is_a? ItemBuilder
+ end
+
+ def +( other )
+ self << other
+ end
+ end
+
class List < RecursiveList
def to_html( options = {} )
- l = ListBuilder.new( [], nil )
+ l = ListBuilder.new
to_a.each do |item|
l << item.structure
end
l.to_html(options)
end
def to_s( options = {} )
- l = ListBuilder.new( [], nil )
+ l = ListBuilder.new
to_a.each do |item|
l << item.structure
end
l.to_s(options)
end
end
- class Bulleted < ParseNode
+ class BulletedListBuilder < ListBuilder
def to_html( options = {} )
- "<li>#{phrase.to_html(options)}</li>"
+ "<ul>#{contents_to_html(options)}</ul>"
end
+ def item_to_html( item, options = {} )
+ "<li>#{item.to_html(options)}</li>"
+ end
+ end
+
+ class Bulleted < ParseNode
def to_s( options = {} )
indent = (options[:indent] || 0)
' ' * (indent > 0 ? indent - 1 : 0) +
@@ -102,9 +114,9 @@ def to_s( options = {} )
def structure
if phrase
- ListBuilder.new( self, :ul )
+ BulletedListBuilder.new( ItemBuilder.new( phrase ) )
else
- ListBuilder.new( list_item.structure, :ul )
+ BulletedListBuilder.new( list_item.structure )
end
end
@@ -114,11 +126,17 @@ def phrase
end
end
- class Numbered < ParseNode
+ class NumberedListBuilder < ListBuilder
def to_html( options = {} )
- "<li>#{phrase.to_html(options)}</li>"
+ "<ol>#{contents_to_html(options)}</ol>"
+ end
+
+ def item_to_html( item, options = {} )
+ "<li>#{item.to_html(options)}</li>"
end
+ end
+ class Numbered < ParseNode
def to_s( options = {} )
indent = (options[:indent] || 0)
' ' * (indent > 0 ? indent - 1 : 0) +
@@ -127,9 +145,9 @@ def to_s( options = {} )
def structure
if phrase
- ListBuilder.new( self, :ol )
+ NumberedListBuilder.new( ItemBuilder.new( phrase ) )
else
- ListBuilder.new( list_item.structure, :ol )
+ NumberedListBuilder.new( list_item.structure )
end
end
@@ -139,11 +157,17 @@ def phrase
end
end
- class Indented < ParseNode
+ class IndentedListBuilder < ListBuilder
def to_html( options = {} )
- "<div>#{phrase.to_html(options)}</div>"
+ "<div class='indent'>#{contents_to_html(options)}</div>"
end
+ def item_to_html( item, options = {} )
+ "<div class='indent_item'>#{item.to_html(options)}</div>"
+ end
+ end
+
+ class Indented < ParseNode
def to_s( options = {} )
indent = (options[:indent] || 0)
' ' * (indent > 0 ? indent : 0) +
@@ -152,9 +176,9 @@ def to_s( options = {} )
def structure
if phrase
- ListBuilder.new( self, :div, :class => 'indent' )
+ IndentedListBuilder.new( ItemBuilder.new( phrase ) )
else
- ListBuilder.new( list_item.structure, :div, :class => 'indent' )
+ IndentedListBuilder.new( list_item.structure )
end
end
@@ -164,20 +188,27 @@ def phrase
end
end
- class Definition < ParseNode
+ class DefinitionListBuilder < ListBuilder
def to_html( options = {} )
+ "<dl>#{contents_to_html(options)}</dl>"
+ end
+
+ def item_to_html( item, options = {} )
+ term, definition = item
"<dt>#{term.to_html(options)}</dt>" +
- ( definition ? "<dd>#{definition.to_html(options)}</dd>" : "" )
+ "<dd>#{definition.to_html(options)}</dd>"
end
+ end
+ class Definition < ParseNode
def to_s( options = {} )
indent = (options[:indent] || 0)
' ' * (indent > 0 ? indent - 1: 0) +
"#{term.to_s(options)} :: #{definition.to_s(options)}"
end
def structure
- ListBuilder.new( self, :dl )
+ DefinitionListBuilder.new( [term, ItemBuilder.new( definition )] )
end
#-- defaults ++
View
17 test/lists_test.rb
@@ -14,7 +14,7 @@ def test_nested_bulleted_list
text = "* List item 1\n** List item 2\n* List item 3"
markup = Marker.parse text
- assert_match("<ul><li>List item 1</li><ul><li>List item 2</li></ul><li>List item 3</li></ul>", markup.to_html)
+ assert_match("<ul><li>List item 1<ul><li>List item 2</li></ul></li><li>List item 3</li></ul>", markup.to_html)
end
def test_numbered_list
@@ -28,7 +28,7 @@ def test_nested_numbered_list
text = "# List item 1\n## List item 2\n# List item 3"
markup = Marker.parse text
- assert_match("<ol><li>List item 1</li><ol><li>List item 2</li></ol><li>List item 3</li></ol>", markup.to_html)
+ assert_match("<ol><li>List item 1<ol><li>List item 2</li></ol></li><li>List item 3</li></ol>", markup.to_html)
end
def test_definition_list
@@ -59,15 +59,22 @@ def test_mixed_list
markup = Marker.parse text
assert_match("<ol><li>List item 1</li></ol><ul><li>List item 2</li></ul><ol><li>List item 3</li></ol><dl><dt>List item 4</dt>" +
- "<dd>definition</dd></dl><div class='indent'><div>List item 5</div></div><ul><li>List item 6</li></ul>", markup.to_html)
+ "<dd>definition</dd></dl><div class='indent'><div class='indent_item'>List item 5</div></div><ul><li>List item 6</li></ul>", markup.to_html)
end
def test_nested_mixed_list
text = "# List item 1\n#* List item 2\n# List item 3\n## List item 4\n#; List item 5 : definition\n#:List item 6"
markup = Marker.parse text
- assert_match("<ol><li>List item 1</li><ul><li>List item 2</li></ul><li>List item 3</li><ol><li>List item 4</li></ol>" +
- "<dl><dt>List item 5</dt><dd>definition</dd></dl><div class='indent'><div>List item 6</div></div></ol>", markup.to_html)
+ assert_match("<ol><li>List item 1<ul><li>List item 2</li></ul></li><li>List item 3<ol><li>List item 4</li></ol>" +
+ "<dl><dt>List item 5</dt><dd>definition</dd></dl><div class='indent'><div class='indent_item'>List item 6</div></div></li></ol>", markup.to_html)
+ end
+
+ def test_bare_nested_list
+ text = "*** item"
+ markup = Marker.parse text
+
+ assert_match("<ul><li><ul><li><ul><li>item</li></ul></li></ul></li></ul>", markup.to_html)
end
end

0 comments on commit 913d6c8

Please sign in to comment.