Commits

Saurabh Kathpalia committed adce8b8 Merge

Merged default into default (9d04f5a)

  • Participants
  • Parent commits 9d04f5a, 600a9b4

Comments (0)

Files changed (28)

MoinMoin/converter/_tests/test_creole_in.py

 
 import re
 
-from MoinMoin.util.tree import moin_page, xlink
+from MoinMoin.util.tree import moin_page, xlink, html, xinclude
 
 from ..creole_in import Converter
 
     namespaces = {
         moin_page: '',
         xlink: 'xlink',
+        html: 'xhtml',
+        xinclude: 'xinclude',
     }
 
     output_re = re.compile(r'\s+xmlns(:\S+)?="[^"]+"')
                 '<page><body><p><a xlink:href="http://moinmo.in/">http://moinmo.in/</a></p></body></page>'),
             (u'[[http://moinmo.in/]]',
                 '<page><body><p><a xlink:href="http://moinmo.in/">http://moinmo.in/</a></p></body></page>'),
+            (u'[[MoinMoin:InterWiki]]',
+                '<page><body><p><a xlink:href="wiki://MoinMoin/InterWiki">InterWiki</a></p></body></page>'),
             (u'[[javascript:alert("xss")]]',
                 '<page><body><p><a xlink:href="wiki.local:javascript:alert%28%22xss%22%29">javascript:alert("xss")</a></p></body></page>'),
             (u'[[http://moinmo.in/|MoinMoin]]',
             (u'[[MoinMoin]]',
                 '<page><body><p><a xlink:href="wiki.local:MoinMoin">MoinMoin</a></p></body></page>'),
             (u'{{http://moinmo.in/}}',
-                '<page><body><p><object xlink:href="http://moinmo.in/">http://moinmo.in/</object></p></body></page>'),
+                '<page><body><p><object xlink:href="http://moinmo.in/" /></p></body></page>'),
             (u'{{http://moinmo.in/|MoinMoin}}',
-                '<page><body><p><object xlink:href="http://moinmo.in/">MoinMoin</object></p></body></page>'),
+                '<page><body><p><object xhtml:alt="MoinMoin" xlink:href="http://moinmo.in/" /></p></body></page>'),
+            (u'{{my.png}}',
+                '<page><body><p><xinclude:include xinclude:href="wiki.local:my.png" /></p></body></page>'),
             (u'----',
-                '<page><body><separator /></body></page>'),
+                '<page><body><separator class="moin-hr3" /></body></page>'),
         ]
         for i in data:
             yield (self.do, ) + i

MoinMoin/converter/_tests/test_image.py

         ]
 
         output = ('<div xmlns="http://www.w3.org/1999/xhtml"><p data-lineno="1"><span class="moin-transclusion" '
-                  'data-href="/imagetest"><img src="/+get/+2882c905b2ab409fbf79cd05637a112d/imagetest">'
+                  'data-href="/imagetest"><img alt="imagetest" src="/+get/+2882c905b2ab409fbf79cd05637a112d/imagetest">'
                   '</span></p></div>')
 
         for imagetype in tests:
                         'ns2:height="10" ns2:width="10" ns0:type="image/jpeg" />'
                         '</ns0:body></ns0:page></ns0:p></ns0:body></ns0:page>')
 
-        image_resize_out = ('<div xmlns="http://www.w3.org/1999/xhtml"><p><div><img height="10" '
+        image_resize_out = ('<div xmlns="http://www.w3.org/1999/xhtml"><p><div><img alt="imagetest" height="10" '
                             'src="/+get/+2882c905b2ab409fbf79cd05637a112d/imagetest" width="10">'
                             '</div></p></div>')
 

MoinMoin/converter/_tests/test_include.py

         rendered = Item.create(u'page1').content._render_data()
         assert '<p><span class="moin-transclusion" data-href="/logo.png"><img alt="logo.png" src=' in rendered
         assert '/logo.png" /></span></p>' in rendered
+        # simple transclusion with alt text and width
+        update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'{{logo.png|my alt text|width="100"}}')
+        rendered = Item.create(u'page1').content._render_data()
+        assert '<p><span class="moin-transclusion" data-href="/logo.png"><img alt="my alt text" src=' in rendered
+        assert 'logo.png" width="100" /></span></p>' in rendered
         # within paragraph
         update_item(u'page1', {CONTENTTYPE: u'text/x.moin.wiki;charset=utf-8'}, u'text {{logo.png}} text')
         rendered = Item.create(u'page1').content._render_data()

MoinMoin/converter/_tests/test_markdown_in.py

             (u'Text\nTest',
                 '<p>Text\nTest</p>'),
             (u'Text\n\nTest',
-                '<p>Text</p>\n<p>Test</p>'),
+                '<p>Text</p><p>Test</p>'),
             (u'<http://moinmo.in/>',
                 '<p><a xlink:href="http://moinmo.in/">http://moinmo.in/</a></p>'),
             (u'[yo](javascript:alert("xss"))',
             (u'[MoinMoin](http://moinmo.in/)',
                 '<p><a xlink:href="http://moinmo.in/">MoinMoin</a></p>'),
             (u'----',
-                '<separator />'),
+                '<separator class="moin-hr3" />'),
         ]
         for i in data:
             yield (self.do, ) + i
             (u'###### Heading 6 ######',
                 '<h outline-level="6" xml:id="heading-6">Heading 6</h>'),
             (u'Heading 1\n=========\nHeading 2\n---------\n',
-                '<h outline-level="1" xml:id="heading-1">Heading 1</h>\n<h outline-level="2" xml:id="heading-2">Heading 2</h>'),
+                '<h outline-level="1" xml:id="heading-1">Heading 1</h><h outline-level="2" xml:id="heading-2">Heading 2</h>'),
             (u'Heading 2\n---------\n',
                 '<h outline-level="2" xml:id="heading-2">Heading 2</h>'),
             (u'Heading\n=======\n\nxxxx',
-                '<h outline-level="1" xml:id="heading">Heading</h>\n<p>xxxx</p>'),
+                '<h outline-level="1" xml:id="heading">Heading</h><p>xxxx</p>'),
         ]
         for i in data:
             yield (self.do, ) + i
     def test_list(self):
         data = [
             (u'* Item',
-                '<list item-label-generate="unordered">\n<list-item><list-item-body>Item</list-item-body></list-item>\n</list>'),
+                '<list item-label-generate="unordered"><list-item><list-item-body>Item</list-item-body></list-item></list>'),
             (u'* Item\nItem',
-                '<list item-label-generate="unordered">\n<list-item><list-item-body>Item\nItem</list-item-body></list-item>\n</list>'),
+                '<list item-label-generate="unordered"><list-item><list-item-body>Item\nItem</list-item-body></list-item></list>'),
             (u'* Item 1\n* Item 2',
-                '<list item-label-generate="unordered">\n<list-item><list-item-body>Item 1</list-item-body></list-item>\n<list-item><list-item-body>Item 2</list-item-body></list-item>\n</list>'),
+                '<list item-label-generate="unordered"><list-item><list-item-body>Item 1</list-item-body></list-item><list-item><list-item-body>Item 2</list-item-body></list-item></list>'),
             (u'* Item 1\n    * Item 1.2\n* Item 2',
-                '<list item-label-generate="unordered">\n<list-item><list-item-body>Item 1<list item-label-generate="unordered">\n<list-item><list-item-body>Item 1.2</list-item-body></list-item>\n</list>\n</list-item-body></list-item>\n<list-item><list-item-body>Item 2</list-item-body></list-item>\n</list>'),
+                '<list item-label-generate="unordered"><list-item><list-item-body>Item 1<list item-label-generate="unordered"><list-item><list-item-body>Item 1.2</list-item-body></list-item></list></list-item-body></list-item><list-item><list-item-body>Item 2</list-item-body></list-item></list>'),
             (u'* List 1\n\nyo\n\n\n* List 2',
-                '<list item-label-generate="unordered">\n<list-item><list-item-body>List 1</list-item-body></list-item>\n</list>\n<p>yo</p>\n<list item-label-generate="unordered">\n<list-item><list-item-body>List 2</list-item-body></list-item>\n</list>'),
+                '<list item-label-generate="unordered"><list-item><list-item-body>List 1</list-item-body></list-item></list><p>yo</p><list item-label-generate="unordered"><list-item><list-item-body>List 2</list-item-body></list-item></list>'),
             (u'8. Item',
-                '<list item-label-generate="ordered">\n<list-item><list-item-body>Item</list-item-body></list-item>\n</list>'),
+                '<list item-label-generate="ordered"><list-item><list-item-body>Item</list-item-body></list-item></list>'),
         ]
         for i in data:
             yield (self.do, ) + i
             (u'![Alt text](png "Optional title")',
                 '<p><xinclude:include html:alt="Alt text" xinclude:href="wiki.local:png" /></p>'),
             (u'![](png "Optional title")',
-                '<p><xinclude:include html:alt="" xinclude:href="wiki.local:png" /></p>'),
+                '<p><xinclude:include xinclude:href="wiki.local:png" /></p>'),
             (u'![remote image](http://static.moinmo.in/logos/moinmoin.png)',
-                '<p><object xlink:href="http://static.moinmo.in/logos/moinmoin.png">remote image</object></p>'),
+                '<p><object html:alt="remote image" xlink:href="http://static.moinmo.in/logos/moinmoin.png" /></p>'),
             (u'![Alt text](http://test.moinmo.in/png)',
-                '<p><object xlink:href="http://test.moinmo.in/png">Alt text</object></p>'),
+                '<p><object html:alt="Alt text" xlink:href="http://test.moinmo.in/png" /></p>'),
             (u'![transclude local wiki item](someitem)',
                 '<p><xinclude:include html:alt="transclude local wiki item" xinclude:href="wiki.local:someitem" /></p>'),
         ]
     def do(self, input, output, args={}):
         out = self.conv(input, 'text/x-markdown;charset=utf-8', **args)
         got_output = self.serialize(out)
-        desired_output = "<page><body>\n%s\n</body></page>" % output
+        desired_output = "<page><body>%s</body></page>" % output
         print '------------------------------------'
         print "WANTED:"
         print desired_output

MoinMoin/converter/_tests/test_moinwiki_in.py

                 '<page><body><p><a xlink:href="wiki.local:MoinMoin">MoinMoin</a></p></body></page>'),
             (u'{{somelocalimage|my alt text|width=10, height=10}}',
                 '<page><body><p><xinclude:include xhtml:alt="my alt text" xhtml:height="10" xhtml:width="10" xinclude:href="wiki.local:somelocalimage?" /></p></body></page>'),
+            # html5 requires img tags to have an alt attribute, html_out.py will add any required attributes that are missing
             (u'{{somelocalimage||width=10, height=10}}',
                 '<page><body><p><xinclude:include xhtml:height="10" xhtml:width="10" xinclude:href="wiki.local:somelocalimage?" /></p></body></page>'),
             (u'{{somelocalimage||width=10, &h=10}}',
                 '<page><body><p><xinclude:include xhtml:width="10" xinclude:href="wiki.local:somelocalimage?h=10" /></p></body></page>'),
-            # object tags do not have alt attributes, instead it is placed before the </object>
+            (u'before {{somelocalimage}} middle {{somelocalimage}} after',
+                '<page><body><p>before <xinclude:include xinclude:href="wiki.local:somelocalimage?" /> middle <xinclude:include xinclude:href="wiki.local:somelocalimage?" /> after</p></body></page>'),
+            (u'before {{http://moinmo.in}} middle {{http://moinmo.in}} after',
+                '<page><body><p>before <object xlink:href="http://moinmo.in" /> middle <object xlink:href="http://moinmo.in" /> after</p></body></page>'),
+            # in html5, object tags must not have alt attributes, html_out.py will adjust this so alt text is placed before the </object>
             (u'{{http://moinmo.in/|test|width=10, height=10}}',
-                '<page><body><p><object xhtml:height="10" xhtml:width="10" xlink:href="http://moinmo.in/">test</object></p></body></page>'),
+                '<page><body><p><object xhtml:alt="test" xhtml:height="10" xhtml:width="10" xlink:href="http://moinmo.in/" /></p></body></page>'),
             (u'{{http://moinmo.in/}}',
                 '<page><body><p><object xlink:href="http://moinmo.in/" /></p></body></page>', None, 'unknown'),
             (u'{{http://moinmo.in/|MoinMoin}}',

MoinMoin/converter/_tests/test_moinwiki_in_out.py

 import re
 
 from emeraldtree import ElementTree as ET
-from MoinMoin.util.tree import moin_page, xlink
+from MoinMoin.util.tree import moin_page, xlink, xinclude, html
 from MoinMoin.converter.moinwiki_in import Converter as conv_in
 from MoinMoin.converter.moinwiki_out import Converter as conv_out
 
 
 class TestConverter(object):
 
-    input_namespaces = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}"'.format(moin_page.namespace, moin_page.namespace, xlink.namespace)
+    input_namespaces = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:xinclude="{3}" xmlns:html="{4}"'.format(
+        moin_page.namespace, moin_page.namespace, xlink.namespace, xinclude.namespace, html.namespace)
 
     namespaces = {
         moin_page.namespace: 'page',
         xlink.namespace: 'xlink',
+        xinclude.namespace: 'xinclude',
+        html.namespace: 'html',
     }
     input_re = re.compile(r'^(<[a-z:]+)')
     output_re = re.compile(r'\s+xmlns(:\S+)?="[^"]+"')
             # (u'[[http://moinmo.in/|MoinMoin Wiki|class=green dotted, accesskey=1]]', '[[http://moinmo.in/|MoinMoin Wiki|class=green dotted,accesskey=1]]\n'),
             # (u'[[MoinMoin:MoinMoinWiki|MoinMoin Wiki|&action=diff,&rev1=1,&rev2=2]]', '[[MoinMoin:MoinMoinWiki|MoinMoin Wiki|&action=diff,&rev1=1,&rev2=2]]\n'),
             # (u'[[attachment:HelpOnImages/pineapple.jpg|a pineapple|&do=get]]', '[[attachment:HelpOnImages/pineapple.jpg|a pineapple|&do=get]]\n'),
-            # (u'[[attachment:filename.txt]]', '[[attachment:filename.txt]]\n')
+            # Note: old style attachments are converted to new style sub-item syntax
+            (u'[[attachment:filename.txt]]', '[[/filename.txt]]\n')
         ]
         for i in data:
             yield (self.do, ) + i
 
     def test_object(self):
         data = [
-            # (u'{{png}}', '{{png}}\n'),
-            # (u'{{png|png}}', '{{png|png}}\n'),
-            # (u'{{png|my png}}', '{{png|my png}}\n'),
-            # (u'{{png|my png|width=100}}', '{{png|my png|width=100}}\n'),
-            # (u'{{png||width=100}}', '{{png||width=100}}\n'),
-            # (u"{{drawing:anywikitest.adraw}}", '{{drawing:anywikitest.adraw}}\n'),
+            (u'{{png}}', '{{png}}\n'),
+            (u'{{png|png}}', '{{png|png}}\n'),  # alt text same as default test
+            (u'{{png|my png}}', '{{png|my png}}\n'),
+            # output attributes will always be quoted, even if input is not quoted
+            (u'{{png|my png|width=100}}', '{{png|my png|width="100"}}\n'),
+            (u'{{png|my png|&w=100"}}', '{{png|my png|&w=100}}\n'),
+            (u'{{png||width="100"}}', '{{png||width="100"}}\n'),
+            (u"{{drawing:anywikitest.adraw}}", '{{drawing:anywikitest.adraw}}\n'),
             (u"{{http://static.moinmo.in/logos/moinmoin.png}}\n", '{{http://static.moinmo.in/logos/moinmoin.png}}\n'),
-            # (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text}}', '{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n'),
-            # (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|width=100 height=150 align=right}}', '{{http://static.moinmo.in/logos/moinmoin.png|alt text|width=100 height=150 align=right}}\n'),
-            # (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|width=100}}', '{{http://static.moinmo.in/logos/moinmoin.png|alt text|width=100}}\n'),
-            # (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|width=100 height=150 style="float: right"}}', '{{http://static.moinmo.in/logos/moinmoin.png|alt text|width=100 height=150 style="float: right"}}\n'),
-            # (u'{{attachment:image.png}}', '{{attachment:image.png}}\n'),
-            # (u'{{attachment:image.png|alt text}}', '{{attachment:image.png|alt text}}\n'),
-            # (u'{{attachment:image.png|alt text|width=100 align=left height=150}}', '{{attachment:image.png|alt text|width=100 align=left height=150}}\n'),
+            (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n', '{{http://static.moinmo.in/logos/moinmoin.png|alt text}}\n'),
+            # output sequence of height, width, class may not be the same as input, so here we test only one attribute at a time to avoid random test failures
+            (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|height="150"}}\n', '{{http://static.moinmo.in/logos/moinmoin.png|alt text|height="150"}}\n'),
+            (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|width="100"}}', '{{http://static.moinmo.in/logos/moinmoin.png|alt text|width="100"}}\n'),
+            (u'{{http://static.moinmo.in/logos/moinmoin.png|alt text|class="right"}}', '{{http://static.moinmo.in/logos/moinmoin.png|alt text|class="right"}}\n'),
+            # Note: old style attachments are converted to new style sub-item syntax
+            (u'{{attachment:image.png}}', '{{/image.png}}\n'),
+            (u'{{attachment:image.png|alt text}}', '{{/image.png|alt text}}\n'),
+            (u'{{attachment:image.png|alt text|height="150"}}', '{{/image.png|alt text|height="150"}}\n'),
 
         ]
         for i in data:

MoinMoin/converter/_tests/test_moinwiki_out.py

 
 
 class Base(object):
-    input_namespaces = ns_all = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}"'.format(moin_page.namespace, moin_page.namespace, xlink.namespace)
+    input_namespaces = ns_all = 'xmlns="{0}" xmlns:page="{1}" xmlns:xlink="{2}" xmlns:html="{3}"'.format(
+        moin_page.namespace, moin_page.namespace, xlink.namespace, html.namespace)
     output_namespaces = {
         moin_page.namespace: 'page'
     }
 
     def test_object(self):
         data = [
-            (u"<page:object xlink:href=\"drawing:anywikitest.adraw\">drawing:anywikitest.adraw</page:object>", '{{drawing:anywikitest.adraw}}'),
+            (u"<page:object xlink:href=\"drawing:anywikitest.adraw\"></page:object>", '{{drawing:anywikitest.adraw}}'),
             (u"<page:object xlink:href=\"http://static.moinmo.in/logos/moinmoin.png\" />", '{{http://static.moinmo.in/logos/moinmoin.png}}'),
             (u'<page:object page:alt="alt text" xlink:href="http://static.moinmo.in/logos/moinmoin.png">alt text</page:object>', '{{http://static.moinmo.in/logos/moinmoin.png|alt text}}'),
             (u'<page:object xlink:href="attachment:image.png" />', '{{attachment:image.png}}'),
-            # TODO: review following 2 lines, alt is invalid within object tag, align is not valid in html5
             (u'<page:object page:alt="alt text" xlink:href="attachment:image.png">alt text</page:object>', '{{attachment:image.png|alt text}}'),
-            (u'<page:object page:alt="alt text" xlink:href="attachment:image.png?width=100&amp;height=150&amp;align=left" />', '{{attachment:image.png|alt text|width=100 height=150 align=left}}'),
+            (u'<page:object xlink:href="attachment:image.png" html:width="100" html:height="150" html:class="left">alt text</page:object>', '{{attachment:image.png|alt text|height="150" width="100" class="left"}}'),
         ]
         for i in data:
             yield (self.do, ) + i

MoinMoin/converter/creole_in.py

 
 from MoinMoin.constants.misc import URI_SCHEMES
 from MoinMoin.util.iri import Iri
-from MoinMoin.util.tree import moin_page, xlink, xinclude
+from MoinMoin.util.tree import moin_page, xlink, xinclude, html
+from MoinMoin.util.interwiki import is_known_wiki
 
 from ._args_wiki import parse as parse_arguments
 from ._wiki_macro import ConverterMacro
 
     block_separator = r'(?P<separator> ^ \s* ---- \s* $ )'
 
-    def block_separator_repl(self, _iter_content, stack, separator):
+    def block_separator_repl(self, _iter_content, stack, separator, hr_class=u'moin-hr3'):
         stack.clear()
-        stack.top_append(moin_page.separator())
+        stack.top_append(moin_page.separator(attrib={moin_page.class_: hr_class}))
 
     block_table = r"""
         (?P<table>
     def inline_escape_repl(self, stack, escape, escaped_char):
         stack.top_append(escaped_char)
 
-    # TODO: Parse interwiki the same way as Moin Wiki.
     inline_link = r"""
         (?P<link>
             \[\[
                     [^|]+?
                 )
                 |
-                (?P<link_page> [^|]+? )
+                (
+                    (?P<link_interwiki_site>[A-Z][a-zA-Z]+)
+                    :
+                    (?P<link_interwiki_item>[^|]+) # accept any item name; will verify link_interwiki_site below
+                )
+                |
+                (?P<link_item> [^|]+? )
             )
             \s*
             ([|] \s* (?P<link_text>.+?) \s*)?
         )
     """
 
-    def inline_link_repl(self, stack, link, link_url=None, link_page=None, link_text=None):
+    def inline_link_repl(self, stack, link, link_url=None, link_item=None, link_text=None,
+                         link_interwiki_site=None, link_interwiki_item=None):
         """Handle all kinds of links."""
-
-        if link_page is not None:
+        if link_interwiki_site:
+            if is_known_wiki(link_interwiki_site):
+                link = Iri(scheme='wiki',
+                           authority=link_interwiki_site,
+                           path='/' + link_interwiki_item)
+                element = moin_page.a(attrib={xlink.href: link})
+                stack.push(element)
+                if link_text:
+                    self.parse_inline(link_text, stack, self.inlinedesc_re)
+                else:
+                    stack.top_append(link_interwiki_item)
+                stack.pop()
+                return
+            else:
+                # assume local language uses ":" inside of words, set link_item and continue
+                link_item = '{0}:{1}'.format(link_interwiki_site, link_interwiki_item)
+        if link_item is not None:
             att = 'attachment:'  # moin 1.9 needed this for an attached file
-            if link_page.startswith(att):
-                link_page = '/' + link_page[len(att):]  # now we have a subitem
-            target = unicode(Iri(scheme='wiki.local', path=link_page))
-            text = link_page
+            if link_item.startswith(att):
+                link_item = '/' + link_item[len(att):]  # now we have a subitem
+            target = unicode(Iri(scheme='wiki.local', path=link_item))
+            text = link_item
         else:
             target = link_url
             text = link_url
         element = moin_page.a(attrib={xlink.href: target})
         stack.push(element)
-        self.parse_inline(link_text or text, stack, self.link_desc_re)
+        if link_text:
+            self.parse_inline(link_text, stack, self.inlinedesc_re)
+        else:
+            stack.top_append(text)
         stack.pop()
 
     inline_macro = r"""
 
     def inline_object_repl(self, stack, object, object_page=None, object_url=None, object_text=None):
         """Handles objects included in the page."""
-
+        attrib = {}
+        if object_text:
+            attrib[html.alt] = object_text
         if object_page is not None:
             att = 'attachment:'  # moin 1.9 needed this for an attached file
             if object_page.startswith(att):
                 object_page = '/' + object_page[len(att):]  # now we have a subitem
             target = Iri(scheme='wiki.local', path=object_page)
-            text = object_page
-
-            attrib = {xinclude.href: target}
+            attrib[xinclude.href] = target
             element = xinclude.include(attrib=attrib)
-            stack.top_append(element)
-
         else:
-            target = object_url
-            text = object_url
-
-            element = moin_page.object({xlink.href: target})
-            stack.push(element)
-            self.parse_inline(object_text or text, stack, self.link_desc_re)
-            stack.pop()
+            attrib[xlink.href] = object_url
+            element = moin_page.object(attrib)
+        stack.top_append(element)
 
     inline_url = r"""
         (?P<url>
     """
 
     def tablerow_cell_repl(self, stack, cell, cell_text, cell_head=None):
-        element = moin_page.table_cell()
+        """
+        Creole has feature that allows table headings to be either row based or column based.
+
+        We avoid use of HTML5 row based thead tag and apply CSS styling to any cell marked as a heading.
+        """
+        attrib = {}
+        if cell_head:
+            attrib[moin_page.class_] = 'moin-thead'
+        element = moin_page.table_cell(attrib=attrib)
         stack.push(element)
-
-        # TODO: support table headings
         self.parse_inline(cell_text, stack)
-
         stack.pop_name('table-cell')
 
     # Block elements
     )
     inline_re = re.compile('|'.join(inline), re.X | re.U)
 
+    inlinedesc = (
+        inline_macro,
+        inline_nowiki,
+        inline_emph,
+        inline_strong,
+        inline_object,
+    )
+    inlinedesc_re = re.compile('|'.join(inlinedesc), re.X | re.U)
+
     # Link description
     link_desc = (
         inline_object,

MoinMoin/converter/docbook_in.py

         attrib[key] = "footnote"
         children = self.new(moin_page('note-body'), attrib={},
                             children=self.do_children(element, depth))
-        # must delete lineno because footnote will be placed near end of page and out of sequence
-        children._children[1].attrib.pop(html.data_lineno, None)
+        if len(children) > 1:
+            # must delete lineno because footnote will be placed near end of page and out of sequence
+            del children._children[1].attrib[html.data_lineno]
         return self.new(moin_page.note, attrib=attrib, children=[children])
 
     def visit_docbook_formalpara(self, element, depth):

MoinMoin/converter/html_out.py

 
 from MoinMoin import wikiutil
 from MoinMoin.i18n import _, L_, N_
-from MoinMoin.util.tree import html, moin_page, xlink, xml, Name
+from MoinMoin.util.tree import html, moin_page, xlink, xml, Name, xinclude
 from MoinMoin.constants.contenttypes import CONTENTTYPE_NONEXISTENT
+from MoinMoin.util.iri import Iri
 
 from MoinMoin import log
 logging = log.getLogger(__name__)
         else:
             ret = html.dl(attrib=attrib_new)
 
+        # TODO: ReST parser creates definition list classifiers but they are ignored here.
+        # TODO: An extraneous  "<dd></dd>" is created given " object::\n :: desc 1\n :: desc 2\n" -- moinwiki_in error?
         for item in elem:
             if isinstance(item, ET.Element):
                 if item.tag.uri == moin_page and item.tag.name == 'list-item':
                                 break
         return ret
 
+    def visit_moinpage_list_item(self, elem):
+        """
+        Used for markdown definition lists.
+
+        Compared to moinwiki and ReST parsers, the markdown parser creates definition lists using only one
+        list-item tag.name for entire list where moinwiki and ReST have one list-item tag.name for
+        each entry in list.
+        """
+        attrib = Attributes(elem)
+        attrib_new = attrib.convert()
+        ret = html.dl(attrib=attrib_new)
+        for item in elem:
+            if isinstance(item, ET.Element) and item.tag.uri == moin_page:
+                if item.tag.name == 'list-item-label':
+                    ret.append(self.new_copy(html.dt, item))
+                elif item.tag.name == 'list-item-body':
+                    ret.append(self.new_copy(html.dd, item))
+        return ret
+
     def eval_object_type(self, mimetype, href):
         """
         Returns the type of an object as a str, one of the following: img, video, audio, object
         """
         href = elem.get(xlink.href, None)
         attrib = {}
-        whitelist = ['width', 'height']
+        whitelist = ['width', 'height', 'alt', 'class']
         for key in elem.attrib:
             if key.name in whitelist:
                 attrib[key] = elem.attrib[key]
         mimetype = Type(_type=elem.get(moin_page.type_, CONTENTTYPE_NONEXISTENT))
+        if elem.get(moin_page.type_):
+            del elem.attrib[moin_page.type_]
         # Get the object type
         obj_type = self.eval_object_type(mimetype, href)
 
         if href is not None:
             # Set the attribute of the returned element appropriately
             attrib[attr] = href
+        alt = convert_getlink_to_showlink(unicode(href))
+        alt = re.sub('^\/', '', alt)
 
         if obj_type == "img":
-            # Images have alt text
+            # Images must have alt attribute in html5, but if user did not specify then default to url
             if not attrib.get(html.alt):
-                alt = ''.join(unicode(e) for e in elem)  # XXX handle non-text e
-                if alt:
-                    attrib[html.alt] = alt
+                attrib[html.alt] = alt
             new_elem = html.img(attrib=attrib)
 
         else:
             if obj_type != "object":
-                # Non-objects have the "controls" attribute
+                # Non-objects like video and audio have the "controls" attribute
                 attrib[html.controls] = 'controls'
-            new_elem = self.new_copy(getattr(html, obj_type), elem, attrib)
+                new_elem = self.new_copy(getattr(html, obj_type), elem, attrib)
+            else:
+                # is an object
+                new_elem = html.object(attrib=attrib)
+                if new_elem.attrib.get(html.alt):
+                    new_elem.append(new_elem.attrib.get(html.alt))
+                    del new_elem.attrib[html.alt]
+                else:
+                    new_elem.append(alt)
 
         if obj_type == "object" and getattr(href, 'scheme', None):
             # items similar to {{http://moinmo.in}} are marked here, other objects are marked in include.py
         return html.p()
 
     def visit_moinpage_quote(self, elem):
-        return self.new_copy(html.quote, elem)
+        return self.new_copy(html.q, elem)
 
     def visit_moinpage_separator(self, elem):
         return self.new_copy(html.hr, elem)

MoinMoin/converter/include.py

                         loop = self.stack[self.stack.index(p_href):]
                         loop = [u'{0}'.format(ref.path[1:]) for ref in loop if ref is not None] + [page.name]
                         msg = u'Error: Transclusion loop via: ' + u', '.join(loop)
-                        attrib = {getattr(moin_page, 'class'): 'moin-error'}
+                        attrib = {html.class_: 'moin-error'}
                         strong = ET.Element(moin_page.strong, attrib, (msg, ))
                         included_elements.append(strong)
                         continue
                         included_elements.append(elem_h)
 
                     page_doc = page.content.internal_representation(attributes=Arguments(keyword=elem.attrib))
-                    # page_doc.tag = self.tag_div # XXX why did we have this?
 
                     self.recurse(page_doc, page_href)
 
                                     # is usually replaced by container
                                     return [container, new_trans_ptr]
                             else:
-                                # default action for odd things like circular transclusion error messages
+                                # default action for inline transclusions or odd things like circular transclusion error messages
+                                classes = child.attrib.get(html.class_, '').split()
+                                classes += ret.attrib.get(html.class_, '').split()
+                                ret.attrib[html.class_] = ' '.join(classes)
                                 elem[i] = ret
                         elif isinstance(ret, types.ListType):
                             # a container has been returned.
                                 new_trans_ptr = len(container)
                                 # child may have classes like "comment" that must be added to transcluded element
                                 classes = child.attrib.get(moin_page.class_, '').split()
-                                # this must be html, not moin_page:
+                                # must use moin_page.class_ above, but use html.class below per html_out.py code
                                 classes += ret_container[trans_ptr].attrib.get(html.class_, '').split()
-                                # this must be html, not moin_page:
                                 ret_container[trans_ptr].attrib[html.class_] = ' '.join(classes)
                                 container.append(ret_container[trans_ptr])  # the transclusion
                                 if len(after):
                                     container.append(after)
                                 return [container, new_trans_ptr]
                             else:
-                                # elem is a block element,
+                                # elem is a block element
+                                for grandchild in child:
+                                    if isinstance(grandchild, ET.Node) and grandchild.tag.name == u'include':
+                                        # the include may have classes that must be added to transcluded element
+                                        classes = grandchild.attrib.get(html.class_, '').split()
+                                        classes += ret_container[trans_ptr].attrib.get(html.class_, '').split()
+                                        ret_container[trans_ptr].attrib[html.class_] = ' '.join(classes)
                                 # replace child element with the container generated in lower recursion
                                 elem[i:i + 1] = ret_container  # elem[i] is the child
-                                # avoid duplicate recursion over nodes already processed
-                                i += len(ret_container) - 1
                         else:
                             # default action for any ret not fitting special cases above,
                             # e.g. tranclusion is within a table cell
     def __call__(self, tree):
         self.stack = []
         self.recurse(tree, None)
-
         return tree
 
 

MoinMoin/converter/markdown_in.py

     symmetric_tags = set(['div', 'p', 'strong', 'code', 'quote', 'blockquote'])
 
     # HTML tags to define a list, except dl which is a little bit different
-    list_tags = set(['ul', 'dir', 'ol'])
+    list_tags = set(['ul', 'ol'])
 
     # HTML tags which can be convert without attributes in a different DOM tag
     simple_tags = {  # Emphasis
-        'em': moin_page.emphasis, 'i': moin_page.emphasis,
+        'em': moin_page.emphasis,
+        'i': moin_page.emphasis,
         # Strong
-        'b': moin_page.strong, 'strong': moin_page.strong,
+        'b': moin_page.strong,
+        'strong': moin_page.strong,
         # Code and Blockcode
-        'pre': moin_page.blockcode, 'tt': moin_page.code,
+        'pre': moin_page.blockcode,
+        'tt': moin_page.code,
         'samp': moin_page.code,
         # Lists
-        'dt': moin_page.list_item_label, 'dd': moin_page.list_item_body,
-        # TODO : Some tags related to tables can be also simplify
+        'dl': moin_page.list_item,
+        'dt': moin_page.list_item_label,
+        'dd': moin_page.list_item_body,
+        # Table - th and td require special processing for alignment of cell contents
+        'table': moin_page.table,
+        'thead': moin_page.table_header,
+        'tbody': moin_page.table_body,
+        'tr': moin_page.table_row,
     }
 
     # HTML Tag which does not have equivalence in the DOM Tree
         attrib[key] = 'line-through'
         return self.new_copy(moin_page.span, element, attrib)
 
-    def visit_hr(self, element, min_class=u'moin-hr1', max_class=u'moin-hr6', default_class=u'moin-hr3'):
-        hr_class = element.attrib.get('class')
-        if not (min_class <= hr_class <= max_class):
-            element.attrib[html('class')] = default_class
-        return self.new_copy(moin_page.separator, element, {})
+    def visit_hr(self, element, default_class=u'moin-hr3'):
+        return self.new_copy(moin_page.separator, element, {moin_page.class_: default_class})
 
     def visit_img(self, element):
         """
         """
         attrib = {}
         url = Iri(element.attrib.get('src'))
+        if element.attrib.get('alt'):
+            attrib[html.alt] = element.attrib.get('alt')
         if url.scheme is None:
             # img tag
-            attrib[html.alt] = element.attrib.get('alt', '')
             target = Iri(scheme='wiki.local', path=element.attrib.get("src"), fragment=None)
             attrib[xinclude.href] = target
             new_node = xinclude.include(attrib=attrib)
         else:
             # object tag
             attrib[xlink.href] = url
-            new_node = moin_page.object(attrib, children=[element.attrib.get('alt', '')])
+            new_node = moin_page.object(attrib)
         return new_node
 
     def visit_object(self, element):
         For some specific inline tags (defined in inline_tags)
         We just return <span element="tag.name">
         """
-        key = html('class')
+        key = html.class_
         attrib = {}
         attrib[key] = ''.join(['html-', element.tag.name])
         return self.new_copy(moin_page.span, element, attrib)
             return href
         return self.new_copy(moin_page.a, element, attrib)
 
+    def convert_align_to_class(self, attrib):
+        attr = {}
+        alignment = attrib.get('align')
+        if alignment in (u'right', u'center', u'left'):
+            attr[moin_page.class_] = alignment
+        return attr
+
+    def visit_th(self, element):
+        attrib = self.convert_align_to_class(element.attrib)
+        return self.new_copy(html.th, element, attrib=attrib)
+
+    def visit_td(self, element):
+        attrib = self.convert_align_to_class(element.attrib)
+        return self.new_copy(html.td, element, attrib=attrib)
+
     def visit(self, element):
         # Our element can be converted directly, just by changing the namespace
         if element.tag in self.symmetric_tags:
 
     def do_children(self, element, add_lineno=False):
         new = []
-        if hasattr(element, "text") and element.text is not None:
+        # markdown parser surrounds child nodes with u"\n" children, we ignore them here to avoid the issue in html_out.py
+        if hasattr(element, "text") and element.text is not None and element.text != u'\n':
             new.append(postproc_text(self.markdown, element.text))
 
         for child in element:
                     r.attrib[html.data_lineno] = self.line_numbers.popleft()
                 r = (r, )
             new.extend(r)
-            if hasattr(child, "tail") and child.tail is not None:
+            if hasattr(child, "tail") and child.tail is not None and child.tail != u'\n':
                 new.append(postproc_text(self.markdown, child.tail))
         return new
 

MoinMoin/converter/moinwiki_in.py

 
         query_keys = {}
         attrib = {}
-        whitelist = ['width', 'height']
+        whitelist = ['width', 'height', 'class']
         for attr, value in args.iteritems():
             if attr.startswith('&'):
                 query_keys[attr[1:]] = value
             elif attr in whitelist:
                 attrib[html(attr)] = value
-
+        if object_text:
+            attrib[html.alt] = object_text
         if object_item is not None:
             # img tag
             query = url_encode(query_keys, charset=CHARSET, encode_keys=True)
                 object_item = '/' + object_item[len(att):]  # now we have a subitem
             target = Iri(scheme='wiki.local', path=object_item, query=query, fragment=None)
             attrib[xinclude.href] = target
-            if object_text:
-                attrib[html.alt] = object_text
             element = xinclude.include(attrib=attrib)
             stack.top_append(element)
         else:
             # object tag
             target = Iri(object_url)
-            text = object_url
             attrib[xlink.href] = target
             element = moin_page.object(attrib)
-            stack.push(element)
-            if object_text:
-                self.parse_inline(object_text, stack, self.inlinedesc_re)
-            else:
-                stack.top_append(text)
-            stack.pop()
+            stack.top_append(element)
 
     table = block_table
 

MoinMoin/converter/moinwiki_out.py

 
 from __future__ import absolute_import, division
 
-from MoinMoin.util.tree import moin_page, xlink
+from MoinMoin.util.tree import moin_page, xlink, xinclude, html
+from MoinMoin.util.iri import Iri
 
 from emeraldtree import ElementTree as ET
 
-from re import findall
+from re import findall, sub
 
 from werkzeug.utils import unescape
 
     Converter application/x.moin.document -> text/x.moin.wiki
     """
     namespaces = {
-        moin_page.namespace: 'moinpage'}
+        moin_page.namespace: 'moinpage',
+        xinclude: 'xinclude',
+    }
 
     supported_tag = {
         'moinpage': (
             'object',
             'table',
             'table_header',
-            'teble_footer',
+            'table_footer',
             'table_body',
             'table_row',
             'table_cell',
-        )
+        ),
+        'xinclude': (
+            'include',
+        ),
     }
 
     @classmethod
             f = getattr(self, n, None)
             if f is not None:
                 return f(elem)
-        return open_children(elem)
+        # process odd things like xinclude
+        return self.open_children(elem)
 
     def open_moinpage(self, elem):
         n = 'open_moinpage_' + elem.tag.name.replace('-', '_')
         return u''
 
     def open_moinpage_object(self, elem):
-        # TODO: this can be done with one regex:
-        href = elem.get(xlink.href, u'')
-        # XXX: We don't have Iri support for now
-        from MoinMoin.util.iri import Iri
+        """Process objects and xincludes."""
+        href = elem.get(xlink.href, elem.get(xinclude.href, u''))
         if isinstance(href, Iri):
             href = unicode(href)
         href = href.split(u'?')
         if len(href) > 1:
             args = u' '.join([s for s in findall(r'(?:^|;|,|&|)(\w+=\w+)(?:,|&|$)', href[1]) if s[:3] != u'do='])
         href = href[0].split(u'wiki.local:')[-1]
-        # TODO: add '|' to Moinwiki class and rewrite this using % formatting
-        ret = Moinwiki.object_open
-        ret += href
-        alt = elem.get(moin_page.alt, u'')
-        if alt and alt != href:
-            # TODO: this will fail on: {{png||width=100}}
-            ret += u'|' + alt
-            if args:
-                ret += u'|' + args
-        ret += Moinwiki.object_close
+
+        if len(elem) and isinstance(elem[0], unicode):
+            # alt text for objects is enclosed within <object...>...</object>
+            alt = elem[0]
+        else:
+            alt = elem.attrib.get(html.alt, u'')
+
+        whitelist = {html.width: 'width', html.height: 'height', html.class_: 'class'}
+        options = []
+        for attr, value in elem.attrib.items():
+            if attr in whitelist.keys():
+                options.append('{0}="{1}"'.format(whitelist[attr], value))
+
+        if args:
+            args = u'&' + args
+        args += u' '.join(options)
+
+        ret = '{0}{1}|{2}|{3}{4}'.format(Moinwiki.object_open, href, alt, args, Moinwiki.object_close)
+        ret = sub(r"\|+}}", "}}", ret)
         return ret
 
     def open_moinpage_p(self, elem):
     def open_moinpage_table_of_content(self, elem):
         return u"<<TableOfContents({0})>>\n".format(elem.get(moin_page.outline_level, u""))
 
+    def open_xinclude(self, elem):
+        """Processing of transclusions is similar to objects."""
+        return self.open_moinpage_object(elem)
+
+
 from . import default_registry
 from MoinMoin.util.mime import Type, type_moin_document, type_moin_wiki
 default_registry.register(Converter.factory, type_moin_document, type_moin_wiki)

MoinMoin/converter/rst_in.py

         """
         Processes images and other transcluded objects.
         """
-        # TODO: ReST also defines "align" as a parameter, but it is invalid in HTML5.  Depending upon value
-        # of align, we could add a style attribute of either:
-        #   vertical-align: middle | top | bottom
-        #   float: left | right
         whitelist = ['width', 'height', 'alt', ]
         attrib = {}
         for key in whitelist:
                 if html(key) in attrib:
                     attrib[html(key)] = int(int(attrib[html(key)]) * scaling_factor)
 
+        # "align" parameter is invalid in HTML5. Convert it to a class defined in userstyles.css.
+        userstyles = {
+            'left': 'left',
+            'center': 'center',
+            'right': 'right',
+            'top': 'top',  # rst parser creates error messages for top, bottom, and middle
+            'bottom': 'bottom',
+            'middle': 'middle',
+        }
+        alignment = userstyles.get(node.get('align'))
+        if alignment:
+            attrib[html.class_] = alignment
+
         url = Iri(node['uri'])
         if url.scheme is None:
             # img
             new_node = xinclude.include(attrib=attrib)
         else:
             # obj
-            # TODO: alt is set above, OK on img tags, but invalid here on object tags, it needs to be a text child of object tag for present code in html_out.py
-            # this should be handled consistently for rest, moinwiki, markdown, mediawiki...
             new_node = moin_page.object(attrib)
             new_node.set(xlink.href, url)
 

MoinMoin/static/js/common.js

 // Transclusion initialization is executed once after document ready.
 MoinMoin.prototype.initTransclusionOverlays = function () {
     "use strict";
-    var elem, overlayUL, overlayLR, wrapper, wrappers, transclusions,
+    var elem, overlayUL, overlayLR, wrapper, wrappers, transclusions, classes,
         rightArrow = '\u2192';
     // get list of elements to be wrapped; must work in reverse order in case there are nested transclusions
     transclusions = $($('.moin-transclusion').get().reverse());
             if ($(elem).parent()[0].tagName === 'A') {
                 elem = $(elem).parent()[0];
             }
+            // copy user specified classes from img/object tag to wrapper
+            if (elem.tagName === 'OBJECT') {
+                // do not copy classes starting with moin-
+                classes = $(elem).attr('class');
+                classes = classes.split(" ").filter(function(c) {
+                    return c.lastIndexOf('moin-', 0) !== 0;
+                });
+                $(wrapper).addClass(classes.join(' '));
+            } else {
+                // copy all classes from img tags
+                $(wrapper).addClass($(elem).find(">:first-child").attr('class'));
+            }
             // insert wrapper after elem, append (move) elem, append overlays
             $(elem).after(wrapper);
             $(wrapper).append(elem);
 
     moin.selected_link();
     moin.initTransclusionOverlays();
-    moin.QuicklinksExpander();
+    if(document.getElementById('moin-navibar')!=null) {
+        moin.QuicklinksExpander();
+    }
 
     $('.moin-insertname-action').click(function () {
         var fullname = $(this).data('name');
         moin.toggleSubtree(this);
     });
 
+    $('.moin-useractions').click(function () {
+        $('#user-actions').toggleClass('hidden');
+        return false;
+    });
+
+    $('.moin-viewoptions').click(function () {
+        $('#view-options').toggleClass('hidden');
+        return false;
+    });
+
+    $('.moin-itemactions').click(function () {
+        $('#item-actions').toggleClass('hidden');
+        return false;
+    });
+
     moin.enhanceUserSettings();
     moin.enhanceEdit();
     // placing initToggleComments after enhanceEdit prevents odd autoscroll issue when editing hidden comments

MoinMoin/themes/basic/static/css/basic.css

 .list-group-item {
   position: relative;
   display: block;
-  padding: 10px 15px;
+  padding: 5px 15px;
   margin-bottom: -1px;
   background-color: #ffffff;
   border: 1px solid #dddddd;
 .moin-user-action-special {
   position: relative;
   display: block;
-  padding: 10px 15px;
+  padding: 5px 15px;
   margin-bottom: -1px;
   background-color: #ffffff;
   width: 100%;
 }
 .moin-quicklink {
   position: relative;
-  padding: 10px 15px 10px 15px;
+  padding: 5px 15px 5px 15px;
   border: 1px solid #dddddd;
   margin-bottom: -1px;
 }
 ul.moin-breadcrumb li:hover ul.moin-bcs-subitems {
   display: block;
 }
+.moin-thead {
+  background-color: silver;
+}
+.moin-viewoptions, .moin-itemactions, .moin-useractions {
+  float: right;
+}
+.list-group-item {
+  padding: 5px 15px;
+}

MoinMoin/themes/basic/static/custom-less/basic.less

   }
   border: 1px solid @list-group-border;
   border-radius: 0;
+  padding: 5px 15px;
+}
+
+.moin-viewoptions, .moin-itemactions, .moin-useractions {
+  float: right;
+}
+
+.list-group-item {
+  padding: 5px 15px;
 }
 
 .moin-diff-added {
 
 .moin-quicklink {
   position: relative;
-  padding: 10px 15px 10px 15px;
+  padding: 5px 15px 5px 15px;
   border: 1px solid @list-group-border;
   margin-bottom: -1px;
 
 ul.moin-breadcrumb li:hover ul.moin-bcs-subitems{
   display: block;
 }
+.moin-thead{
+    background-color: silver;
+}

MoinMoin/themes/basic/templates/show.html

         <!-- user, site, item actions -->
     <div class="list-group">
         {% if user_actions %}
-            <div class="list-group-item moin-list-heading"> {{ _("User Actions") }} </div>
-            {{ self.user_actions() }}
+            <div class="list-group-item moin-list-heading">
+                {{ _("User Actions") }}
+                <a class="moin-showhide moin-useractions" href="#" >[+]</a>
+            </div>
+            <div id="user-actions" class="hidden">{{ self.user_actions() }}</div>
         {% endif %}
 
-        <div class="list-group-item moin-list-heading">View Options</div>
-        <button type="button"
-                class="btn btn-default moin-user-action-special moin-toggle-comments-button"
-                data-toggle="button"
-                title="Show Comments">
-            <span class="pull-left">
-                <i class="icon-comment icon-fixed-width"></i>
-                Comments
-            </span>
-        </button>
-        <button type="button"
-                class="btn btn-default moin-user-action-special moin-transclusions-button"
-                data-toggle="button"
-                title="Show Transclusions">
-            <span class="pull-left">
-                <i class="icon-edit icon-fixed-width"></i>
-                Transclusions
-            </span>
-        </button>
+        <div class="list-group-item moin-list-heading">
+            View Options
+            <a class="moin-showhide moin-viewoptions" href="#">[+]</a>
+        </div>
+        <div id="view-options" class="hidden">
+            <button type="button"
+                    class="btn btn-default moin-user-action-special moin-toggle-comments-button"
+                    data-toggle="button"
+                    title="Show Comments">
+                <span class="pull-left">
+                    <i class="icon-comment icon-fixed-width"></i>
+                    Comments
+                </span>
+            </button>
+            <button type="button"
+                    class="btn btn-default moin-user-action-special moin-transclusions-button"
+                    data-toggle="button"
+                    title="Show Transclusions">
+                <span class="pull-left">
+                    <i class="icon-edit icon-fixed-width"></i>
+                    Transclusions
+                </span>
+            </button>
+        </div>
 
         {% if item_actions %}
-            <div class="list-group-item moin-list-heading"> {{ _("Item Actions") }} </div>
-            {{ self.item_actions() }}
+            <div class="list-group-item moin-list-heading">
+                {{ _("Item Actions") }}
+                <a class="moin-showhide moin-itemactions" href="#" >[+]</a>
+            </div>
+            <div id="item-actions" class="hidden">{{ self.item_actions() }}</div>
         {% endif %}
     </div>
 

MoinMoin/themes/foobar/static/css/common.css

 article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
 section { display: block; }
 nav ul { list-style: none; }
-blockquote, q { quotes: none; }
-blockquote:before, blockquote:after, q:before, q:after { content: none; }
+blockquote { quotes: none; }
+blockquote:before, blockquote:after { content: none; }
 a { margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent; }
 ins { background-color: #ff9; color: #000; text-decoration: none; }
 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
 .right { text-align: right; }
 .justify { text-align: justify; }
 .monospaced { font-family: monospace; }
+/* position image or object - the left, center, right rules above are either consistent with or harmless to the rules below */
+.moin-item-wrapper.left { float: left; }
+.moin-item-wrapper.center { width: 100%; text-align: center; }
+.moin-item-wrapper.right { float: right; }
+.moin-item-wrapper.middle { vertical-align: middle; }
+.moin-item-wrapper.top { vertical-align: top; }
+.moin-item-wrapper.bottom { vertical-align: bottom; }
 a.moin-www:before,a.moin-http:before,a.moin-https:before{content:url("../img/moin-www.png");margin:0 .2em;vertical-align:middle}
 a.moin-file:before,a.moin-ftp:before{content:url("../img/moin-ftp.png");margin:0 .2em;vertical-align:middle}
 a.moin-nntp:before,a.moin-news:before{content:url("../img/moin-news.png");margin:0 .2em;vertical-align:middle}
 ol.moin-loweralpha-list{list-style-type:lower-alpha}
 ol.moin-upperroman-list{list-style-type:upper-roman}
 ol.moin-lowerroman-list{list-style-type:lower-roman}
+blockquote{margin-left:2em}
 ul.moin-tags{list-style:none;}
 ul.moin-tags li{display:inline;}
 ul.moin-tags li.weight0{font-size:.4822530864197531em}
 table th,table td{padding:.3em .4em;vertical-align:middle;text-align:left}
 table th{border:1px solid #ccc;background-color:#808080;color:#fff}
 table td{border:1px solid #ccc}
+.moin-thead{background-color:#808080}
 table.zebra{border:1px solid #ccc;border-collapse:separate;border-spacing:1px;width:98%;border-radius:6px;}
 table.zebra thead{background-color:#808080;color:#fff}
 table.zebra tfoot{background-color:#d6d5d0}

MoinMoin/themes/foobar/static/css/stylus/main.styl

     &.moin-lowerroman-list
         list-style-type lower-roman
 
+blockquote
+    margin-left 2em
+
 // tags list / tag cloud
 tag_scale = 1.2
 tag_font_size = 1em
     td
         border border_style
 
+.moin-thead
+    background-color table_header_color
+
 table.zebra
     border border_style
     border-collapse separate

MoinMoin/themes/foobar/static/css/stylus/reset.css

 article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
 section { display: block; }
 nav ul { list-style: none; }
-blockquote, q { quotes: none; }
-blockquote:before, blockquote:after, q:before, q:after { content: none; }
+blockquote { quotes: none; }
+blockquote:before, blockquote:after { content: none; }
 a { margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent; }
 ins { background-color: #ff9; color: #000; text-decoration: none; }
 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }

MoinMoin/themes/foobar/static/css/stylus/userstyles.css

 .right { text-align: right; }
 .justify { text-align: justify; }
 .monospaced { font-family: monospace; }
+/* position image or object - the left, center, right rules above are either consistent with or harmless to the rules below */
+.moin-item-wrapper.left { float: left; }
+.moin-item-wrapper.center { width: 100%; text-align: center; }
+.moin-item-wrapper.right { float: right; }
+.moin-item-wrapper.middle { vertical-align: middle; }
+.moin-item-wrapper.top { vertical-align: top; }
+.moin-item-wrapper.bottom { vertical-align: bottom; }

MoinMoin/themes/modernized/static/css/common.css

 article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
 section { display: block; }
 nav ul { list-style: none; }
-blockquote, q { quotes: none; }
-blockquote:before, blockquote:after, q:before, q:after { content: none; }
+blockquote { quotes: none; }
+blockquote:before, blockquote:after { content: none; }
 a { margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent; }
 ins { background-color: #ff9; color: #000; text-decoration: none; }
 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
 .right { text-align: right; }
 .justify { text-align: justify; }
 .monospaced { font-family: monospace; }
+/* position image or object - the left, center, right rules above are either consistent with or harmless to the rules below */
+.moin-item-wrapper.left { float: left; }
+.moin-item-wrapper.center { width: 100%; text-align: center; }
+.moin-item-wrapper.right { float: right; }
+.moin-item-wrapper.middle { vertical-align: middle; }
+.moin-item-wrapper.top { vertical-align: top; }
+.moin-item-wrapper.bottom { vertical-align: bottom; }
 a.moin-www:before,a.moin-http:before,a.moin-https:before{content:url("../img/moin-www.png");margin:0 .2em;vertical-align:middle}
 a.moin-file:before,a.moin-ftp:before{content:url("../img/moin-ftp.png");margin:0 .2em;vertical-align:middle}
 a.moin-nntp:before,a.moin-news:before{content:url("../img/moin-news.png");margin:0 .2em;vertical-align:middle}
 dd{font-size:.92em;margin-left:40px;margin-top:0;margin-bottom:0}
 dd p{margin:.25em 0}
 dd + dd p{margin-top:.67em}
+blockquote{margin-left:2em}
 hr{height:1px;background-color:#4e7da9;border:0;margin:.9em 0}
 .moin-hr1{height:2px}
 .moin-hr2{height:3px}
 th{border:1px solid #4e7da9;background-color:#708090;text-align:center;color:#d8dfe9}
 td{border:1px solid #4e7da9}
 td p{margin:0;padding:0}
+.moin-thead{background-color:#708090}
 table.zebra{border-collapse:separate;border-spacing:1px;width:98%;border:1px solid #4e7da9;border-radius:6px}
 .zebra thead{background-color:#708090}
 .zebra tfoot{background-color:#dfdfdf}
 ul.moin-bcs-item li{position:relative}
 ul.moin-bcs-item li ul.moin-bcs-subitems{display:none;position:absolute}
 ul.moin-bcs-item li:hover ul.moin-bcs-subitems{display:block}
-

MoinMoin/themes/modernized/static/css/stylus/main.styl

 dd + dd p
     margin-top .67em
 
+blockquote
+    margin-left 2em
+
 hr
     height 1px
     background-color border_color
     margin 0
     padding 0
 
+.moin-thead
+    background-color header_bg_color
+
 table.zebra
     border-collapse separate
     border-spacing 1px

MoinMoin/themes/modernized/static/css/stylus/reset.css

 article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
 section { display: block; }
 nav ul { list-style: none; }
-blockquote, q { quotes: none; }
-blockquote:before, blockquote:after, q:before, q:after { content: none; }
+blockquote { quotes: none; }
+blockquote:before, blockquote:after { content: none; }
 a { margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent; }
 ins { background-color: #ff9; color: #000; text-decoration: none; }
 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }

MoinMoin/themes/modernized/static/css/stylus/userstyles.css

 .right { text-align: right; }
 .justify { text-align: justify; }
 .monospaced { font-family: monospace; }
+/* position image or object - the left, center, right rules above are either consistent with or harmless to the rules below */
+.moin-item-wrapper.left { float: left; }
+.moin-item-wrapper.center { width: 100%; text-align: center; }
+.moin-item-wrapper.right { float: right; }
+.moin-item-wrapper.middle { vertical-align: middle; }
+.moin-item-wrapper.top { vertical-align: top; }
+.moin-item-wrapper.bottom { vertical-align: bottom; }

contrib/serialized/items.moin

Binary file modified.