changeset 407:bea43f99cc27

star-merged moin--refactor--1.5 (formatter, parser, converter fixes, see docs/CHANGES.refactor) Patches applied: * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--base-0 tag of arch@arch.thinkmo.de--2003-archives/moin--main--1.5--patch-399 * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-1 CHANGES.refactor file to read and use here * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-2 change version to 1.5.refactor * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-3 deron meranda's formatter-patch-r4.diff * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-4 PEP8 whitespace changes, fixed typos, comments, NO non-trivial code changes * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-5 text_html: use python2.3 sets instead of own code * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-6 some src cosmetics, removed unused code, kwargs -> kw * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-7 make prettyprinting and lineanchors configurable (see TOF), optimize, fix typos * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-8 revert text_gedit's invalid list nesting * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-9 converter: fixed nested lists * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-10 converter: made dl processing similar to ul * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-11 fixed hr crash in blockquote, but needs more fixes to work correctly * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-12 html formatter cleanup and extension (div, span), fix FootNote action, fix rst headline levels * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-13 use formatter.{span,div} * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-14 disabled xml:lang stuff, src whitespace cosmetics * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-15 fix for having multiple p in one li * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-16 fix _open/_close calls for text_gedit, new . bulletless list markup * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-17 updated CHANGES * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-18 fixed MoinMoinBugs/WikiParserThinksItIsInsidePreWhenItIsNot * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-19 allow hex and symbolic entities * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-20 removed some unused code, src cosmetics * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-21 fixed MoinMoinBugs/ListItemGeneratedOutsideList * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-22 fix for wrong p after macro * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-23 html formatter now know about blockquote being a block element * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-24 GUI editor converter now also accepts relative/same server http: URLs imported from: moin--main--1.5--patch-411
author Thomas Waldmann <tw@waldmann-edv.de>
date Tue, 31 Jan 2006 21:17:13 +0000
parents 0ff631c36afb
children bf40a8d29393
files ChangeLog MoinMoin/PageEditor.py MoinMoin/converter/text_html_text_x_moin.py MoinMoin/formatter/__init__.py MoinMoin/formatter/base.py MoinMoin/formatter/dom_xml.py MoinMoin/formatter/pagelinks.py MoinMoin/formatter/text_gedit.py MoinMoin/formatter/text_html.py MoinMoin/formatter/text_plain.py MoinMoin/formatter/text_python.py MoinMoin/formatter/text_xml.py MoinMoin/formatter/xml_docbook.py MoinMoin/macro/FootNote.py MoinMoin/macro/Include.py MoinMoin/parser/python.py MoinMoin/parser/rst.py MoinMoin/parser/wiki.py MoinMoin/search.py MoinMoin/version.py MoinMoin/wikimacro.py docs/CHANGES.refactor wiki/htdocs/classic/css/common.css wiki/htdocs/modern/css/common.css wiki/htdocs/rightsidebar/css/common.css
diffstat 25 files changed, 1346 insertions(+), 598 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jan 31 21:09:45 2006 +0000
+++ b/ChangeLog	Tue Jan 31 21:17:13 2006 +0000
@@ -2,6 +2,142 @@
 # arch-tag: automatic-ChangeLog--arch@arch.thinkmo.de--2003-archives/moin--main--1.5
 #
 
+2006-01-31 22:17:13 GMT	Thomas Waldmann <tw@waldmann-edv.de>	patch-411
+
+    Summary:
+      star-merged moin--refactor--1.5 (formatter, parser, converter fixes, see docs/CHANGES.refactor)
+    Revision:
+      moin--main--1.5--patch-411
+
+    star-merged moin--refactor--1.5 (formatter, parser, converter fixes, see docs/CHANGES.refactor)
+    
+    Patches applied:
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--base-0
+       tag of arch@arch.thinkmo.de--2003-archives/moin--main--1.5--patch-399
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-1
+       CHANGES.refactor file to read and use here
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-2
+       change version to 1.5.refactor
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-3
+       deron meranda's formatter-patch-r4.diff
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-4
+       PEP8 whitespace changes, fixed typos, comments, NO non-trivial code changes
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-5
+       text_html: use python2.3 sets instead of own code
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-6
+       some src cosmetics, removed unused code, kwargs -> kw
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-7
+       make prettyprinting and lineanchors configurable (see TOF), optimize, fix typos
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-8
+       revert text_gedit's invalid list nesting
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-9
+       converter: fixed nested lists
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-10
+       converter: made dl processing similar to ul
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-11
+       fixed hr crash in blockquote, but needs more fixes to work correctly
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-12
+       html formatter cleanup and extension (div, span), fix FootNote action, fix rst headline levels
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-13
+       use formatter.{span,div}
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-14
+       disabled xml:lang stuff, src whitespace cosmetics
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-15
+       fix for having multiple p in one li
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-16
+       fix _open/_close calls for text_gedit, new . bulletless list markup
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-17
+       updated CHANGES
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-18
+       fixed MoinMoinBugs/WikiParserThinksItIsInsidePreWhenItIsNot
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-19
+       allow hex and symbolic entities
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-20
+       removed some unused code, src cosmetics
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-21
+       fixed MoinMoinBugs/ListItemGeneratedOutsideList
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-22
+       fix for wrong p after macro
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-23
+       html formatter now know about blockquote being a block element
+    
+     * arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-24
+       GUI editor converter now also accepts relative/same server http: URLs
+    
+
+    new files:
+     docs/.arch-ids/CHANGES.refactor.id docs/CHANGES.refactor
+
+    modified files:
+     ChangeLog MoinMoin/PageEditor.py
+     MoinMoin/converter/text_html_text_x_moin.py
+     MoinMoin/formatter/__init__.py MoinMoin/formatter/base.py
+     MoinMoin/formatter/dom_xml.py MoinMoin/formatter/pagelinks.py
+     MoinMoin/formatter/text_gedit.py
+     MoinMoin/formatter/text_html.py
+     MoinMoin/formatter/text_plain.py
+     MoinMoin/formatter/text_python.py
+     MoinMoin/formatter/text_xml.py
+     MoinMoin/formatter/xml_docbook.py MoinMoin/macro/FootNote.py
+     MoinMoin/macro/Include.py MoinMoin/parser/python.py
+     MoinMoin/parser/rst.py MoinMoin/parser/wiki.py
+     MoinMoin/search.py MoinMoin/version.py MoinMoin/wikimacro.py
+     wiki/htdocs/classic/css/common.css
+     wiki/htdocs/modern/css/common.css
+     wiki/htdocs/rightsidebar/css/common.css
+
+    new patches:
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--base-0
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-1
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-2
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-3
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-4
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-5
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-6
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-7
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-8
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-9
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-10
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-11
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-12
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-13
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-14
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-15
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-16
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-17
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-18
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-19
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-20
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-21
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-22
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-23
+     arch@arch.thinkmo.de--2003-archives/moin--refactor--1.5--patch-24
+
+
 2006-01-31 22:09:45 GMT	Thomas Waldmann <tw@waldmann-edv.de>	patch-410
 
     Summary:
--- a/MoinMoin/PageEditor.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/PageEditor.py	Tue Jan 31 21:17:13 2006 +0000
@@ -402,14 +402,16 @@
         self.request.write("</form>")
         
         # QuickHelp originally by Georg Mischler <schorsch@lightingwiki.com>
-        self.request.write('<div id="editor-help">\n' + _(""" Emphasis:: [[Verbatim('')]]''italics''[[Verbatim('')]]; [[Verbatim(''')]]'''bold'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''bold italics'''''[[Verbatim(''''')]]; [[Verbatim('')]]''mixed ''[[Verbatim(''')]]'''''bold'''[[Verbatim(''')]] and italics''[[Verbatim('')]]; [[Verbatim(----)]] horizontal rule.
+        self.request.write(self.request.formatter.div(1, id="editor-help"))
+        self.request.write(_(""" Emphasis:: [[Verbatim('')]]''italics''[[Verbatim('')]]; [[Verbatim(''')]]'''bold'''[[Verbatim(''')]]; [[Verbatim(''''')]]'''''bold italics'''''[[Verbatim(''''')]]; [[Verbatim('')]]''mixed ''[[Verbatim(''')]]'''''bold'''[[Verbatim(''')]] and italics''[[Verbatim('')]]; [[Verbatim(----)]] horizontal rule.
  Headings:: [[Verbatim(=)]] Title 1 [[Verbatim(=)]]; [[Verbatim(==)]] Title 2 [[Verbatim(==)]]; [[Verbatim(===)]] Title 3 [[Verbatim(===)]];   [[Verbatim(====)]] Title 4 [[Verbatim(====)]]; [[Verbatim(=====)]] Title 5 [[Verbatim(=====)]].
  Lists:: space and one of: * bullets; 1., a., A., i., I. numbered items; 1.#n start numbering at n; space alone indents.
  Links:: [[Verbatim(JoinCapitalizedWords)]]; [[Verbatim(["brackets and double quotes"])]]; url; [url]; [url label].
  Tables:: || cell text |||| cell text spanning 2 columns ||;    no trailing white space allowed after tables or titles.
 
 (!) For more help, see HelpOnEditing or SyntaxReference.
-""") + '\n</div>\n')
+"""))
+        self.request.write(self.request.formatter.div(0))
 
         if preview is not None:
             if staytop:
--- a/MoinMoin/converter/text_html_text_x_moin.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/converter/text_html_text_x_moin.py	Tue Jan 31 21:17:13 2006 +0000
@@ -537,24 +537,6 @@
     def visit_text(self, node):
         self.text.append(node.data)
 
-    def process_dl(self, node):
-        self.depth += 1
-        indent = " " * self.depth
-        for i in node.childNodes:
-            if i.nodeType == Node.ELEMENT_NODE:
-                if i.localName == 'dt':
-                    self.text.append(indent)
-                    text = self.node_list_text_only(i.childNodes)
-                    self.text.append(text.replace("\n", " "))
-                elif i.localName == 'dd':
-                    self.text.append(":: ")
-                    self.process_list_item(i)
-                else:
-                    raise ConvertError("Illegal list element %s" % i.localName)
-        if self.depth == 1:
-            self.text.append("\n")
-        self.depth -= 1
-
     def process_heading(self, node):
         text = self.node_list_text_only(node.childNodes).strip()
         if text:
@@ -565,22 +547,54 @@
             self.text.append(self.new_line)
 
     def _get_list_item_markup(self, list, listitem):
-        markup = " " * self.depth
-        type = None
-        if list.localName == 'ol':
+        before = ""
+        #indent = str(self.depth) * self.depth # nice for debugging :)
+        indent = " " * self.depth
+        markup = ""
+        name = list.localName
+        if name == 'ol':
+            class_ = listitem.getAttribute("class")
+            if class_ == "gap":
+                before = "\n"
             if list.hasAttribute("type"):
                 type = list.getAttribute("type")
             else:
                 type = "1"
-            markup = "%s%s. " % (markup, type)
-        else:
+            markup = "%s. " % type
+        elif name == 'ul':
             class_ = listitem.getAttribute("class")
             if class_ == "gap":
-                markup = "\n" + markup
+                before = "\n"
             style = listitem.getAttribute("style")
-            if not re.match(u"list-style-type:\s*none", style, re.I):
-                markup += "* "
-        return markup
+            if re.match(u"list-style-type:\s*none", style, re.I):
+                markup = ". "
+            else:
+                markup = "* "
+        elif name == 'dl':
+            markup = ":: "
+        else:
+            raise ConvertError("Illegal list type %s" % name)
+        return before, indent, markup
+
+    def process_dl(self, node):
+        self.depth += 1
+        markup = ":: " # can there be a dl dd without dt?
+        for i in node.childNodes:
+            if i.nodeType == Node.ELEMENT_NODE:
+                name = i.localName
+                if name == 'dt':
+                    before, indent, markup = self._get_list_item_markup(node, i)
+                    self.text.append(before+indent)
+                    text = self.node_list_text_only(i.childNodes)
+                    self.text.append(text.replace("\n", " "))
+                elif name == 'dd':
+                    self.text.append(markup)
+                    self.process_list_item(i, indent)
+                else:
+                    raise ConvertError("Illegal list element %s" % i.localName)
+        self.depth -= 1
+        if self.depth == 0:
+            self.text.append("\n")
 
     def process_list(self, node):
         self.depth += 1
@@ -588,27 +602,33 @@
             if i.nodeType == Node.ELEMENT_NODE:
                 name = i.localName
                 if name == 'li':
-                    self.text.append(self._get_list_item_markup(node, i))
-                    self.process_list_item(i)
+                    before, indent, markup = self._get_list_item_markup(node, i)
+                    self.text.append(before+indent+markup)
+                    self.process_list_item(i, indent)
                 elif name in ('ol', 'ul',):
                     self.process_list(i)
                 elif name == 'dl':
                     self.process_dl(i)
                 else:
                     raise ConvertError("Illegal list element %s" % i.localName)
-        if self.depth == 1:
-            self.text.append("\n")
         self.depth -= 1
+        if self.depth == 0:
+            self.text.append("\n")
 
-    def process_list_item(self, node):
+    def process_list_item(self, node, indent):
         found = False
+        first_child = True
         for i in node.childNodes:
             name = i.localName
             if name == 'p':
+                if not first_child:
+                    self.text.append(indent)
                 self.process_paragraph_item(i)
                 self.text.append("\n")
                 found = True
             elif name == 'pre':
+                if not first_child:
+                    self.text.append(indent)
                 self.process_preformatted_item(i)
                 found = True
             elif name in ('ol', 'ul',):
@@ -618,16 +638,23 @@
                 self.process_dl(i)
                 found = True
             elif name == 'table':
+                if not first_child:
+                    self.text.append(indent)
                 self.process_table(i)
                 found = True
             #else:
             #    self.process_inline(i)
+            first_child = False
                 
         if not found:
             self.process_paragraph_item(node)
             self.text.append("\n")
 
     def process_blockquote(self, node):
+        # XXX this does not really work. e.g.:
+        # <bq>aaaaaa
+        # <hr---------->
+        # <bq>bbbbbb
         self.depth += 1
         for i in node.childNodes:
             if i.nodeType == Node.ELEMENT_NODE:
@@ -652,7 +679,9 @@
                     self.visit_node_list_element_only(i.childNodes)
                 elif name == 'blockquote':
                     self.process_blockquote(i)
-                elif name in ('br',):
+                elif name == 'hr':
+                    self.process_hr(i)
+                elif name == 'br':
                     self.process_br(i)
                 else:
                     raise ConvertError("process_blockquote: Don't support %s element" % name)
@@ -1130,7 +1159,7 @@
                     pass #print name, data, filename, alt
             raise ConvertError("Unknown smiley icon '%s'" % filename)
         # Image URL
-        elif src and src.startswith("http://") and wikiutil.isPicture(src):
+        elif src and src.startswith("http:") and wikiutil.isPicture(src):
             self.text.extend([self.white_space, src, self.white_space])
         else:
             raise ConvertError("Strange image src: '%s'" % src)
--- a/MoinMoin/formatter/__init__.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/__init__.py	Tue Jan 31 21:17:13 2006 +0000
@@ -10,3 +10,4 @@
 from MoinMoin.util import pysupport
 
 modules = pysupport.getPackageModules(__file__)
+
--- a/MoinMoin/formatter/base.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/base.py	Tue Jan 31 21:17:13 2006 +0000
@@ -62,7 +62,7 @@
     def endDocument(self):
         return ""
 
-    def startContent(self, content_id="content", **kwargs):
+    def startContent(self, content_id="content", **kw):
         return ""
 
     def endContent(self):
@@ -87,7 +87,7 @@
         # call pagelink() for internal interwikilinks
         # to make shure they get counted for self.pagelinks
         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
-        if wikitag=='Self' or wikitag==self.request.cfg.interwikiname:
+        if wikitag == 'Self' or wikitag == self.request.cfg.interwikiname:
             if wikitail.find('#') > -1:
                 wikitail, kw['anchor'] = wikitail.split('#', 1)
                 wikitail = wikiutil.url_unquote(wikitail)
@@ -128,30 +128,33 @@
 
         return self.attachment_link(url, text)
 
-
     def anchordef(self, name):
         return ""
 
     def line_anchordef(self, lineno):
         return ""
 
-    def anchorlink(self, on, name='', id=None):
+    def anchorlink(self, on, name='', **kw):
         return ""
 
     def line_anchorlink(self, on, lineno=0):
         return ""
 
-    def image(self, **kw):
-        """ Take HTML <IMG> tag attributes in `attr`.
+    def image(self, src=None, **kw):
+        """An inline image.
 
-        Attribute names have to be lowercase!
+        Extra keyword arguments are according to the HTML <img> tag attributes.
+        In particular an 'alt' or 'title' argument should give a description
+        of the image.
         """
-        attrstr = u''
-        for attr, value in kw.items():
-            if attr=='html_class':
-                attr='class'
-            attrstr = attrstr + u' %s="%s"' % (attr, wikiutil.escape(value))
-        return u'<img%s>' % attrstr
+        title = src
+        for titleattr in ('title', 'html__title', 'alt', 'html__alt'):
+            if kw.has_key(titleattr):
+                title = kw[titleattr]
+                break
+        if title:
+            return '[Image:%s]' % title
+        return '[Image]'
 
     def smiley(self, text):
         return text
@@ -161,7 +164,7 @@
 
     # Text and Text Attributes ########################################### 
     
-    def text(self, text):
+    def text(self, text, **kw):
         if not self._highlight_re:
             return self._text(text)
             
@@ -185,42 +188,42 @@
     def _text(self, text):
         raise NotImplementedError
 
-    def strong(self, on):
-        raise NotImplementedError
-
-    def emphasis(self, on):
-        raise NotImplementedError
-
-    def underline(self, on):
+    def strong(self, on, **kw):
         raise NotImplementedError
 
-    def highlight(self, on):
+    def emphasis(self, on, **kw):
         raise NotImplementedError
 
-    def sup(self, on):
+    def underline(self, on, **kw):
         raise NotImplementedError
 
-    def sub(self, on):
+    def highlight(self, on, **kw):
         raise NotImplementedError
 
-    def strike(self, on):
+    def sup(self, on, **kw):
+        raise NotImplementedError
+
+    def sub(self, on, **kw):
+        raise NotImplementedError
+
+    def strike(self, on, **kw):
         raise NotImplementedError
 
     def code(self, on, **kw):
         raise NotImplementedError
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         self.in_pre = on != 0
 
-    def small(self, on):
+    def small(self, on, **kw):
         raise NotImplementedError
 
-    def big(self, on):
+    def big(self, on, **kw):
         raise NotImplementedError
 
     # special markup for syntax highlighting #############################
 
-    def code_area(self, on, code_id, **kwargs):
+    def code_area(self, on, code_id, **kw):
         raise NotImplementedError
 
     def code_line(self, on):
@@ -234,10 +237,10 @@
     def linebreak(self, preformatted=1):
         raise NotImplementedError
 
-    def paragraph(self, on):
-        self.in_p = (on != 0)
+    def paragraph(self, on, **kw):
+        self.in_p = on != 0
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         raise NotImplementedError
 
     def icon(self, type):
@@ -245,22 +248,22 @@
 
     # Lists ##############################################################
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         raise NotImplementedError
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         raise NotImplementedError
 
     def listitem(self, on, **kw):
         raise NotImplementedError
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         raise NotImplementedError
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         raise NotImplementedError
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         raise NotImplementedError
 
     def heading(self, on, depth, **kw):
@@ -268,13 +271,13 @@
 
     # Tables #############################################################
     
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         raise NotImplementedError
 
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         raise NotImplementedError
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         raise NotImplementedError
 
     # Dynamic stuff / Plugins ############################################
@@ -284,7 +287,7 @@
         return macro_obj.execute(name, args)    
 
     def _get_bang_args(self, line):
-        if line[:2]=='#!':
+        if line[:2] == '#!':
             try:
                 name, args = line[2:].split(None, 1)
             except ValueError:
@@ -293,7 +296,7 @@
                 return args
         return None
 
-    def processor(self, processor_name, lines, is_parser = 0):
+    def processor(self, processor_name, lines, is_parser=0):
         """ processor_name MUST be valid!
             writes out the result instead of returning it!
         """
@@ -306,14 +309,14 @@
                                            processor_name, "Parser")
             args = self._get_bang_args(lines[0])
             if args is not None:
-                lines=lines[1:]
-            p = parser('\n'.join(lines), self.request, format_args = args)
+                lines = lines[1:]
+            p = parser('\n'.join(lines), self.request, format_args=args)
             p.format(self)
             del p
         return ''
 
-    def dynamic_content(self, parser, callback, arg_list = [], arg_dict = {},
-                        returns_content = 1):
+    def dynamic_content(self, parser, callback, arg_list=[], arg_dict={},
+                        returns_content=1):
         content = parser[callback](*arg_list, **arg_dict)
         if returns_content:
             return content
@@ -322,6 +325,14 @@
 
     # Other ##############################################################
     
+    def div(self, on, **kw):
+        """ open/close a blocklevel division """
+        return ""
+    
+    def span(self, on, **kw):
+        """ open/close a inline span """
+        return ""
+    
     def rawHTML(self, markup):
         """ This allows emitting pre-formatted HTML markup, and should be
             used wisely (i.e. very seldom).
@@ -342,7 +353,7 @@
 
         return self.text(f.getvalue())
 
-    def escapedText(self, on):
+    def escapedText(self, on, **kw):
         """ This allows emitting text as-is, anything special will
             be escaped (at least in HTML, some text output format
             would possibly do nothing here)
@@ -351,3 +362,4 @@
 
     def comment(self, text):
         return ""
+
--- a/MoinMoin/formatter/dom_xml.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/dom_xml.py	Tue Jan 31 21:17:13 2006 +0000
@@ -29,8 +29,8 @@
                     'p', 'ol', 'ul', 'li', 'pre', 'a',
                     'table', 'td', 'tr']
 
-    need_p = [] #format_tags[:]
-    need_p.extend(['ol', 'a', 'pagelink', 'interwiki', 'macro']) #XXX add more
+    need_p = [] # format_tags[:]
+    need_p.extend(['ol', 'a', 'pagelink', 'interwiki', 'macro']) # XXX add more
 
     no_p_after = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'ol', 'ul', 'pre',
                   'small', 'big', 'table', 'td', 'tr', 'dt',
@@ -38,25 +38,25 @@
                   'sysmesg']
 
     close_on_open = {
-        'h1' : ['p'],
-        'li' : ['li'],
-        'p' : ['p'],
-        #'pre' : ['p'],
+        'h1': ['p'],
+        'li': ['li'],
+        'p': ['p'],
+        #'pre': ['p'],
         }
 
-    for i in xrange(2,7):
+    for i in xrange(2, 7):
         close_on_open['h%i' % i] = close_on_open['h1']
 
     close_on_open = {} # XXX
 
     close_on_close = {
-        'table' : ['td', 'tr'],
-        'td' : ['tr'],
-        'tr' : ['td'],
-        'ol' : ['li'],
-        'ul' : ['li'],
+        'table': ['td', 'tr'],
+        'td': ['tr'],
+        'tr': ['td'],
+        'ol': ['li'],
+        'ul': ['li'],
         }
-    close_on_close = {} #XXX
+    close_on_close = {} # XXX
 
     def __init__(self, request, **kw):
         self.request = request
@@ -95,7 +95,7 @@
             must be the last opened tag!!!
         """
         if tag == 'p':
-            self.in_p = 0 #XXX
+            self.in_p = 0 # XXX
         if self.tag_stack[-1][0] != tag:
             raise ValueError, "<%s> expected <%s> given" % (self.tag_stack[-1][0], tag)
         self.position = self.position.parentNode
@@ -220,8 +220,8 @@
                 self.text('\n'.join(lines)) +
                 self._set_tag('processor', False))
 
-    def dynamic_content(self, parser, callback, arg_list = [], arg_dict = {},
-                        returns_content = 1):
+    def dynamic_content(self, parser, callback, arg_list=[], arg_dict={},
+                        returns_content=1):
         content = parser[callback](*arg_list, **arg_dict)
         if returns_content:
             return content
@@ -232,7 +232,7 @@
         kw['href'] = str(url)
         if css:
             kw['class'] = str(css)
-        return self._set_tag('a', on,  **kw)
+        return self._set_tag('a', on, **kw)
 
     def attachment_link(self, on, url='', **kw):
         kw['href'] = url
@@ -253,7 +253,7 @@
         kw['type'] = 'inline'
         return self._add_tag('attachment', **kw)
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         return self._add_tag('hr', size=str(size))
 
     def icon(self, type):
@@ -262,41 +262,41 @@
     def smiley(self, type):
         return self._add_tag('smiley', type=type)
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return self._set_tag('b', on)
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return self._set_tag('em', on)
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return self._set_tag('highlight', on)
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         return self._set_tag('ol', on, type=type, start=start)
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         return self._set_tag('ul', on)
 
     def listitem(self, on, **kw):
         return self._set_tag('li', on)
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         return self._set_tag('sup', on)
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return self._set_tag('sub', on)
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         return self._set_tag('strike', on)
 
     def code(self, on, **kw):
         return self._set_tag('code', on)
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         self.in_pre = on != 0
         return self._set_tag('pre', on)
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         return self._set_tag('p', on)
 
@@ -315,31 +315,28 @@
             result[str(name)] = value
         return result
 
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         return self._set_tag('table', on, **self._check_attrs(attrs))
         
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         return self._set_tag('tr', on, **self._check_attrs(attrs))
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         return self._set_tag('td', on, **self._check_attrs(attrs))
 
     def anchordef(self, name):
         return self._add_tag('anchor', name=name)
 
-    def anchorlink(self, on, name, id=None):
-        kw = {}
-        if id:
-            kw['id'] = str(id)
+    def anchorlink(self, on, name, **kw):
         return self.url(on, "#" + name, **kw)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return self._set_tag('u', on)
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         return self._set_tag('dl', on)
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         # XXX may be not correct
         # self._langAttr() missing
         if compact and on:
@@ -347,30 +344,32 @@
         else:
             return self._set_tag('dt', on)            
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         # self._langAttr() missing
         return self._set_tag('dd', on)
 
-    def image(self, **kw):
+    def image(self, src=None, **kw):
         """ Take HTML <IMG> tag attributes in `attr`.
 
             Attribute names have to be lowercase!
         """
+        if src:
+            kw['src'] = src
         return self._add_tag('img', **kw)
 
-    def escapedText(self, text):
+    def escapedText(self, text, **kw):
         return wikiutil.escape(text)
 
-    def small(self, on):
+    def small(self, on, **kw):
         return self._set_tag('small', on)
 
-    def big(self, on):
+    def big(self, on, **kw):
         return self._set_tag('big', on)
 
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
-        kw = {'id' : code_id,
-              'type' : code_type,
-              'show' : show,
+        kw = {'id': code_id,
+              'type': code_type,
+              'show': show,
              }
         if start != -1:
             kw['start'] = start
--- a/MoinMoin/formatter/pagelinks.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/pagelinks.py	Tue Jan 31 21:17:13 2006 +0000
@@ -1,14 +1,13 @@
 # -*- coding: iso-8859-1 -*-
 """
-MoinMoin - pagelinks Formatter
+    MoinMoin - pagelinks Formatter
 
-@copyright: 2005 Nir Soffer <nirs@freeshell.org>
-@license: GNU GPL, see COPYING for details.
+    @copyright: 2005 Nir Soffer <nirs@freeshell.org>
+    @license: GNU GPL, see COPYING for details.
 """
 
 from MoinMoin.formatter.base import FormatterBase
 
-
 class Formatter(FormatterBase):
     """ Collect pagelinks and format nothing :-) """        
     
@@ -57,13 +56,4 @@
     attachment_link = null
     attachment_image = null
     attachment_drawing = null
-    
-    # These are private additions to formatter added by text_html, and
-    # some code use or might use them.
-    open = null
-    close = null
-    formatAttributes = null
-    
-    def langAttr(self, lang=None):
-        return {}
-        
+
--- a/MoinMoin/formatter/text_gedit.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/text_gedit.py	Tue Jan 31 21:17:13 2006 +0000
@@ -1,6 +1,6 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - "text/html+css" Formatter
+    MoinMoin - "text/html+css" Formatter for feeding the GUI editor
 
     @copyright: (c) Bastian Blank, Florian Festi, Thomas Waldmann
     @license: GNU GPL, see COPYING for details.
@@ -19,7 +19,7 @@
 
     # Block elements ####################################################
 
-    def heading(self, on, depth, id = None, **kw):
+    def heading(self, on, depth, id=None, **kw):
         # remember depth of first heading, and adapt counting depth accordingly
         if not self._base_depth:
             self._base_depth = depth
@@ -29,9 +29,9 @@
 
         # closing tag, with empty line after, to make source more readable
         if not on:
-            return self.close('h%d' % heading_depth)
+            return self._close('h%d' % heading_depth)
         else:
-            return self.open('h%d' % heading_depth)
+            return self._open('h%d' % heading_depth)
 
     # Links ##############################################################
 
@@ -60,25 +60,8 @@
         return self.url(1, href, title=title, unescaped=0, css=html_class)
         # unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
 
-    '''
-    def attachment_link(self, url, text, **kw):
-        if url==text:
-            return "attachment:%s" % url
-        else:
-            return "[attachment:%s %s]" % (url, text)
-    
-    def attachment_image(self, url, **kw):
-        return "attachment:%s" % url
-    
-    def attachment_drawing(self, url, text, **kw):
-        if url==text:
-            return "drawing:%s" % url
-        else:
-            return "[drawing:%s %s]" % (url, text)
-'''
-    
     def attachment_inlined(self, url, text, **kw):
-        if url==text:
+        if url == text:
             return '<span style="background-color:#ffff11">inline:%s</span>' % url
         else:
             return '<span style="background-color:#ffff11">[inline:%s %s]</span>' % (url, text)
@@ -123,7 +106,7 @@
             result = "[[%s]]" % name
         return '<span style="background-color:#ffff11">%s</span>' % result
 
-    def processor(self, processor_name, lines, is_parser = 0):
+    def processor(self, processor_name, lines, is_parser=0):
         """ processor_name MUST be valid!
             writes out the result instead of returning it!
         """
@@ -135,41 +118,17 @@
 
         return "".join(result)
 
-
-    # Lists ##############################################################
-
-    # Change nesting: sub lists are no longer within the <li> tags
-    
-    def number_list(self, on, type=None, start=None):
-        li = ""
-        if self._in_li: # close <li>
-            li = self.listitem(False)
-        return li + text_html.Formatter.number_list(self, on, type, start)
-
-    def bullet_list(self, on):
-        li = ""
-        if self._in_li: # close <li>
-            li = self.listitem(False)
-        return li + text_html.Formatter.bullet_list(self, on)
-
-    def listitem(self, on, **kw):
-        # only if not already closed
-        if on or self._in_li:
-            return text_html.Formatter.listitem(self, on, **kw)
-        else:
-            return ""
-
     # Other ##############################################################
 
     style2attribute = {
         'width': 'width',
         'height': 'height',
-        'background' : 'bgcolor',
-        'background-color' : 'bgcolor',
+        'background': 'bgcolor',
+        'background-color': 'bgcolor',
         #if this is used as table style="text-align: right", it doesn't work
         #if it is transformed to align="right":
-        #'text-align' : 'align',
-        #'vertical-align' : 'valign'
+        #'text-align': 'align',
+        #'vertical-align': 'valign'
         }
 
     def _style_to_attributes(self, attrs):
@@ -197,7 +156,7 @@
         attrs = text_html.Formatter._checkTableAttr(self, attrs, prefix)
         return self._style_to_attributes(attrs)
 
-    def table(self, on, attrs=None):
+    def table(self, on, attrs=None, **kw):
         """ Create table
 
         @param on: start table
@@ -214,36 +173,38 @@
                 #result.append(self.rawHTML("<!-- ATTRS1: %s -->" % repr(attrs)))
                 attrs = self._checkTableAttr(attrs, 'table')
                 #result.append(self.rawHTML("<!-- ATTRS2: %s -->" % repr(attrs)))
-            result.append(self.open('table', newline=1, attr=attrs))
+            result.append(self._open('table', newline=1, attr=attrs))
         else:
             # Close table then div
-            result.append(self.close('table'))
+            result.append(self._close('table'))
 
         return ''.join(result)    
 
-    def comment(self, text):
+    def comment(self, text, **kw):
         text = text.rstrip() # workaround for growing amount of blanks at EOL
         return self.preformatted(1, attr={'class': 'comment'}) + text + self.preformatted(0)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         tag = 'u'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag)
+        return self._close(tag)
                     
-    def strong(self, on):
+    def strong(self, on, **kw):
         tag = 'b'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag)
+        return self._close(tag)
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         tag = 'i'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag)
+        return self._close(tag)
 
-    def code(self, on, css=None):
+    def code(self, on, css=None, **kw):
+        if not css and kw.has_key('css_class'):
+            css = kw['css_class']
         tag = 'tt'
         # Maybe we don't need this, because we have tt will be in inlineStack.
         self._in_code = on
@@ -253,8 +214,8 @@
             attrs = None
 
         if on:
-            return self.open(tag, attr=attrs)
-        return self.close(tag)
+            return self._open(tag, attr=attrs)
+        return self._close(tag)
 
     def line_anchordef(self, lineno):
         return '' # not needed for gui editor feeding
--- a/MoinMoin/formatter/text_html.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/text_html.py	Tue Jan 31 21:17:13 2006 +0000
@@ -6,17 +6,167 @@
     @license: GNU GPL, see COPYING for details.
 """
 import os.path, re
+from sets import Set # TODO: when we require Python 2.4+ use the builtin 'set' type
 from MoinMoin.formatter.base import FormatterBase
 from MoinMoin import wikiutil, i18n, config
 from MoinMoin.Page import Page
 from MoinMoin.action import AttachFile
 
+line_anchors = False
+prettyprint = False
+
+# These are the HTML elements that we treat as block elements.
+_blocks = Set(['dd', 'div', 'dl', 'dt', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
+               'hr', 'li', 'ol', 'p', 'pre', 'table', 'tbody', 'td', 'tfoot', 'th',
+               'thead', 'tr', 'ul', 'blockquote', ])
+
+# These are the HTML elements which are typically only used with
+# an opening tag without a separate closing tag.  We do not
+# include 'script' or 'style' because sometimes they do have
+# content, and also IE has a parsing bug with those two elements (only)
+# when they don't have a closing tag even if valid XHTML.
+
+_self_closing_tags = Set(['area', 'base', 'br', 'col', 'frame', 'hr', 'img', 'input',
+                          'isindex', 'link', 'meta', 'param'])
+
+# These are the elements which generally should cause an increase in the
+# indention level in the html souce code.
+_indenting_tags = Set(['ol', 'ul', 'dl', 'li', 'dt', 'dd', 'tr', 'td'])
+
+# These are the elements that discard any whitespace they contain as
+# immediate child nodes.
+_space_eating_tags = Set(['colgroup', 'dl', 'frameset', 'head', 'map' 'menu',
+                          'ol', 'optgroup', 'select', 'table', 'tbody', 'tfoot',
+                          'thead', 'tr', 'ul'])
+
+# These are standard HTML attributes which are typically used without any
+# value; e.g., as boolean flags indicated by their presence.
+_html_attribute_boolflags = Set(['compact', 'disabled', 'ismap', 'nohref',
+                                 'noresize', 'noshade', 'nowrap', 'readonly',
+                                 'selected', 'wrap'])
+
+# These are all the standard HTML attributes that are allowed on any element.
+_common_attributes = Set(['accesskey', 'class', 'dir', 'disabled', 'id', 'lang',
+                          'style', 'tabindex', 'title'])
+
+
+def rewrite_attribute_name(name, default_namespace='html'):
+    """Takes an attribute name and tries to make it HTML correct.
+
+    This function takes an attribute name as a string, as it may be
+    passed in to a formatting method using a keyword-argument syntax,
+    and tries to convert it into a real attribute name.  This is
+    necessary because some attributes may conflict with Python
+    reserved words or variable syntax (such as 'for', 'class', or
+    'z-index'); and also to help with backwards compatibility with
+    older versions of MoinMoin where different names may have been
+    used (such as 'content_id' or 'css').
+
+    Returns a tuple of strings: (namespace, attribute).
+
+    Namespaces: The default namespace is always assumed to be 'html',
+    unless the input string contains a colon or a double-underscore;
+    in which case the first such occurance is assumed to separate the
+    namespace prefix from name.  So, for example, to get the HTML
+    attribute 'for' (as on a <label> element), you can pass in the
+    string 'html__for' or even '__for'.
+
+    Hyphens:  To better support hyphens (which are not allowed in Python
+    variable names), all occurances of two underscores will be replaced
+    with a hyphen.  If you use this, then you must also provide a
+    namespace since the first occurance of '__' separates a namespace
+    from the name.
+
+    Special cases: Within the 'html' namespace, mainly to preserve
+    backwards compatibility, these exceptions ars recognized:
+    'content_type', 'content_id', 'css_class', and 'css'.
+    Additionally all html attributes starting with 'on' are forced to
+    lower-case.  Also the string 'xmlns' is recognized as having
+    no namespace.
+
+    Examples:
+        'id' -> ('html', 'id')
+        'css_class' -> ('html', 'class')
+        'content_id' -> ('html', 'id')
+        'content_type' -> ('html', 'type')
+        'html__for' -> ('html', 'for)
+        'xml__space' -> ('xml', 'space')
+        '__z__index' -> ('html', 'z-index')
+        '__http__equiv' -> ('html', 'http-equiv')
+        'onChange' -> ('html', 'onchange')
+        'xmlns' -> ('', 'xmlns')
+        'xmlns__abc' -> ('xmlns', 'abc')
+
+    (In actuality we only deal with namespace prefixes, not any real
+    namespace URI...we only care about the syntax not the meanings.)
+    """
+
+    # Handle any namespaces (just in case someday we support XHTML)
+    if ':' in name:
+        ns, name = name.split(':', 1)
+    elif '__' in name:
+        ns, name = name.split('__', 1)
+    elif name == 'xmlns':
+        ns = ''
+    else:
+        ns = default_namespace
+
+    name.replace('__', '-')
+    if ns == 'html':
+        # We have an HTML attribute, fix according to DTD
+        if name == 'content_type': # MIME type such as in <a> and <link> elements
+            name =  'type'
+        elif name == 'content_id': # moin historical convention
+            name =  'id'
+        elif name in ('css_class', 'css'): # to avoid python word 'class'
+            name = 'class'
+        elif name.startswith('on'): # event handler hook
+            name = name.lower()
+    return ns, name
+
+
+def extend_attribute_dictionary(attributedict, ns, name, value):
+    """Add a new attribute to an attribute dictionary, merging values where possible.
+
+    The attributedict must be a dictionary with tuple-keys of the form:
+    (namespace, attrname).
+
+    The given ns, name, and value will be added to the dictionary.  It
+    will replace the old value if it existed, except for a few special
+    cases where the values are logically merged instead (CSS class
+    names and style rules).
+
+    As a special case, if value is None (not just ''), then the
+    attribute is actually deleted from the dictionary.
+    """
+
+    key = ns, name
+    if value is None:
+        if attributedict.has_key(key):
+            del attributedict[key]
+    else:
+        if ns == 'html' and attributedict.has_key(key):
+            if name == 'class':
+                # CSS classes are appended by space-separated list
+                value = attributedict[key] + ' ' + value
+            elif name == 'style':
+                # CSS styles are appended by semicolon-separated rules list
+                value = attributedict[key] + '; ' + value
+            elif name in _html_attribute_boolflags:
+                # All attributes must have a value. According to XHTML those
+                # traditionally used as flags should have their value set to
+                # the same as the attribute name.
+                value = name
+        attributedict[key] = value
+
+
 class Formatter(FormatterBase):
     """
         Send HTML data.
     """
 
     hardspace = '&nbsp;'
+    indentspace = ' '
 
     def __init__(self, request, **kw):
         apply(FormatterBase.__init__, (self, request), kw)
@@ -26,8 +176,11 @@
         # the stack are closed.
         self._inlineStack = []
 
-        self._in_li = 0
-        self._in_code = 0
+        # stack of all tags
+        self._tag_stack = []
+        self._indent_level = 0
+
+        self._in_code = 0 # used by text_gedit
         self._in_code_area = 0
         self._in_code_line = 0
         self._code_area_num = 0
@@ -36,7 +189,7 @@
         self._show_section_numbers = None
         self._content_ids = []
         self.pagelink_preclosed = False
-        self._is_included = kw.get('is_included',False)
+        self._is_included = kw.get('is_included', False)
         self.request = request
         self.cfg = request.cfg
 
@@ -49,8 +202,9 @@
     # code clean and handle pathological cases like unclosed p and
     # inline tags.
 
-    def langAttr(self, lang=None):
+    def _langAttr(self, lang=None):
         """ Return lang and dir attribute
+        (INTERNAL USE BY HTML FORMATTER ONLY!)
 
         Must be used on all block elements - div, p, table, etc.
         @param lang: if defined, will return attributes for lang. if not
@@ -67,65 +221,159 @@
                 # lang is inherited from content div
                 return {}
 
+        #attr = {'xml:lang': lang, 'lang': lang, 'dir': i18n.getDirection(lang),}
         attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
         return attr
 
-    def formatAttributes(self, attr=None):
-        """ Return formatted attributes string
+    def _formatAttributes(self, attr=None, allowed_attrs=None, **kw):
+        """ Return HTML attributes formatted as a single string. (INTERNAL USE BY HTML FORMATTER ONLY!)
 
         @param attr: dict containing keys and values
-        @rtype: string ?
+        @param allowed_attrs: A list of allowable attribute names
+        @param **kw: other arbitrary attributes expressed as keyword arguments.
+        @rtype: string
         @return: formated attributes or empty string
+
+        The attributes and their values can either be given in the
+        'attr' dictionary, or as extra keyword arguments.  They are
+        both merged together.  See the function
+        rewrite_attribute_name() for special notes on how to name
+        attributes.
+
+        Setting a value to None rather than a string (or string
+        coercible) will remove that attribute from the list.
+        
+        If the list of allowed_attrs is provided, then an error is
+        raised if an HTML attribute is encountered that is not in that
+        list (or is not a common attribute which is always allowed or
+        is not in another XML namespace using the double-underscore
+        syntax).
         """
+
+        # Merge the attr dict and kw dict into a single attributes
+        # dictionary (rewriting any attribute names, extracting
+        # namespaces, and merging some values like css classes).
+        attributes = {} # dict of key=(namespace,name): value=attribute_value
         if attr:
-            attr = [' %s="%s"' % (k, v) for k, v in attr.items()]           
-            return ''.join(attr)
+            for a, v in attr.items():
+                a_ns, a_name = rewrite_attribute_name(a)
+                extend_attribute_dictionary(attributes, a_ns, a_name, v)
+        if kw:
+            for a, v in kw.items():
+                a_ns, a_name = rewrite_attribute_name(a)
+                extend_attribute_dictionary(attributes, a_ns, a_name, v)
+
+        # Add title attribute if missing, but it has an alt.
+        if attributes.has_key(('html', 'alt')) and not attributes.has_key(('html', 'title')):
+            attributes[('html', 'title')] = attributes[('html', 'alt')]
+
+        # Force both lang and xml:lang to be present and identical if
+        # either exists.  The lang takes precedence over xml:lang if
+        # both exist.
+        #if attributes.has_key(('html', 'lang')):
+        #    attributes[('xml', 'lang')] = attributes[('html', 'lang')]
+        #elif attributes.has_key(('xml', 'lang')):
+        #    attributes[('html', 'lang')] = attributes[('xml', 'lang')]
+
+        # Check all the HTML attributes to see if they are known and
+        # allowed.  Ignore attributes if in non-HTML namespaces.
+        if allowed_attrs:
+            for name in [key[1] for key in attributes.keys() if key[0] == 'html']:
+                if name in _common_attributes or name in allowed_attrs:
+                    pass
+                elif name.startswith('on'):
+                    pass  # Too many event handlers to enumerate, just let them all pass.
+                else:
+                    # Unknown or unallowed attribute.
+                    err = 'Illegal HTML attribute "%s" passed to formatter' % name
+                    raise ValueError(err)
+
+        # Finally, format them all as a single string.
+        if attributes:
+            # Construct a formatted string containing all attributes
+            # with their values escaped.  Any html:* namespace
+            # attributes drop the namespace prefix.  We build this by
+            # separating the attributes into three categories:
+            #
+            #  * Those without any namespace (should only be xmlns attributes)
+            #  * Those in the HTML namespace (we drop the html: prefix for these)
+            #  * Those in any other non-HTML namespace, including xml:
+
+            xmlnslist = ['%s="%s"' % (k[1], wikiutil.escape(v, 1))
+                         for k, v in attributes.items() if not k[0]]
+            htmllist = ['%s="%s"' % (k[1], wikiutil.escape(v, 1))
+                        for k, v in attributes.items() if k[0] == 'html']
+            otherlist = ['%s:%s="%s"' % (k[0], k[1], wikiutil.escape(v, 1))
+                         for k, v in attributes.items() if k[0] and k[0] != 'html']
+
+            # Join all these lists together in a space-separated string.  Also
+            # prefix the whole thing with a space too.
+            htmllist.sort()
+            otherlist.sort()
+            all = [''] + xmlnslist + htmllist + otherlist
+            return ' '.join(all)
         return ''
 
-    # TODO: use set when we require Python 2.3
-    # TODO: The list is not complete, add missing from dtd
-    _blocks = 'p div pre table tr td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
-    _blocks = dict(zip(_blocks.split(), [1] * len(_blocks)))
-
-    def open(self, tag, newline=False, attr=None):
-        """ Open a tag with optional attributes
+    def _open(self, tag, newline=False, attr=None, allowed_attrs=None, **kw):
+        """ Open a tag with optional attributes (INTERNAL USE BY HTML FORMATTER ONLY!)
         
         @param tag: html tag, string
-        @param newline: render tag on a separate line
-        @parm attr: dict with tag attributes
+        @param newline: render tag so following data is on a separate line
+        @param attr: dict with tag attributes
+        @param allowed_attrs: list of allowed attributes for this element
+        @param kw: arbitrary attributes and values
         @rtype: string ?
-        @return: open tag with attributes
+        @return: open tag with attributes as a string
         """
-        if tag in self._blocks:
+        is_self_closing = ''
+        if tag in _self_closing_tags:
+            # Don't expect a closing tag later on.
+            is_self_closing = ' /'
+
+        if tag in _blocks:
             # Block elements
             result = []
             
             # Add language attributes, but let caller overide the default
-            attributes = self.langAttr()
+            attributes = self._langAttr()
             if attr:
                 attributes.update(attr)
             
             # Format
-            attributes = self.formatAttributes(attributes)
-            result.append('<%s%s>' % (tag, attributes))
+            attributes = self._formatAttributes(attributes, allowed_attrs=allowed_attrs, **kw)
+            result.append('<%s%s%s>' % (tag, attributes, is_self_closing))
             if newline:
-                result.append('\n')
-            return ''.join(result)
+                result.append(self._newline())
+            tagstr = ''.join(result)
         else:
             # Inline elements
             # Add to inlineStack
-            self._inlineStack.append(tag)
+            if not is_self_closing:
+                # Only push on stack if we expect a close-tag later
+                self._inlineStack.append(tag)
             # Format
-            return '<%s%s>' % (tag, self.formatAttributes(attr))
-       
-    def close(self, tag, newline=False):
-        """ Close tag
+            tagstr = '<%s%s%s>' % (tag,
+                                      self._formatAttributes(attr, allowed_attrs, **kw),
+                                      is_self_closing)
+        # XXX SENSE ???
+        #if not self.close:
+        #    self._tag_stack.append(tag)
+        #    if tag in _indenting_tags:
+        #        self._indent_level += 1
+        return tagstr
+
+    def _close(self, tag, newline=False):
+        """ Close tag (INTERNAL USE BY HTML FORMATTER ONLY!)
 
         @param tag: html tag, string
-        @rtype: string ?
-        @return: closing tag
+        @param newline: render tag so following data is on a separate line
+        @rtype: string
+        @return: closing tag as a string
         """
-        if tag in self._blocks:
+        if tag in _self_closing_tags:
+            # This tag was already closed
+            tagstr = ''
+        elif tag in _blocks:
             # Block elements
             # Close all tags in inline stack
             # Work on a copy, because close(inline) manipulate the stack
@@ -133,56 +381,78 @@
             stack = self._inlineStack[:]
             stack.reverse()
             for inline in stack:
-                result.append(self.close(inline))
+                result.append(self._close(inline))
             # Format with newline
             if newline:
-                result.append('\n')
-            result.append('</%s>\n' % (tag))
-            return ''.join(result)            
+                result.append(self._newline())
+            result.append('</%s>' % (tag))
+            tagstr = ''.join(result)            
         else:
             # Inline elements 
             # Pull from stack, ignore order, that is not our problem.
             # The code that calls us should keep correct calling order.
             if tag in self._inlineStack:
                 self._inlineStack.remove(tag)
-            return '</%s>' % tag
+            tagstr = '</%s>' % tag
 
+        # XXX see other place marked with "SENSE"
+        #if tag in _self_closing_tags:
+        #    self._tag_stack.pop()
+        #    if tag in _indenting_tags:
+        #        # decrease indent level
+        #        self._indent_level -= 1
+        if newline:
+            tagstr += self._newline()
+        return tagstr
 
     # Public methods ###################################################
 
-    def startContent(self, content_id='content', **kwargs):
-        """ Start page content div """
+    def startContent(self, content_id='content', newline=True, **kw):
+        """ Start page content div.
+
+        A link anchor is provided at the beginning of the div, with
+        an id of 'top' or 'top_xxxx' if the content_id argument is
+        set to 'xxxx'.
+        """
 
         # Setup id
-        if content_id!='content':
+        if content_id != 'content':
             aid = 'top_%s' % (content_id,)
         else:
             aid = 'top'
         self._content_ids.append(content_id)
         result = []
         # Use the content language
-        attr = self.langAttr(self.request.content_lang)
+        attr = self._langAttr(self.request.content_lang)
         attr['id'] = content_id
-        result.append(self.open('div', newline=1, attr=attr))
+        result.append(self._open('div', newline=False, attr=attr,
+                                 allowed_attrs=['align'], **kw))
         result.append(self.anchordef(aid))
+        if newline:
+            result.append('\n')
         return ''.join(result)
         
-    def endContent(self):
-        """ Close page content div """
+    def endContent(self, newline=True):
+        """ Close page content div.
+
+        A link anchor is provided at the end of the div, with
+        an id of 'bottom' or 'bottom_xxxx' if the content_id argument
+        to the previus startContent() call was set to 'xxxx'.
+        """
 
         # Setup id
         try:
             cid = self._content_ids.pop()
         except:
             cid = 'content'
-        if cid!='content':
+        if cid != 'content':
             aid = 'bottom_%s' % (cid,)
         else:
             aid = 'bottom'
 
         result = []
         result.append(self.anchordef(aid))
-        result.append(self.close('div', newline=1))
+        result.append(self._close('div', newline=newline))
         return ''.join(result) 
 
     def lang(self, on, lang_name):
@@ -195,19 +465,13 @@
         if lang_name != self.request.current_lang:
             # Enclose text in span using lang attributes
             if on:
-                attr = self.langAttr(lang=lang_name)
-                return self.open(tag, attr=attr)
-            return self.close(tag)
+                attr = self._langAttr(lang=lang_name)
+                return self._open(tag, attr=attr)
+            return self._close(tag)
 
         # Direction did not change, no need for span
         return ''            
                 
-    def sysmsg(self, on, **kw):
-        tag = 'div'
-        if on:
-            return self.open(tag, attr={'class': 'message'})
-        return self.close(tag)
-    
     # Links ##############################################################
     
     def pagelink(self, on, pagename='', page=None, **kw):
@@ -237,7 +501,7 @@
         @keyword title: override using the interwiki wikiname as title
         """
         if not on:
-            return '</a>'
+            return self.url(0)
         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
         wikiurl = wikiutil.mapURL(self.request, wikiurl)
         if wikitag == 'Self': # for own wiki, do simple links
@@ -259,44 +523,106 @@
             # unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
 
     def url(self, on, url=None, css=None, **kw):
-        """
+        """ Inserts an <a> element.
+
+            Call once with on=1 to start the link, and again with on=0
+            to end it (no other arguments are needed when on==0).
+
             Keyword params:
-                title - <a> title attribute
-                attrs -  just include those <a> attrs "as is"
+                url - the URL to link to; will go through Wiki URL mapping.
+                css - a space-separated list of CSS classes
+                attrs -  just include this string verbatim inside
+                         the <a> element; can be used for arbitrary attrs;
+                         all escaping and quoting is the caller's responsibility.
+
+            Note that the 'attrs' keyword argument is for backwards compatibility
+            only.  It should not be used for new code--instead just pass
+            any attributes in as separate keyword arguments.
         """
+        if not on:
+            return self._close('a')
+        attrs = self._langAttr()
+
+        # Handle the URL mapping
+        if url is None and kw.has_key('href'):
+            url = kw['href']
+            del kw['href']
         if url is not None:
             url = wikiutil.mapURL(self.request, url)
-        title = kw.get('title', None)
-        attrs = kw.get('attrs', None)
+            attrs['href'] = url
+
+        if css:
+            attrs['class'] = css
+        
+        if kw.has_key('attrs'):
+            # for backwards compatibility, raw pre-formated attribute string
+            extra_attrs = kw['attrs']
+            del kw['attrs']
+        else:
+            extra_attrs = None
+
+        # create link
         if on:
-            str = '<a'
-            if css: 
-                str = '%s class="%s"' % (str, css)
-            if title:
-                str = '%s title="%s"' % (str, title)
-            if attrs:
-                str = '%s %s' % (str, attrs)
-            str = '%s href="%s">' % (str, wikiutil.escape(url, 1))
+            str = self._open('a', attr=attrs, **kw)
+            if extra_attrs:
+                # insert this into the tag verbatim (messy)
+                if str[-2:] == '/>':
+                    str = '%s %s />' % (str[:-2], extra_attrs)
+                else:
+                    str = '%s %s>' % (str[:-1], extra_attrs)
         else:
-            str = '</a>'
+            str = self._close('a')
         return str
 
     def anchordef(self, id):
-        #return '<a id="%s"></a>' % (id, ) # this breaks PRE sections for IE
-        # do not add a \n here, it breaks pre sections with line_anchordef
-        return '<span id="%s" class="anchor"></span>' % (id, )
+        """Inserts an invisible element used as a link target.
+
+        Inserts an empty <span> element with an id attribute, used as an anchor
+        for link references.  We use <span></span> rather than <span/>
+        for browser portability.
+        """
+        # Don't add newlines, \n, as it will break pre and
+        # line-numbered code sections (from line_achordef() method).
+        #return '<a id="%s"></a>' % (id, ) # do not use - this breaks PRE sections for IE
+        return '<span class="anchor" id="%s"></span>' % wikiutil.escape(id, 1)
 
     def line_anchordef(self, lineno):
-        return self.anchordef("line-%d" % lineno)
+        if line_anchors:
+            return self.anchordef("line-%d" % lineno)
+        else:
+            return ''
 
-    def anchorlink(self, on, name='', id=None):
-        extra = ''
-        if id:
-            extra = ' id="%s"' % id
-        return ['<a href="#%s"%s>' % (name, extra), '</a>'][not on]
+    def anchorlink(self, on, name='', **kw):
+        """Insert an <a> link pointing to an anchor on the same page.
+
+        Call once with on=1 to start the link, and a second time with
+        on=0 to end it.  No other arguments are needed on the second
+        call.
+
+        The name argument should be the same as the id provided to the
+        anchordef() method, or some other elment.  It should NOT start
+        with '#' as that will be added automatically.
+
+        The id argument, if provided, is instead the id of this link
+        itself and not of the target element the link references.
+        """
+
+        attrs = self._langAttr()
+        if name:
+            attrs['href'] = '#%s' % name
+        if kw.has_key('href'):
+            del kw['href']
+        if on:
+            str = self._open('a', attr=attrs, **kw)
+        else:
+            str = self._close('a')
+        return str
 
     def line_anchorlink(self, on, lineno=0):
-        return self.anchorlink(on, name="line-%d" % lineno)
+        if line_anchors:
+            return self.anchorlink(on, name="line-%d" % lineno)
+        else:
+            return ''
 
     # Attachments ######################################################
 
@@ -373,7 +699,7 @@
             # we have a image map. inline it and add a map ref
             # to the img tag
             try:
-                map = open(mappath,'r').read()
+                map = file(mappath, 'r').read()
             except IOError:
                 pass
             except OSError:
@@ -383,11 +709,11 @@
                 # replace MAPNAME
                 map = map.replace('%MAPNAME%', mapid)
                 # add alt and title tags to areas
-                map = re.sub('href\s*=\s*"((?!%TWIKIDRAW%).+?)"',r'href="\1" alt="\1" title="\1"',map)
+                map = re.sub('href\s*=\s*"((?!%TWIKIDRAW%).+?)"', r'href="\1" alt="\1" title="\1"', map)
                 # add in edit links plus alt and title attributes
                 map = map.replace('%TWIKIDRAW%"', edit_link + '" alt="' + _('Edit drawing %(filename)s') % {'filename': self.text(fname)} + '" title="' + _('Edit drawing %(filename)s') % {'filename': self.text(fname)} + '"')
                 # unxml, because 4.01 concrete will not validate />
-                map = map.replace('/>','>')
+                map = map.replace('/>', '>')
                 return (map + self.image(
                     alt=drawing,
                     src=AttachFile.getAttachUrl(
@@ -409,87 +735,148 @@
     # Text ##############################################################
     
     def _text(self, text):
+        text = wikiutil.escape(text)
         if self._in_code:
-            return wikiutil.escape(text).replace(' ', self.hardspace)
-        return wikiutil.escape(text)
+            text = text.replace(' ', self.hardspace)
+        return text
 
     # Inline ###########################################################
         
-    def strong(self, on):
-        tag = 'strong'
-        if on:
-            return self.open(tag)
-        return self.close(tag)
+    def strong(self, on, **kw):
+        """Creates an HTML <strong> element.
 
-    def emphasis(self, on):
-        tag = 'em'
-        if on:
-            return self.open(tag)
-        return self.close(tag)
-
-    def underline(self, on):
-        tag = 'span'
-        if on:
-            return self.open(tag, attr={'class': 'u'})
-        return self.close(tag)
-
-    def highlight(self, on):
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'strong'
         if on:
-            return self.open(tag, attr={'class': 'highlight'})
-        return self.close(tag)
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
 
-    def sup(self, on):
+    def emphasis(self, on, **kw):
+        """Creates an HTML <em> element.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
+        tag = 'em'
+        if on:
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
+
+    def underline(self, on, **kw):
+        """Creates a text span for underlining (css class "u").
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
+        tag = 'span'
+        if on:
+            return self._open(tag, attr={'class': 'u'}, allowed_attrs=[], **kw)
+        return self._close(tag)
+
+    def highlight(self, on, **kw):
+        """Creates a text span for highlighting (css class "highlight").
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
+        tag = 'strong'
+        if on:
+            return self._open(tag, attr={'class': 'highlight'}, allowed_attrs=[], **kw)
+        return self._close(tag)
+
+    def sup(self, on, **kw):
+        """Creates a <sup> element for superscript text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'sup'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
 
-    def sub(self, on):
+    def sub(self, on, **kw):
+        """Creates a <sub> element for subscript text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'sub'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
 
-    def strike(self, on):
-        tag = 'strike'
+    def strike(self, on, **kw):
+        """Creates a text span for line-through (strikeout) text (css class 'strike').
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
+        # This does not use <strike> because has been deprecated in standard HTML.
+        tag = 'span'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag, attr={'class': 'strike'},
+                              allowed_attrs=[], **kw)
+        return self._close(tag)
 
     def code(self, on, **kw):
+        """Creates a <tt> element for inline code or monospaced text.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+
+        Any text within this section will have spaces converted to
+        non-break spaces.
+        """
         tag = 'tt'
         # Maybe we don't need this, because we have tt will be in inlineStack.
         self._in_code = on        
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
         
-    def small(self, on):
+    def small(self, on, **kw):
+        """Creates a <small> element for smaller font.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'small'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
 
-    def big(self, on):
+    def big(self, on, **kw):
+        """Creates a <big> element for larger font.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'big'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            return self._open(tag, allowed_attrs=[], **kw)
+        return self._close(tag)
 
 
     # Block elements ####################################################
 
-    def preformatted(self, on, attr=None):
+    def preformatted(self, on, **kw):
+        """Creates a preformatted text region, with a <pre> element.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         FormatterBase.preformatted(self, on)
         tag = 'pre'
         if on:
-            return self.open(tag, newline=1, attr=attr)
-        return self.close(tag)
+            return self._open(tag, newline=1, **kw)
+        return self._close(tag)
                 
     # Use by code area
     _toggleLineNumbersScript = """
-<script type="text/JavaScript">
+<script type="text/javascript">
 function isnumbered(obj) {
   return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber';
 }
@@ -541,6 +928,19 @@
 """
     
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
+        """Creates a formatted code region, with line numbering.
+
+        This region is formatted as a <div> with a <pre> inside it.  The
+        code_id argument is assigned to the 'id' of the div element, and
+        must be unique within the document.  The show, start, and step are
+        used for line numbering.
+
+        Note this is not like most formatter methods, it can not take any
+        extra keyword arguments.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         res = []
         ci = self.request.makeUniqueID('CA-%s_%03d' % (code_id, self._code_area_num))
         if on:
@@ -551,7 +951,7 @@
 
             # Open the code div - using left to right always!
             attr = {'class': 'codearea', 'lang': 'en', 'dir': 'ltr'}
-            res.append(self.open('div', attr=attr))
+            res.append(self._open('div', attr=attr))
 
             # Add the script only in the first code area on the page
             if self._code_area_js == 0 and self._code_area_state[1] >= 0:
@@ -562,7 +962,7 @@
             if self._code_area_state[1] >= 0:
                 toggleLineNumbersLink = r'''
 <script type="text/javascript">
-document.write('<a href="#" onClick="return togglenumber(\'%s\', %d, %d);" \
+document.write('<a href="#" onclick="return togglenumber(\'%s\', %d, %d);" \
                 class="codenumbers">Toggle line numbers<\/a>');
 </script>
 ''' % (self._code_area_state[0], self._code_area_state[2], self._code_area_state[3])
@@ -570,14 +970,14 @@
 
             # Open pre - using left to right always!
             attr = {'id': self._code_area_state[0], 'lang': 'en', 'dir': 'ltr'}
-            res.append(self.open('pre', newline=True, attr=attr))
+            res.append(self._open('pre', newline=True, attr=attr))
         else:
             # Close code area
             res = []
             if self._in_code_line:
                 res.append(self.code_line(0))
-            res.append(self.close('pre'))
-            res.append(self.close('div'))
+            res.append(self._close('pre'))
+            res.append(self._close('div'))
 
             # Update state
             self._in_code_area = 0
@@ -602,27 +1002,59 @@
 
     # Paragraphs, Lines, Rules ###########################################
     
+    def _indent_spaces(self):
+        """Returns space(s) for indenting the html source so list nesting is easy to read.
+
+        Note that this mostly works, but because of caching may not always be accurate."""
+        if prettyprint:
+            return self.indentspace * self._indent_level
+        else:
+            return ''
+
+    def _newline(self):
+        """Returns the whitespace for starting a new html source line, properly indented."""
+        if prettyprint:
+            return '\n' + self._indent_spaces()
+        else:
+            return ''
+
     def linebreak(self, preformatted=1):
+        """Creates a line break in the HTML output.
+        
+        If preformatted is true a <br> element is inserted, otherwise
+        the linebreak will only be visible in the HTML source.
+        """
         if self._in_code_area:
             preformatted = 1
-        return ['\n', '<br>\n'][not preformatted]
+        return ['\n', '<br />\n'][not preformatted] + self._indent_spaces()
         
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
+        """Creates a paragraph with a <p> element.
+        
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         if self._terse:
             return ''
         FormatterBase.paragraph(self, on)
-        if self._in_li:
-            self._in_li = self._in_li + 1
         tag = 'p'
         if on:
-            return self.open(tag)
-        return self.close(tag)
-            
-    def rule(self, size=None):
-        if size:
+            tagstr = self._open(tag, **kw)
+        else:
+            tagstr = self._close(tag)
+        return tagstr
+
+    def rule(self, size=None, **kw):
+        """Creates a horizontal rule with an <hr> element.
+
+        If size is a number in the range [1..6], the CSS class of the rule
+        is set to 'hr1' through 'hr6'.  The intent is that the larger the
+        size number the thicker or bolder the rule will be.
+        """
+        if size and 1 <= size <= 6:
             # Add hr class: hr1 - hr6
-            return self.open('hr', newline=1, attr={'class': 'hr%d' % size})
-        return self.open('hr', newline=1)
+            return self._open('hr', newline=1, attr={'class': 'hr%d' % size}, **kw)
+        return self._open('hr', newline=1, **kw)
                 
     def icon(self, type):
         return self.request.theme.make_icon(type)
@@ -634,9 +1066,29 @@
             href = self.request.theme.img_url(img)
         return self.image(src=href, alt=text, width=str(w), height=str(h))
 
+    def image(self, src=None, **kw):
+        """Creates an inline image with an <img> element.
+
+        The src argument must be the URL to the image file.
+        """
+        if src:
+            kw['src'] = src
+        return self._open('img', **kw)
+
     # Lists ##############################################################
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
+        """Creates an HTML ordered list, <ol> element.
+
+        The 'type' if specified can be any legal numbered
+        list-style-type, such as 'decimal','lower-roman', etc.
+
+        The 'start' argument if specified gives the numeric value of
+        the first list item (default is 1).
+
+        Call once with on=1 to start the list, and a second time
+        with on=0 to end it.
+        """
         tag = 'ol'
         if on:
             attr = {}
@@ -644,49 +1096,81 @@
                 attr['type'] = type
             if start is not None:
                 attr['start'] = start
-            return self.open(tag, newline=1, attr=attr)
-        return self.close(tag)
+            tagstr = self._open(tag, newline=1, attr=attr, **kw)
+        else:
+            tagstr = self._close(tag, newline=1)
+        return tagstr
     
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
+        """Creates an HTML ordered list, <ul> element.
+
+        The 'type' if specified can be any legal unnumbered
+        list-style-type, such as 'disc','square', etc.
+
+        Call once with on=1 to start the list, and a second time
+        with on=0 to end it.
+        """
         tag = 'ul'
         if on:
-            return self.open(tag, newline=1)
-        return self.close(tag)
-           
+            tagstr = self._open(tag, newline=1, **kw)
+        else:
+            tagstr = self._close(tag, newline=1)
+        return tagstr
+
     def listitem(self, on, **kw):
-        """ List item inherit its lang from the list. """
+        """Adds a list item, <li> element, to a previously opened
+        bullet or number list.
+
+        Call once with on=1 to start the region, and a second time
+        with on=0 to end it.
+        """
         tag = 'li'
-        self._in_li = on != 0
         if on:
-            attr = {}
-            css_class = kw.get('css_class', None)
-            if css_class:
-                attr['class'] = css_class
-            style = kw.get('style', None)
-            if style:
-                attr['style'] = style
-            return self.open(tag, attr=attr)
-        return self.close(tag)
+            tagstr =  self._open(tag, newline=1, **kw)
+        else:
+            tagstr = self._close(tag, newline=1)
+        return tagstr
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
+        """Creates an HTML definition list, <dl> element.
+
+        Call once with on=1 to start the list, and a second time
+        with on=0 to end it.
+        """
         tag = 'dl'
         if on:
-            return self.open(tag, newline=1)
-        return self.close(tag)
+            tagstr = self._open(tag, newline=1, **kw)
+        else:
+            tagstr = self._close(tag, newline=1)
+        return tagstr
 
-    def definition_term(self, on):
+    def definition_term(self, on, **kw):
+        """Adds a new term to a definition list, HTML element <dt>.
+
+        Call once with on=1 to start the term, and a second time
+        with on=0 to end it.
+        """
         tag = 'dt'
         if on:
-            return self.open(tag)
-        return self.close(tag)
-        
-    def definition_desc(self, on):
+            tagstr = self._open(tag, newline=1, **kw)
+        else:
+            tagstr = self._close(tag, newline=0)
+        return tagstr
+
+    def definition_desc(self, on, **kw):
+        """Gives the definition to a definition item, HTML element <dd>.
+
+        Call once with on=1 to start the definition, and a second time
+        with on=0 to end it.
+        """
         tag = 'dd'
         if on:
-            return self.open(tag)
-        return self.close(tag)
+            tagstr = self._open(tag, newline=1, **kw)
+        else:
+            tagstr = self._close(tag, newline=0)
+        return tagstr
 
-    def heading(self, on, depth, id = None, **kw):
+    def heading(self, on, depth, **kw):
         # remember depth of first heading, and adapt counting depth accordingly
         if not self._base_depth:
             self._base_depth = depth
@@ -709,7 +1193,7 @@
 
         # closing tag, with empty line after, to make source more readable
         if not on:
-            return self.close('h%d' % heading_depth) + '\n'
+            return self._close('h%d' % heading_depth) + '\n'
             
         # create section number
         number = ''
@@ -722,25 +1206,22 @@
             number = '.'.join(map(str, self.request._fmt_hd_counters[self._show_section_numbers-1:]))
             if number: number += ". "
 
-        attr = {}
-        if id:
-            attr['id'] = id
         # Add space before heading, easier to check source code
-        result = '\n' + self.open('h%d' % heading_depth, attr=attr)
+        result = '\n' + self._open('h%d' % heading_depth, **kw)
 
         # TODO: convert this to readable code
         if self.request.user.show_topbottom:
             # TODO change top/bottom refs to content-specific top/bottom refs?
             result = ("%s%s%s%s%s%s%s%s" %
                       (result,
-                       kw.get('icons',''),
+                       kw.get('icons', ''),
                        self.url(1, "#bottom", unescaped=1),
                        self.icon('bottom'),
                        self.url(0),
                        self.url(1, "#top", unescaped=1),
                        self.icon('top'),
                        self.url(0)))
-        return "%s%s%s" % (result, kw.get('icons',''), number)
+        return "%s%s%s" % (result, kw.get('icons', ''), number)
 
     
     # Tables #############################################################
@@ -795,7 +1276,7 @@
         return result
 
 
-    def table(self, on, attrs=None):
+    def table(self, on, attrs=None, **kw):
         """ Create table
 
         @param on: start table
@@ -807,44 +1288,80 @@
         if on:
             # Open div to get correct alignment with table width smaller
             # than 100%
-            result.append(self.open('div', newline=1))
+            result.append(self._open('div', newline=1))
 
             # Open table
             if not attrs:
                 attrs = {}
             else:
                 attrs = self._checkTableAttr(attrs, 'table')
-            result.append(self.open('table', newline=1, attr=attrs))
+            result.append(self._open('table', newline=1, attr=attrs,
+                                     allowed_attrs=self._allowed_table_attrs['table'],
+                                     **kw))
+            result.append(self._open('tbody', newline=1))
         else:
-            # Close table then div
-            result.append(self.close('table'))
-            result.append(self.close('div'))
+            # Close tbody, table, and then div
+            result.append(self._close('tbody'))
+            result.append(self._close('table'))
+            result.append(self._close('div'))
 
         return ''.join(result)    
     
-    def table_row(self, on, attrs=None):
+    def table_row(self, on, attrs=None, **kw):
         tag = 'tr'
         if on:
             if not attrs:
                 attrs = {}
             else:
                 attrs = self._checkTableAttr(attrs, 'row')
-            return self.open(tag, newline=1, attr=attrs)
-        return self.close(tag)
+            return self._open(tag, newline=1, attr=attrs,
+                             allowed_attrs=self._allowed_table_attrs['row'],
+                             **kw)
+        return self._close(tag) + '\n'
     
-    def table_cell(self, on, attrs=None):
+    def table_cell(self, on, attrs=None, **kw):
         tag = 'td'
         if on:
             if not attrs:
                 attrs = {}
             else:
                 attrs = self._checkTableAttr(attrs, '')
-            return self.open(tag, newline=1, attr=attrs)
-        return self.close(tag)
+            return '  ' + self._open(tag, attr=attrs,
+                             allowed_attrs=self._allowed_table_attrs[''],
+                             **kw)
+        return self._close(tag) + '\n'
 
-    def escapedText(self, text):
-        return wikiutil.escape(text)
+    def text(self, text, **kw):
+        txt = FormatterBase.text(self, text, **kw)
+        if kw:
+            return self._open('span', **kw) + txt + self._close('span')
+        return txt
+
+    def escapedText(self, text, **kw):
+        txt = wikiutil.escape(text)
+        if kw:
+            return self._open('span', **kw) + txt + self._close('span')
+        return txt
 
     def rawHTML(self, markup):
         return markup
 
+    def sysmsg(self, on, **kw):
+        tag = 'div'
+        if on:
+            return self._open(tag, attr={'class': 'message'}, **kw)
+        return self._close(tag)
+    
+    def div(self, on, **kw):
+        tag = 'div'
+        if on:
+            return self._open(tag, **kw)
+        return self._close(tag)
+
+    def span(self, on, **kw):
+        tag = 'span'
+        if on:
+            return self._open(tag, **kw)
+        return self._close(tag)
+    
+
--- a/MoinMoin/formatter/text_plain.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/text_plain.py	Tue Jan 31 21:17:13 2006 +0000
@@ -26,7 +26,7 @@
         self._text = None # XXX does not work with links in headings!!!!!
 
     def startDocument(self, pagename):
-        line = u"*" * (len(pagename)+2) + u'\n'
+        line = u"*" * (len(pagename) + 2) + u'\n'
         return u"%s %s \n%s" % (line, pagename, line)
 
     def endDocument(self):
@@ -69,37 +69,40 @@
                 self._text = None
                 return u' [%s]' % (self._url)
 
-    # Attachments ######################################################
-
     def attachment_link(self, url, text, **kw):
         return "[%s]" % text
+
     def attachment_image(self, url, **kw):
-        return "[image:%s]" % url
+        title = ''
+        for a in (u'title', u'html__title', u'alt', u'html_alt'):
+            if kw.has_key(a):
+                title = ':' + kw[a]
+        return "[image:%s%s]" % (url, title)
 
     def attachment_drawing(self, url, text, **kw):
         return "[drawing:%s]" % text
     
-    def text(self, text):
+    def text(self, text, **kw):
         self._did_para = 0
         if self._text is not None:
             self._text.append(text)
         return text
 
-    def rule(self, size=0):
+    def rule(self, size=0, **kw):
         size = min(size, 10)
         ch = u"---~=*+#####"[size]
         return (ch * 79) + u'\n'
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return u'*'
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return u'/'
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return u''
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         if on:
             self._in_list = 1
             return [u'\n', u'\n\n'][not self._did_para]
@@ -110,7 +113,7 @@
                 return u'\n'
         return u''
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         if on:
             self._in_list = -1
             return [u'\n', u'\n\n'][not self._did_para]
@@ -123,11 +126,11 @@
 
     def listitem(self, on, **kw):
         if on:
-            if self._in_list>0:
+            if self._in_list > 0:
                 self._in_list += 1
                 self._did_para = 1
                 return ' %d. ' % (self._in_list-1,)
-            elif self._in_list<0:
+            elif self._in_list < 0:
                 self._did_para = 1
                 return u' * '
             else:
@@ -136,20 +139,20 @@
             self._did_para = 1
             return u'\n'
         
-    def sup(self, on):
+    def sup(self, on, **kw):
         return u'^'
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return u'_'
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         return u'__'
 
     def code(self, on, **kw):
         #return [unichr(0x60), unichr(0xb4)][not on]
         return u"'" # avoid high-ascii
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         FormatterBase.preformatted(self, on)
         snip = u'---%<'
         snip = snip + (u'-' * (78 - len(snip)))
@@ -158,10 +161,10 @@
         else:
             return snip + u'\n'
 
-    def small(self, on):
+    def small(self, on, **kw):
         return u''
 
-    def big(self, on):
+    def big(self, on, **kw):
         return u''
 
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
@@ -182,8 +185,8 @@
         if not on or (on and self._in_code_line):
             res += u'\n'
         if on:
-            if self._code_area_state[0]>0:
-                res += u' %4d  ' % ( self._code_area_state[3] )
+            if self._code_area_state[0] > 0:
+                res += u' %4d  ' % self._code_area_state[3]
                 self._code_area_state[3] += self._code_area_state[2]
         self._in_code_line = on != 0
         return res
@@ -191,7 +194,7 @@
     def code_token(self, on, tok_type):
         return ""
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         if self._did_para:
             on = 0
@@ -212,34 +215,38 @@
             self._text = None
             return result
 
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         return u''
 
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         return u''
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         return u''
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return u'_'
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         return u''
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         result = u''
-        if not compact: result = result + u'\n'
-        if not on: result = result + u':\n'
+        if not compact:
+            result = result + u'\n'
+        if not on:
+            result = result + u':\n'
         return result
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         return [u'    ', u'\n'][not on]
 
-    def image(self, **kw):
-        if kw.has_key(u'alt'):
-            return kw[u'alt']
+    def image(self, src=None, **kw):
+        for a in (u'title', u'html__title', u'alt', u'html_alt'):
+            if kw.has_key(a):
+                return kw[a]
         return u''
 
     def lang(self, on, lang_name):
         return ''
+
--- a/MoinMoin/formatter/text_python.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/text_python.py	Tue Jan 31 21:17:13 2006 +0000
@@ -22,7 +22,7 @@
 
     defaultDependencies = ["time"]
     
-    def __init__(self, request, static = [], formatter = None, **kw):
+    def __init__(self, request, static=[], formatter=None, **kw):
         if formatter:
             self.formatter = formatter
         else:
@@ -67,7 +67,7 @@
                            self.code_fragments[i],
                            self.text_cmd_begin,
                            repr(text[i+1])])
-            i = i + 1
+            i += 1
         source.append(self.text_cmd_end)
         source.append(self.__adjust_formatter_state())
         
@@ -88,10 +88,11 @@
 
     def __is_static(self, dependencies):
         for dep in dependencies:
-            if dep not in  self.static: return False
+            if dep not in self.static:
+                return False
         return True
 
-    def __adjust_languge_state(self):
+    def __adjust_language_state(self):
         """ Add current language state changing code to the cache """
         if self.__lang != self.request.current_lang:
             self.__lang = self.request.current_lang
@@ -99,7 +100,7 @@
         return ''
         
     def __adjust_formatter_state(self):
-        result = self.__adjust_languge_state()
+        result = self.__adjust_language_state()
         if self.__in_p != self.formatter.in_p:
             result = "%s%s.in_p = %r\n" % (result, self.__formatter,
                                            self.formatter.in_p)
@@ -110,8 +111,8 @@
             self.__in_pre = self.formatter.in_pre        
         return result
     
-    def dynamic_content(self, parser, callback, arg_list = [], arg_dict = {},
-                            returns_content = 1):
+    def dynamic_content(self, parser, callback, arg_list=[], arg_dict={},
+                            returns_content=1):
         adjust = self.__adjust_formatter_state()
         if returns_content:
             return self.__insert_code('%srequest.write(%s.%s(*%r,**%r))' %
@@ -136,6 +137,7 @@
         return self.__insert_code(
             'request.write(%s.attachment_link(%r, %r, **%r))' %
             (self.__formatter, url, text, kw))
+
     def attachment_image(self, url, **kw):
         return self.__insert_code(
             'request.write(%s.attachment_image(%r, **%r))' %
@@ -154,7 +156,7 @@
     def heading(self, on, depth, **kw):        
         if on:
             code = [
-                self.__adjust_languge_state(),
+                self.__adjust_language_state(),
                 'request.write(%s.heading(%r, %r, **%r))' % (self.__formatter,
                                                              on, depth, kw),
                 ]     
--- a/MoinMoin/formatter/text_xml.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/text_xml.py	Tue Jan 31 21:17:13 2006 +0000
@@ -36,7 +36,7 @@
         result = ""
         while self._current_depth > 1:
             result += "</s%d>" % self._current_depth
-            self._current_depth = self._current_depth - 1
+            self._current_depth -= 1
         return result + '</s1>'
 
     def lang(self, on, lang_name):
@@ -60,30 +60,29 @@
         else:
             return '</interwiki>'
 
-
     def url(self, on, url='', css=None, **kw):
         if css:
             str = ' class="%s"' % css
-        else: str = ''
-        
+        else:
+            str = ''
         return ('<jump href="%s"%s>' % (self._escape(url), str), '</jump>') [not on]
-    # Attachments ######################################################
 
     def attachment_link(self, url, text, **kw):
         return '<attachment href="%s">%s</attachment>' % (url, text)
+
     def attachment_image(self, url, **kw):
         return '<attachmentimage href="%s"></attachmentimage>' % (url,)
+
     def attachment_drawing(self, url, text, **kw):
-        return '<attachmentdrawing href="%s">%s</attachmentdrawing>' % (
-            url, text)
+        return '<attachmentdrawing href="%s">%s</attachmentdrawing>' % (url, text)
 
-    def text(self, text):
+    def text(self, text, **kw):
         if self.in_pre:
             return text.replace(']]>', ']]>]]&gt;<![CDATA[')
         return self._escape(text)
 
-    def rule(self, size=0):
-        return "\n<br/>%s<br/>\n" % ("-"*78,) # <hr/> not supported in stylebook
+    def rule(self, size=0, **kw):
+        return "\n<br/>%s<br/>\n" % ("-" * 78,) # <hr/> not supported in stylebook
         if size:
             return '<hr size="%d"/>\n' % (size,)
         else:
@@ -92,23 +91,25 @@
     def icon(self, type):
         return '<icon type="%s" />' % type            
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return ['<strong>', '</strong>'][not on]
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return ['<em>', '</em>'][not on]
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return ['<strong>', '</strong>'][not on]
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         result = ''
-        if self.in_p: result = self.paragraph(0)
+        if self.in_p:
+            result = self.paragraph(0)
         return result + ['<ol>', '</ol>\n'][not on]
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         result = ''
-        if self.in_p: result = self.paragraph(0)
+        if self.in_p:
+            result = self.paragraph(0)
         return result + ['<ul>', '</ul>\n'][not on]
 
     def listitem(self, on, **kw):
@@ -117,29 +118,30 @@
     def code(self, on, **kw):
         return ['<code>', '</code>'][not on]
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         return ['<sup>', '</sup>'][not on]
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return ['<sub>', '</sub>'][not on]
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         return ['<strike>', '</strike>'][not on]
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         FormatterBase.preformatted(self, on)
         result = ''
-        if self.in_p: result = self.paragraph(0)
+        if self.in_p:
+            result = self.paragraph(0)
         return result + ['<source><![CDATA[', ']]></source>'][not on]
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         return ['<p>', '</p>\n'][not on]
 
     def linebreak(self, preformatted=1):
         return ['\n', '<br/>\n'][not preformatted]
 
-    def heading(self, on, depth, id = None, **kw):
+    def heading(self, on, depth, id=None, **kw):
         if not on:
             return '">\n'
         # remember depth of first heading, and adapt current depth accordingly
@@ -151,7 +153,7 @@
         result = ""
         while self._current_depth >= depth:
             result = result + "</s%d>\n" % self._current_depth
-            self._current_depth = self._current_depth - 1
+            self._current_depth -= 1
         self._current_depth = depth
 
         id_text = ''
@@ -160,41 +162,43 @@
 
         return result + '<s%d%s title="' % (depth, id_text)
 
-    def table(self, on, attrs={}):
+    def table(self, on, attrs={}, **kw):
         return ['<table>', '</table>'][not on]
 
-    def table_row(self, on, attrs={}):
+    def table_row(self, on, attrs={}, **kw):
         return ['<tr>', '</tr>'][not on]
 
-    def table_cell(self, on, attrs={}):
+    def table_cell(self, on, attrs={}, **kw):
         return ['<td>', '</td>'][not on]
 
     def anchordef(self, id):
         return '<anchor id="%s"/>' % id
 
-    def anchorlink(self, on, name='', id=None):
+    def anchorlink(self, on, name='', **kw):
+        id = kw.get('id',None)
         extra = ''
         if id:
             extra = ' id="%s"' % id
         return ('<link anchor="%s"%s>' % (name, extra) ,'</link>') [not on]
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return self.strong(on) # no underline in StyleBook
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         result = ''
-        if self.in_p: result = self.paragraph(0)
+        if self.in_p:
+            result = self.paragraph(0)
         return result + ['<gloss>', '</gloss>'][not on]
 
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
         return ['<label>', '</label>'][not on]
 
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
         return ['<item>', '</item>'][not on]
 
-    def image(self, **kw):
-        valid_attrs = ['src', 'width', 'height', 'alt']
-        attrs = {}
+    def image(self, src=None, **kw):
+        valid_attrs = ['src', 'width', 'height', 'alt', 'title']
+        attrs = {'src': src}
         for key, value in kw.items():
             if key in valid_attrs:
                 attrs[key] = value
--- a/MoinMoin/formatter/xml_docbook.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/formatter/xml_docbook.py	Tue Jan 31 21:17:13 2006 +0000
@@ -66,7 +66,7 @@
         
     def getBody(self):
         body = []
-        # print all nodes inside dom behid heading
+        # print all nodes inside dom behind heading
         firstNode = self.doc.documentElement.firstChild
         while firstNode:
             if firstNode != self.domHeadNode:
@@ -116,7 +116,7 @@
         
         return self.outputFormatter.getHeading()
 
-    def startContent(self, content_id="content", **kwargs):
+    def startContent(self, content_id="content", **kw):
         self.cur = self.root
         return ""
 
@@ -132,7 +132,7 @@
     def endDocument(self):
         return self.outputFormatter.getFooter()
 
-    def text(self, text):
+    def text(self, text, **kw):
         if text == "\\n":
             srcText = "\n"
         else:
@@ -162,13 +162,13 @@
                     while (self.cur.nodeName != "section" and self.cur.nodeName != "article"):
                         self.cur = self.cur.parentNode
 
-# Do not understant this code - looks like unneccesery -- maybe it is used to gain some vertical space for large headings?
+# I don't understand this code - looks like unnecessary -- maybe it is used to gain some vertical space for large headings?
 #                    if len(self.cur.childNodes) < 3:
 #                       self._addEmptyNode("para")
                     
                     # check if not top-level
                     if self.cur.nodeName != "article":
-                        self.cur=self.cur.parentNode
+                        self.cur = self.cur.parentNode
 
             section = self.doc.createElement("section")
             self.cur.appendChild(section)
@@ -179,18 +179,18 @@
             self.cur = title
             self.curdepth = depth
         else:
-            self.cur=self.cur.parentNode
+            self.cur = self.cur.parentNode
 
         return ""
 
-    def paragraph(self, on):
+    def paragraph(self, on, **kw):
         FormatterBase.paragraph(self, on)
         if on:
             para = self.doc.createElement("para")
             self.cur.appendChild(para)
             self.cur = para
         else:
-            self.cur=self.cur.parentNode
+            self.cur = self.cur.parentNode
         return ""
 
     def linebreak(self, preformatted=1):
@@ -224,7 +224,7 @@
     def _getTableCellCount(self, attrs=()):
         cols = 1
         if attrs and attrs.has_key('colspan'):
-            s1 = (attrs['colspan'])
+            s1 = attrs['colspan']
             s1 = str(s1).replace('"','')
             cols = int(s1)
         return cols
@@ -263,46 +263,46 @@
 ### Inline ##########################################################
 
     def _handleFormatting(self, name, on, attributes=()):
-        #We add all the elements we create to the list of elements that should not contain a section        
+        # We add all the elements we create to the list of elements that should not contain a section        
         if name not in self.section_should_break:
             self.section_should_break.append(name)
 
         return self._handleNode(name, on, attributes)
 
-    def strong(self, on):
+    def strong(self, on, **kw):
         return self._handleFormatting("emphasis", on, (('role','strong'), ))
 
-    def emphasis(self, on):
+    def emphasis(self, on, **kw):
         return self._handleFormatting("emphasis", on)
 
-    def underline(self, on):
+    def underline(self, on, **kw):
         return self._handleFormatting("emphasis", on, (('role','underline'), ))
 
-    def highlight(self, on):
+    def highlight(self, on, **kw):
         return self._handleFormatting("emphasis", on, (('role','highlight'), ))
 
-    def sup(self, on):
+    def sup(self, on, **kw):
         return self._handleFormatting("superscript", on)
 
-    def sub(self, on):
+    def sub(self, on, **kw):
         return self._handleFormatting("subscript", on)
 
-    def strike(self, on):
+    def strike(self, on, **kw):
         # does not yield <strike> using the HTML XSLT files here ...
         # but seems to be correct
         return self._handleFormatting("emphasis", on,
                                       (('role','strikethrough'), ))
 
-    def code(self, on, **kwargs):
+    def code(self, on, **kw):
         return self._handleFormatting("code", on)
 
-    def preformatted(self, on):
+    def preformatted(self, on, **kw):
         return self._handleFormatting("screen", on)
 
 
 ### Lists ###########################################################
 
-    def number_list(self, on, type=None, start=None):
+    def number_list(self, on, type=None, start=None, **kw):
         docbook_ol_types = {'1': "arabic", 
                             'a': "loweralpha", 
                             'A': "upperalpha",
@@ -310,22 +310,22 @@
                             'I': "upperroman"}
 
         if type and docbook_ol_types.has_key(type):
-            attrs=[("numeration", docbook_ol_types[type])]
+            attrs = [("numeration", docbook_ol_types[type])]
         else:
-            attrs=[]
+            attrs = []
 
         return self._handleNode('orderedlist', on, attrs)
 
-    def bullet_list(self, on):
+    def bullet_list(self, on, **kw):
         return self._handleNode("itemizedlist", on)
 
-    def definition_list(self, on):
+    def definition_list(self, on, **kw):
         return self._handleNode("glosslist", on)        
 
-    '''When on is false, we back out just on level. This is
-       ok because we know definition_desc gets called, and we
-       back out two levels there'''
-    def definition_term(self, on, compact=0):
+    def definition_term(self, on, compact=0, **kw):
+       # When on is false, we back out just on level. This is
+       # ok because we know definition_desc gets called, and we
+       # back out two levels there.
         if on:
             entry=self.doc.createElement('glossentry')
             term=self.doc.createElement('glossterm')
@@ -336,8 +336,8 @@
             self.cur = self.cur.parentNode
         return ""
    
-    '''We backout two levels when 'on' is false, to leave the glossentry stuff'''
-    def definition_desc(self, on):
+    def definition_desc(self, on, **kw):
+        # We backout two levels when 'on' is false, to leave the glossentry stuff
         if on:
             return self._handleNode("glossdef", on)
         else:
@@ -356,13 +356,13 @@
 
 ### Links ###########################################################
 
-    #FIXME: This is quite crappy
+    # FIXME: This is quite crappy
     def pagelink(self, on, pagename='', page=None, **kw):
         FormatterBase.pagelink(self, on, pagename, page, **kw)
 
-        return self.interwikilink(on, 'Self', pagename) #FIXME
+        return self.interwikilink(on, 'Self', pagename) # FIXME
 
-    #FIXME: This is even more crappy
+    # FIXME: This is even more crappy
     def interwikilink(self, on, interwiki='', pagename='', **kw):
         if not on:
             return self.url(on,kw)
@@ -381,7 +381,8 @@
         self._handleNode("ulink", False)
         return ""
 
-    def anchorlink(self, on, name='', id=None):
+    def anchorlink(self, on, name='', **kw):
+        id = kw.get('id',None)
         attrs = []
         if name != '':
             attrs.append(('endterm', name))
@@ -392,7 +393,7 @@
 
         return self._handleNode("link", on, attrs)
 
-# Attachments ######################################################
+### Attachments ######################################################
 
     def attachment_link(self, url, text, **kw):
         _ = self.request.getText
@@ -436,9 +437,11 @@
                                             addts=1),
                 html_class="drawing")
 
+### Images and Smileys ##############################################
 
-### Images and Smileys ##############################################
-    def image(self, **kw):
+    def image(self, src=None, **kw):
+        if src:
+            kw['src'] = src
         media = self.doc.createElement('inlinemediaobject')
 
         imagewrap = self.doc.createElement('imageobject')
@@ -452,12 +455,17 @@
         if kw.has_key('height'):
             image.setAttribute('depth', kw['height'])
         imagewrap.appendChild(image)
-        
-        if kw.has_key('alt'):
+
+        title = ''
+        for a in ('title', 'html_title', 'alt', 'html_alt'):
+            if kw.has_key(titleattr):
+                title = kw[a]
+                break
+        if title:
             txtcontainer = self.doc.createElement('textobject')
             media.appendChild(txtcontainer)        
             txtphrase = self.doc.createElement('phrase')
-            txtphrase.appendChild(self.doc.createTextNode(kw['alt']))
+            txtphrase.appendChild(self.doc.createTextNode(title))
             txtcontainer.appendChild(txtphrase)        
         
         self.cur.appendChild(media)
@@ -471,13 +479,13 @@
         return self.image(src=href, alt=text, width=str(w), height=str(h))
 
     def icon(self, type):
-        return ''#self.request.theme.make_icon(type)
+        return '' # self.request.theme.make_icon(type)
 
 ### Tables ##########################################################
 
     #FIXME: We should copy code from text_html.py for attr handling
 
-    def table(self, on, attrs=None):
+    def table(self, on, attrs=None, **kw):
         sanitized_attrs = []
         if attrs and attrs.has_key('id'):
             sanitized_attrs[id] = attrs['id']
@@ -489,14 +497,14 @@
         self._handleNode("tbody", on)
         return ""
     
-    def table_row(self, on, attrs=None):
+    def table_row(self, on, attrs=None, **kw):
         self.table_current_row_cells = 0
         sanitized_attrs = []
         if attrs and attrs.has_key('id'):
             sanitized_attrs[id] = attrs['id']
         return self._handleNode("row", on, sanitized_attrs)
 
-    def table_cell(self, on, attrs=None):
+    def table_cell(self, on, attrs=None, **kw):
         # Finish row definition
         sanitized_attrs = []
         if attrs and attrs.has_key('id'):
@@ -517,6 +525,7 @@
         return ret
 
 ### Code ############################################################
+
     def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
         show = show and 'numbered' or 'unnumbered'
         if start < 1:
@@ -531,7 +540,7 @@
         return self._handleFormatting("screen", on, attrs)
 
     def code_line(self, on):
-        return '' #No clue why something should be done here
+        return '' # No clue why something should be done here
 
     def code_token(self, on, tok_type):
         toks_map = {'ID':'methodname',
@@ -561,10 +570,10 @@
         # At the begining text mode contain some string which is later
         # exchange for real value. There is problem that data inserted
         # as text mode are encoded to xml, e.g. < is encoded in the output as &lt;
-        text=FormatterBase.macro(self, macro_obj, name, args)
+        text = FormatterBase.macro(self, macro_obj, name, args)
         if len(text) > 0:
             # prepare identificator
-            sKey="EXCHANGESTRINGMACRO-" + str(len(self.exchangeKeys)) + "-EXCHANGESTRINGMACRO"
+            sKey = "EXCHANGESTRINGMACRO-" + str(len(self.exchangeKeys)) + "-EXCHANGESTRINGMACRO"
             self.exchangeKeys.append(sKey)
             self.exchangeValues.append(text)
             # append data to lists
@@ -572,11 +581,13 @@
         return u""
 
 ### Not supported ###################################################
-    def rule(self, size = 0):
+
+    def rule(self, size = 0, **kw):
         return ""
 
-    def small(self, on):
+    def small(self, on, **kw):
         return ""
 
-    def big(self, on):
+    def big(self, on, **kw):
         return ""
+
--- a/MoinMoin/macro/FootNote.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/macro/FootNote.py	Tue Jan 31 21:17:13 2006 +0000
@@ -43,12 +43,7 @@
     if request.footnotes:
         result = []
 
-        # Start footnotes div. It is important to use formatter so open
-        # inline tags will be closed, and we get correct direction.
-        attr = formatter.langAttr()
-        attr['class'] = 'footnotes'
-        attr = [' %s="%s"' % (k, v) for k, v in attr.items()]
-        result.append(formatter.rawHTML('<div%s>' % ''.join(attr)))
+        result.append(formatter.div(1, css_class='footnotes'))
 
         # Add footnotes list
         result.append(formatter.bullet_list(1))
@@ -60,7 +55,7 @@
             fn_id = request.footnotes[idx][1]
             result.append(formatter.anchorlink(1, 'fnref' + fn_id,
                                                id='fndef' + fn_id))
-            result.append(formatter.text(str(idx+1)))
+            result.append(formatter.text(str(idx + 1)))
             result.append(formatter.anchorlink(0))
             result.append(formatter.text(" "))
                         
@@ -78,7 +73,7 @@
         result.append(formatter.bullet_list(0))
 
         # Finish div
-        result.append(formatter.rawHTML('</div>'))
+        result.append(formatter.div(0))
 
         request.footnotes = []
         return ''.join(result)
--- a/MoinMoin/macro/Include.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/macro/Include.py	Tue Jan 31 21:17:13 2006 +0000
@@ -234,10 +234,10 @@
         # if no heading and not in print mode, then output a helper link
         if editlink and not (level or print_mode):
             result.extend([
-                '<div class="include-link">',
+                macro.formatter.div(1, css_class="include-link"),
                 inc_page.link_to(request, '[%s]' % (inc_name,), css_class="include-page-link"),
                 inc_page.link_to(request, '[%s]' % (_('edit'),), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
-                '</div>',
+                macro.formatter.div(0),
             ])
         # XXX page.link_to is wrong now, it escapes the edit_icon html as it escapes normal text
 
--- a/MoinMoin/parser/python.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/parser/python.py	Tue Jan 31 21:17:13 2006 +0000
@@ -70,8 +70,11 @@
         except tokenize.TokenError, ex:
             msg = ex[0]
             line = ex[1][0]
-            self.request.write(self.formatter.rawHTML("<b>ERROR: %s</b><br>%s\n" % (
-                msg, wikiutil.escape(self.raw[self.lines[line]:]))))
+            errmsg = (self.formatter.linebreak() + 
+                      self.formatter.strong(1) + "ERROR: %s" % msg + self.formatter.strong(0) +
+                      self.formatter.linebreak() +
+                      wikiutil.escape(self.raw[self.lines[line]:]))
+            self.request.write(errmsg)
         self.request.write(self.formatter.code_line(0))
         self.request.write(formatter.code_area(0, self._code_id))
 
--- a/MoinMoin/parser/rst.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/parser/rst.py	Tue Jan 31 21:17:13 2006 +0000
@@ -194,12 +194,12 @@
 
         html = []
         if parts['title']:
-            html.append(formatter.rawHTML('<h2>' + parts['title'] + '</h2>'))
+            html.append(formatter.rawHTML('<h1>%s</h1>' % parts['title']))
         # If there is only one subtitle then it is held in parts['subtitle'].
         # However, if there is more than one subtitle then this is empty and
         # fragment contains all of the subtitles.
         if parts['subtitle']:
-            html.append(formatter.rawHTML('<h3>' + parts['subtitle'] + '</h3>'))
+            html.append(formatter.rawHTML('<h2>%s</h2>' % parts['subtitle']))
         if parts['docinfo']:
             html.append(parts['docinfo'])
         html.append(parts['fragment'])
--- a/MoinMoin/parser/wiki.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/parser/wiki.py	Tue Jan 31 21:17:13 2006 +0000
@@ -53,7 +53,7 @@
     dl_rule = ur"^\s+.*?::\s"
 
     # the big, fat, ugly one ;)
-    formatting_rules = ur"""(?P<ent_numeric>&#\d{1,5};)
+    formatting_rules = ur"""(?P<ent_numeric>&#(\d{1,5}|x[0-9a-fA-F]+);)
 (?:(?P<emph_ibb>'''''(?=[^']+'''))
 (?P<emph_ibi>'''''(?=[^']+''))
 (?P<emph_ib_or_bi>'{5}(?=[^']))
@@ -72,7 +72,9 @@
 (?P<macro>\[\[(%%(macronames)s)(?:\(.*?\))?\]\]))
 (?P<ol>%(ol_rule)s)
 (?P<dl>%(dl_rule)s)
-(?P<li>^\s+\*?\s*)
+(?P<li>^\s+\*\s*)
+(?P<li_none>^\s+\.\s*)
+(?P<indent>^\s+)
 (?P<tableZ>\|\| $)
 (?P<table>(?:\|\|)+(?:<[^>]*?>)?(?!\|? $))
 (?P<heading>^\s*(?P<hmarker>=+)\s.*\s(?P=hmarker) $)
@@ -83,6 +85,7 @@
 (?P<email>[-\w._+]+\@[\w-]+(\.[\w-]+)+)
 (?P<smiley>(?<=\s)(%(smiley)s)(?=\s))
 (?P<smileyA>^(%(smiley)s)(?=\s))
+(?P<ent_symbolic>&[a-zA-Z]+;)
 (?P<ent>[<>&])
 (?P<wikiname_bracket>\[".*?"\])
 (?P<tt_bt>`.*?`)"""  % {
@@ -96,9 +99,11 @@
         'smiley': u'|'.join(map(re.escape, config.smileys.keys()))}
 
     # Don't start p before these 
-    no_new_p_before = ("heading rule table tableZ tr td ul ol dl dt dd li "
-                       "processor macro pre")
-    no_new_p_before = dict(zip(no_new_p_before.split(), [1] * len(no_new_p_before)))
+    no_new_p_before = ("heading rule table tableZ tr td "
+                       "ul ol dl dt dd li li_none indent "
+                       "macro processor pre")
+    no_new_p_before = no_new_p_before.split()
+    no_new_p_before = dict(zip(no_new_p_before, [1] * len(no_new_p_before)))
 
     def __init__(self, raw, request, **kw):
         self.raw = raw
@@ -446,35 +451,48 @@
         #        '<': '&lt;',
         #        '>': '&gt;'}[word]
 
-
     def _ent_numeric_repl(self, word):
-        """Handle numeric SGML entities."""
+        """Handle numeric (decimal and hexadecimal) SGML entities."""
         return self.formatter.rawHTML(word)
 
+    def _ent_symbolic_repl(self, word):
+        """Handle symbolic SGML entities."""
+        return self.formatter.rawHTML(word)
+    
+    def _indent_repl(self, match):
+        """Handle pure indentation (no - * 1. markup)."""
+        result = []
+        if not self.in_li:
+            self._close_item(result)
+            self.in_li = 1
+            css_class = None
+            if self.line_was_empty and not self.first_list_item:
+                css_class = 'gap'
+            result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
+        return ''.join(result)
+
+    def _li_none_repl(self, match):
+        """Handle type=none (" .") lists."""
+        result = []
+        self._close_item(result)
+        self.in_li = 1
+        css_class = None
+        if self.line_was_empty and not self.first_list_item:
+            css_class = 'gap'
+        result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
+        return ''.join(result)
 
     def _li_repl(self, match):
-        """Handle bullet lists."""
+        """Handle bullet (" *") lists."""
         result = []
-        indented_only = (match == (" " * len(match)))
-        if indented_only and self.in_li:
-            return ''
-            
         self._close_item(result)
-        #self.inhibit_p = 1
         self.in_li = 1
-        css_class = ''
+        css_class = None
         if self.line_was_empty and not self.first_list_item:
             css_class = 'gap'
-        if indented_only:
-            result.append(self.formatter.listitem(1, css_class=css_class,
-                                                  style="list-style-type:none"))
-        else:
-            result.append(self.formatter.listitem(1, css_class=css_class))
-        # Suspected p!
-        ## result.append(self.formatter.paragraph(1))
+        result.append(self.formatter.listitem(1, css_class=css_class))
         return ''.join(result)
 
-
     def _ol_repl(self, match):
         """Handle numbered lists."""
         return self._li_repl(match)
@@ -516,10 +534,11 @@
         #        self.inhibit_p = 1
     
         # Close lists while char-wise indent is greater than the current one
-        while ((self._indent_level() > new_level) or
-               ( new_level and
-                (self._indent_level() == new_level) and
-                (self.list_types[-1]) != list_type)):
+        #while ((self._indent_level() > new_level) or
+        #       ( new_level and
+        #        (self._indent_level() == new_level) and
+        #        (self.list_types[-1]) != list_type)):
+        while self._indent_level() > new_level:
             self._close_item(close)
             if self.list_types[-1] == 'ol':
                 tag = self.formatter.number_list(0)
@@ -564,6 +583,7 @@
             ##self.inhibit_p = 1
             self.in_li = 0
             self.in_dd = 0
+            
         # If list level changes, close an open table
         if self.in_table and (open or close):
             close[0:0] = [self.formatter.table(0)]
@@ -580,7 +600,7 @@
         result = []
         #result.append("<!-- _undent start -->\n")
         self._close_item(result)
-        for type in self.list_types:
+        for type in self.list_types[::-1]:
             if type == 'ol':
                 result.append(self.formatter.number_list(0))
             elif type == 'dl':
@@ -821,7 +841,7 @@
     def _macro_repl(self, word):
         """Handle macros ([[macroname]])."""
         macro_name = word[2:-2]
-        #self.inhibit_p = 1 # fixes UserPreferences, but makes new trouble!
+        self.inhibit_p = 1 # 0 fixes UserPreferences, but makes new trouble!
 
         # check for arguments
         args = None
@@ -852,7 +872,7 @@
                 ###result.append(u'<span class="info">[add text before match: <tt>"%s"</tt>]</span>' % line[lastpos:match.start()])
                 
                 if not (self.inhibit_p or self.in_pre or self.formatter.in_p):
-                    result.append(self.formatter.paragraph(1))
+                    result.append(self.formatter.paragraph(1, css_class="line879"))
                 result.append(self.formatter.text(line[lastpos:match.start()]))
             
             # Replace match with markup
@@ -864,7 +884,7 @@
         # No match: Add paragraph with the text of the line
         if not (self.in_pre or self.inhibit_p or
                 self.formatter.in_p) and lastpos < len(line):
-            result.append(self.formatter.paragraph(1))
+            result.append(self.formatter.paragraph(1, css_class="line886"))
         result.append(self.formatter.text(line[lastpos:]))
         return u''.join(result)
 
@@ -881,7 +901,7 @@
                     # Open p for certain types
                     if not (self.inhibit_p or self.formatter.in_p
                             or self.in_pre or (type in self.no_new_p_before)):
-                        result.append(self.formatter.paragraph(1))
+                        result.append(self.formatter.paragraph(1, css_class="line903"))
                     
                     # Get replace method and replece hit
                     replace = getattr(self, '_' + type + '_repl')
@@ -994,7 +1014,9 @@
                     self.processor = None
 
                     # send rest of line through regex machinery
-                    line = line[endpos+3:]                    
+                    line = line[endpos+3:]
+                    if not line.strip(): # just in the case "}}} " when we only have blanks left...
+                        continue
             else:
                 # we don't have \n as whitespace any more
                 # This is the space between lines we join to one paragraph
@@ -1007,7 +1029,7 @@
                         self.in_table = 0
                     # CHANGE: removed check for not self.list_types
                     # p should close on every empty line
-                    if (self.formatter.in_p):
+                    if self.formatter.in_p:
                         self.request.write(self.formatter.paragraph(0))
                     self.line_is_empty = 1
                     continue
@@ -1036,8 +1058,7 @@
                             indtype = "dl"
 
                 # output proper indentation tags
-                self.request.write(self._indent_to(indlen, indtype, numtype,
-                                                   numstart))
+                self.request.write(self._indent_to(indlen, indtype, numtype, numstart))
 
                 # Table mode
                 # TODO: move into function?                
@@ -1045,8 +1066,7 @@
                     and line[-3:] == "|| " and len(line) >= 5 + indlen):
                     # Start table
                     if self.list_types and not self.in_li:
-                        self.request.write(self.formatter.listitem
-                                           (1, style="list-style-type:none"))
+                        self.request.write(self.formatter.listitem(1, style="list-style-type:none"))
                         ## CHANGE: no automatic p on li
                         ##self.request.write(self.formatter.paragraph(1))
                         self.in_li = 1
@@ -1073,7 +1093,7 @@
             formatted_line = self.scan(scan_re, line)
             self.request.write(formatted_line)
 
-            if self.in_pre:
+            if self.in_pre == 3:
                 self.request.write(self.formatter.linebreak())
 
         # Close code displays, paragraphs, tables and open lists
@@ -1099,3 +1119,5 @@
                 self.processor_is_parser = 1
             except wikiutil.PluginMissingError:
                 self.processor = None
+
+
--- a/MoinMoin/search.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/search.py	Tue Jan 31 21:17:13 2006 +0000
@@ -809,7 +809,7 @@
             for page in self.hits:
                 matchInfo = ''
                 if info:
-                    matchInfo = self.formatInfo(page)
+                    matchInfo = self.formatInfo(f, page)
                 item = [
                     f.listitem(1),
                     f.pagelink(1, page.page_name, querystr=querystr),
@@ -852,7 +852,7 @@
             for page in self.hits:
                 matchInfo = ''
                 if info:
-                    matchInfo = self.formatInfo(page)
+                    matchInfo = self.formatInfo(f, page)
                 item = [
                     f.definition_term(1),
                     f.pagelink(1, page.page_name, querystr=querystr),
@@ -1072,22 +1072,24 @@
         querystr = wikiutil.escape(querystr)
         return querystr
 
-    def formatInfo(self, page):
+    def formatInfo(self, formatter, page):
         """ Return formatted match info """
-        # TODO: this will not work with non-html formats
-        template = u'<span class="info"> . . . %s %s</span>'
+        template = u' . . . %s %s'
+        template = u"%s%s%s" % (formatter.span(1, css_class="info"),
+                                template,
+                                formatter.span(0))
         # Count number of unique matches in text of all types
         count = len(page.get_matches(unique=1))
         info = template % (count, self.matchLabel[count != 1])
-        return self.formatter.rawHTML(info)         
+        return info
 
     def getvalue(self):
         """ Return output in div with CSS class """
         write = self.request.write
         value = [
-            self.formatter.rawHTML('<div class="searchresults">'),
+            self.formatter.div(1, css_class='searchresults'),
             self.buffer.getvalue(),
-            self.formatter.rawHTML('</div>'),
+            self.formatter.div(0),
             ]
         return '\n'.join(value)
 
--- a/MoinMoin/version.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/version.py	Tue Jan 31 21:17:13 2006 +0000
@@ -13,7 +13,7 @@
     patchlevel = 'release'
 
 project = "MoinMoin"
-release  = '1.5.1'
+release  = '1.5.refactor'
 revision = patchlevel
 
 if __name__ == '__main__':
--- a/MoinMoin/wikimacro.py	Tue Jan 31 21:09:45 2006 +0000
+++ b/MoinMoin/wikimacro.py	Tue Jan 31 21:17:13 2006 +0000
@@ -432,7 +432,9 @@
         except KeyError:
             # Wrong argument, return inline error message
             arg = self.formatter.text(args)
-            return '<span class="error">Wrong argument: %s</span>' % arg
+            return (self.formatter.span(1, css_class="error") +
+                    'Wrong argument: %s' % arg +
+                    self.formatter.span(0))
         
         count = self.request.rootpage.getPageCount(exists=exists)
         return self.formatter.text("%d" % count)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/CHANGES.refactor	Tue Jan 31 21:17:13 2006 +0000
@@ -0,0 +1,50 @@
+MoinMoin Version History
+========================
+
+Version moin--refactor--1.5:
+
+This was branched from moin--main--1.5--patch-399 (== release 1.5.1) to
+separately work on the following goals:
+
+  1. Make html output better (more valid).
+     Reduce problems the GUI editor converter has in 1.5.1.
+  2. Cleanup the formatter API
+
+While we are working on that goal, changes to moin--main--1.5 should be reduced
+to a minimum (security, critical bugs), due to following reasons:
+  * concentrate on goal (see above),
+    thus do not spend time on other features, neither here nor in "main" branch
+  * finish as soon as possible,
+    thus make merging "refactor" branch back to "main" branch easier
+
+Also please keep changes here as small as possible to reach the goal.
+Do not extend the goal without discussion and good reason.
+
+Please use THIS file for "refactor" branch CHANGES entries only, so we can
+easily integrate it when we merge back to "main".
+
+  Fixes:
+    * the new "." markup makes it possible to have a bulletless list with
+      elements on the same level. Before this change and only using indentation
+      with blanks, that would get merged into a single paragraph.
+    * it is possible now to have multiple paragraphs in the same list element
+    * fixed GAP processing for ordered lists
+    * fix text_gedit formatter's invalid list nesting
+    * fixed hr crash in blockquote (but needs more work)
+    * fixed FootNote's formatter usage
+    * fixed rst's headline levels
+    * fixed MoinMoinBugs/WikiParserThinksItIsInsidePreWhenItIsNot
+    * fixed MoinMoinBugs/ListItemGeneratedOutsideList
+    * fixed that macros were followed by a wrong <p>
+    * added <blockquote> to the block elements in the text_html formatter,
+      so it does not close it erratically when you close a inner <p>.
+    * GUI editor converter now also accept http: urls without // (relative or
+      same server urls)
+
+  Other changes:
+    * add new markup for bulletless lists: just use a "." instead of "*".
+    * deron meranda's formatter API cleanup
+    * added div and span to formatter API
+    * allow hex and symbolic entities
+
+
--- a/wiki/htdocs/classic/css/common.css	Tue Jan 31 21:09:45 2006 +0000
+++ b/wiki/htdocs/classic/css/common.css	Tue Jan 31 21:17:13 2006 +0000
@@ -122,8 +122,9 @@
 .hr5 {height: 7px;}
 .hr6 {height: 8px;}
 
-/* Replacement for html 3 u tag */
+/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
 .u {text-decoration: underline;}
+.strike {text-decoration: line-through;}
 
 .footnotes ul {
 	padding: 0 2em;
--- a/wiki/htdocs/modern/css/common.css	Tue Jan 31 21:09:45 2006 +0000
+++ b/wiki/htdocs/modern/css/common.css	Tue Jan 31 21:17:13 2006 +0000
@@ -188,8 +188,9 @@
 .hr5 {height: 6pt;}
 .hr6 {height: 7pt;}
 
-/* Replacement for html 3 u tag */
+/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
 .u {text-decoration: underline;}
+.strike {text-decoration: line-through;}
 
 /* eye catchers */
 .warning 
--- a/wiki/htdocs/rightsidebar/css/common.css	Tue Jan 31 21:09:45 2006 +0000
+++ b/wiki/htdocs/rightsidebar/css/common.css	Tue Jan 31 21:17:13 2006 +0000
@@ -123,8 +123,9 @@
 .hr5 {height: 6px;}
 .hr6 {height: 7px;}
 
-/* Replacement for html 3 u tag */
+/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
 .u {text-decoration: underline;}
+.strike {text-decoration: line-through;}
 
 .footnotes ul {
 	padding: 0 2em;