Attachment 'formatter-patch-r4.diff'

Download

   1 diff -urN moin-1.5.0/MoinMoin/formatter/base.py moin-1.5.0-formatter-patch/MoinMoin/formatter/base.py
   2 --- moin-1.5.0/MoinMoin/formatter/base.py	2005-11-18 14:16:35.000000000 -0500
   3 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/base.py	2006-01-19 14:52:29.000000000 -0500
   4 @@ -135,23 +135,27 @@
   5      def line_anchordef(self, lineno):
   6          return ""
   7  
   8 -    def anchorlink(self, on, name='', id=None):
   9 +    def anchorlink(self, on, name='', **kw):
  10          return ""
  11  
  12      def line_anchorlink(self, on, lineno=0):
  13          return ""
  14  
  15 -    def image(self, **kw):
  16 -        """ Take HTML <IMG> tag attributes in `attr`.
  17 +    def image(self, src=None, **kw):
  18 +        """An inline image.
  19  
  20 -        Attribute names have to be lowercase!
  21 +        Extra keyword arguments are according to the HTML <img> tag attributes.
  22 +        In particular an 'alt' or 'title' argument will should give a
  23 +        description of the image.
  24          """
  25 -        attrstr = u''
  26 -        for attr, value in kw.items():
  27 -            if attr=='html_class':
  28 -                attr='class'
  29 -            attrstr = attrstr + u' %s="%s"' % (attr, wikiutil.escape(value))
  30 -        return u'<img%s>' % attrstr
  31 +        title = src
  32 +        for titleattr in ('title', 'html__title', 'alt', 'html__alt'):
  33 +            if kw.has_key(titleattr):
  34 +                title = kw[titleattr]
  35 +                break
  36 +        if title:
  37 +            return '[Image:%s]' % title
  38 +        return '[Image]'
  39  
  40      def smiley(self, text):
  41          return text
  42 @@ -161,7 +165,7 @@
  43  
  44      # Text and Text Attributes ########################################### 
  45      
  46 -    def text(self, text):
  47 +    def text(self, text, **kw):
  48          if not self._highlight_re:
  49              return self._text(text)
  50              
  51 @@ -185,37 +189,37 @@
  52      def _text(self, text):
  53          raise NotImplementedError
  54  
  55 -    def strong(self, on):
  56 +    def strong(self, on, **kw):
  57          raise NotImplementedError
  58  
  59 -    def emphasis(self, on):
  60 +    def emphasis(self, on, **kw):
  61          raise NotImplementedError
  62  
  63 -    def underline(self, on):
  64 +    def underline(self, on, **kw):
  65          raise NotImplementedError
  66  
  67 -    def highlight(self, on):
  68 +    def highlight(self, on, **kw):
  69          raise NotImplementedError
  70  
  71 -    def sup(self, on):
  72 +    def sup(self, on, **kw):
  73          raise NotImplementedError
  74  
  75 -    def sub(self, on):
  76 +    def sub(self, on, **kw):
  77          raise NotImplementedError
  78  
  79 -    def strike(self, on):
  80 +    def strike(self, on, **kw):
  81          raise NotImplementedError
  82  
  83      def code(self, on, **kw):
  84          raise NotImplementedError
  85  
  86 -    def preformatted(self, on):
  87 +    def preformatted(self, on, **kw):
  88          self.in_pre = on != 0
  89  
  90 -    def small(self, on):
  91 +    def small(self, on, **kw):
  92          raise NotImplementedError
  93  
  94 -    def big(self, on):
  95 +    def big(self, on, **kw):
  96          raise NotImplementedError
  97  
  98      # special markup for syntax highlighting #############################
  99 @@ -234,10 +238,10 @@
 100      def linebreak(self, preformatted=1):
 101          raise NotImplementedError
 102  
 103 -    def paragraph(self, on):
 104 +    def paragraph(self, on, **kw):
 105          self.in_p = (on != 0)
 106  
 107 -    def rule(self, size=0):
 108 +    def rule(self, size=0, **kw):
 109          raise NotImplementedError
 110  
 111      def icon(self, type):
 112 @@ -245,22 +249,22 @@
 113  
 114      # Lists ##############################################################
 115  
 116 -    def number_list(self, on, type=None, start=None):
 117 +    def number_list(self, on, type=None, start=None, **kw):
 118          raise NotImplementedError
 119  
 120 -    def bullet_list(self, on):
 121 +    def bullet_list(self, on, **kw):
 122          raise NotImplementedError
 123  
 124      def listitem(self, on, **kw):
 125          raise NotImplementedError
 126  
 127 -    def definition_list(self, on):
 128 +    def definition_list(self, on, **kw):
 129          raise NotImplementedError
 130  
 131 -    def definition_term(self, on, compact=0):
 132 +    def definition_term(self, on, compact=0, **kw):
 133          raise NotImplementedError
 134  
 135 -    def definition_desc(self, on):
 136 +    def definition_desc(self, on, **kw):
 137          raise NotImplementedError
 138  
 139      def heading(self, on, depth, **kw):
 140 @@ -268,13 +272,13 @@
 141  
 142      # Tables #############################################################
 143      
 144 -    def table(self, on, attrs={}):
 145 +    def table(self, on, attrs={}, **kw):
 146          raise NotImplementedError
 147  
 148 -    def table_row(self, on, attrs={}):
 149 +    def table_row(self, on, attrs={}, **kw):
 150          raise NotImplementedError
 151  
 152 -    def table_cell(self, on, attrs={}):
 153 +    def table_cell(self, on, attrs={}, **kw):
 154          raise NotImplementedError
 155  
 156      # Dynamic stuff / Plugins ############################################
 157 @@ -342,7 +346,7 @@
 158  
 159          return self.text(f.getvalue())
 160  
 161 -    def escapedText(self, on):
 162 +    def escapedText(self, on, **kw):
 163          """ This allows emitting text as-is, anything special will
 164              be escaped (at least in HTML, some text output format
 165              would possibly do nothing here)
 166 diff -urN moin-1.5.0/MoinMoin/formatter/dom_xml.py moin-1.5.0-formatter-patch/MoinMoin/formatter/dom_xml.py
 167 --- moin-1.5.0/MoinMoin/formatter/dom_xml.py	2005-11-18 14:16:35.000000000 -0500
 168 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/dom_xml.py	2006-01-16 12:09:17.000000000 -0500
 169 @@ -253,7 +253,7 @@
 170          kw['type'] = 'inline'
 171          return self._add_tag('attachment', **kw)
 172  
 173 -    def rule(self, size=0):
 174 +    def rule(self, size=0, **kw):
 175          return self._add_tag('hr', size=str(size))
 176  
 177      def icon(self, type):
 178 @@ -262,41 +262,41 @@
 179      def smiley(self, type):
 180          return self._add_tag('smiley', type=type)
 181  
 182 -    def strong(self, on):
 183 +    def strong(self, on, **kw):
 184          return self._set_tag('b', on)
 185  
 186 -    def emphasis(self, on):
 187 +    def emphasis(self, on, **kw):
 188          return self._set_tag('em', on)
 189  
 190 -    def highlight(self, on):
 191 +    def highlight(self, on, **kw):
 192          return self._set_tag('highlight', on)
 193  
 194 -    def number_list(self, on, type=None, start=None):
 195 +    def number_list(self, on, type=None, start=None, **kw):
 196          return self._set_tag('ol', on, type=type, start=start)
 197  
 198 -    def bullet_list(self, on):
 199 +    def bullet_list(self, on, **kw):
 200          return self._set_tag('ul', on)
 201  
 202      def listitem(self, on, **kw):
 203          return self._set_tag('li', on)
 204  
 205 -    def sup(self, on):
 206 +    def sup(self, on, **kw):
 207          return self._set_tag('sup', on)
 208  
 209 -    def sub(self, on):
 210 +    def sub(self, on, **kw):
 211          return self._set_tag('sub', on)
 212  
 213 -    def strike(self, on):
 214 +    def strike(self, on, **kw):
 215          return self._set_tag('strike', on)
 216  
 217      def code(self, on, **kw):
 218          return self._set_tag('code', on)
 219  
 220 -    def preformatted(self, on):
 221 +    def preformatted(self, on, **kw):
 222          self.in_pre = on != 0
 223          return self._set_tag('pre', on)
 224  
 225 -    def paragraph(self, on):
 226 +    def paragraph(self, on, **kw):
 227          FormatterBase.paragraph(self, on)
 228          return self._set_tag('p', on)
 229  
 230 @@ -315,31 +315,28 @@
 231              result[str(name)] = value
 232          return result
 233  
 234 -    def table(self, on, attrs={}):
 235 +    def table(self, on, attrs={}, **kw):
 236          return self._set_tag('table', on, **self._check_attrs(attrs))
 237          
 238 -    def table_row(self, on, attrs={}):
 239 +    def table_row(self, on, attrs={}, **kw):
 240          return self._set_tag('tr', on, **self._check_attrs(attrs))
 241  
 242 -    def table_cell(self, on, attrs={}):
 243 +    def table_cell(self, on, attrs={}, **kw):
 244          return self._set_tag('td', on, **self._check_attrs(attrs))
 245  
 246      def anchordef(self, name):
 247          return self._add_tag('anchor', name=name)
 248  
 249 -    def anchorlink(self, on, name, id=None):
 250 -        kw = {}
 251 -        if id:
 252 -            kw['id'] = str(id)
 253 +    def anchorlink(self, on, name, **kw):
 254          return self.url(on, "#" + name, **kw)
 255  
 256 -    def underline(self, on):
 257 +    def underline(self, on, **kw):
 258          return self._set_tag('u', on)
 259  
 260 -    def definition_list(self, on):
 261 +    def definition_list(self, on, **kw):
 262          return self._set_tag('dl', on)
 263  
 264 -    def definition_term(self, on, compact=0):
 265 +    def definition_term(self, on, compact=0, **kw):
 266          # XXX may be not correct
 267          # self._langAttr() missing
 268          if compact and on:
 269 @@ -347,24 +344,26 @@
 270          else:
 271              return self._set_tag('dt', on)            
 272  
 273 -    def definition_desc(self, on):
 274 +    def definition_desc(self, on, **kw):
 275          # self._langAttr() missing
 276          return self._set_tag('dd', on)
 277  
 278 -    def image(self, **kw):
 279 +    def image(self, src=None, **kw):
 280          """ Take HTML <IMG> tag attributes in `attr`.
 281  
 282              Attribute names have to be lowercase!
 283          """
 284 +        if src:
 285 +            kw['src']=src
 286          return self._add_tag('img', **kw)
 287  
 288 -    def escapedText(self, text):
 289 +    def escapedText(self, text, **kw):
 290          return wikiutil.escape(text)
 291  
 292 -    def small(self, on):
 293 +    def small(self, on, **kw):
 294          return self._set_tag('small', on)
 295  
 296 -    def big(self, on):
 297 +    def big(self, on, **kw):
 298          return self._set_tag('big', on)
 299  
 300      def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
 301 diff -urN moin-1.5.0/MoinMoin/formatter/text_gedit.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_gedit.py
 302 --- moin-1.5.0/MoinMoin/formatter/text_gedit.py	2005-12-04 07:37:54.000000000 -0500
 303 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_gedit.py	2006-01-16 12:06:31.000000000 -0500
 304 @@ -140,13 +140,13 @@
 305  
 306      # Change nesting: sub lists are no longer within the <li> tags
 307      
 308 -    def number_list(self, on, type=None, start=None):
 309 +    def number_list(self, on, type=None, start=None, **kw):
 310          li = ""
 311          if self._in_li: # close <li>
 312              li = self.listitem(False)
 313          return li + text_html.Formatter.number_list(self, on, type, start)
 314  
 315 -    def bullet_list(self, on):
 316 +    def bullet_list(self, on, **kw):
 317          li = ""
 318          if self._in_li: # close <li>
 319              li = self.listitem(False)
 320 @@ -197,7 +197,7 @@
 321          attrs = text_html.Formatter._checkTableAttr(self, attrs, prefix)
 322          return self._style_to_attributes(attrs)
 323  
 324 -    def table(self, on, attrs=None):
 325 +    def table(self, on, attrs=None, **kw):
 326          """ Create table
 327  
 328          @param on: start table
 329 @@ -221,29 +221,31 @@
 330  
 331          return ''.join(result)    
 332  
 333 -    def comment(self, text):
 334 +    def comment(self, text, **kw):
 335          text = text.rstrip() # workaround for growing amount of blanks at EOL
 336          return self.preformatted(1, attr={'class': 'comment'}) + text + self.preformatted(0)
 337  
 338 -    def underline(self, on):
 339 +    def underline(self, on, **kw):
 340          tag = 'u'
 341          if on:
 342              return self.open(tag)
 343          return self.close(tag)
 344                      
 345 -    def strong(self, on):
 346 +    def strong(self, on, **kw):
 347          tag = 'b'
 348          if on:
 349              return self.open(tag)
 350          return self.close(tag)
 351  
 352 -    def emphasis(self, on):
 353 +    def emphasis(self, on, **kw):
 354          tag = 'i'
 355          if on:
 356              return self.open(tag)
 357          return self.close(tag)
 358  
 359 -    def code(self, on, css=None):
 360 +    def code(self, on, css=None, **kw):
 361 +        if not css and kw.has_key('css_class'):
 362 +            css=kw['css_class']
 363          tag = 'tt'
 364          # Maybe we don't need this, because we have tt will be in inlineStack.
 365          self._in_code = on
 366 diff -urN moin-1.5.0/MoinMoin/formatter/text_html.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_html.py
 367 --- moin-1.5.0/MoinMoin/formatter/text_html.py	2005-12-04 07:37:54.000000000 -0500
 368 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_html.py	2006-01-19 13:42:06.000000000 -0500
 369 @@ -11,12 +11,171 @@
 370  from MoinMoin.Page import Page
 371  from MoinMoin.action import AttachFile
 372  
 373 +# TODO: when we require Python 2.3+ use the 'Set' type from the sets module.
 374 +# TODO: when we require Python 2.4+ use the builtin 'set' type
 375 +class Set:
 376 +    def __init__(self, *items):
 377 +        self._dict = {}
 378 +        if items:
 379 +            self._dict = dict( [(i,1) for i in list(items)] )
 380 +
 381 +    def __contains__(self, item):
 382 +        if self._dict.has_key(item):
 383 +            return True
 384 +        return False
 385 +
 386 +# These are the HTML elements that we treat as block elements.
 387 +_blocks = Set('dd', 'div', 'dl', 'dt', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
 388 +              'hr', 'li', 'ol', 'p', 'pre', 'table', 'tbody', 'td', 'tfoot', 'th',
 389 +              'thead', 'tr', 'ul')
 390 +
 391 +# These are the HTML elements which are typically only used with
 392 +# an opening tag without a separate closing tag.  We do not
 393 +# include 'script' or 'style' because sometimes they do have
 394 +# content, and also IE has a parsing bug with those two elements (only)
 395 +# when they don't have a closing tag even if valid XHTML.
 396 +
 397 +_self_closing_tags = Set('area','base', 'br', 'col', 'frame', 'hr', 'img', 'input',
 398 +                         'isindex', 'link', 'meta', 'param')
 399 +
 400 +# These are the elements which generally should cause an increase in the
 401 +# indention level in the html souce code.
 402 +_indenting_tags = Set('ol', 'ul', 'dl', 'li', 'dt', 'dd', 'tr', 'td')
 403 +
 404 +# These are the elements that discard any whitespace they contain as
 405 +# immediate child nodes.
 406 +_space_eating_tags = Set('colgroup', 'dl', 'frameset', 'head', 'map' 'menu',
 407 +                         'ol', 'optgroup', 'select', 'table', 'tbody', 'tfoot',
 408 +                         'thead', 'tr', 'ul')
 409 +
 410 +# These are standard HTML attributes which are typically used without any
 411 +# value; e.g., as boolean flags indicated by their presence.
 412 +_html_attribute_boolflags = Set('compact', 'disabled', 'ismap', 'nohref',
 413 +                                'noresize', 'noshade', 'nowrap', 'readonly',
 414 +                                'selected', 'wrap')
 415 +
 416 +# These are all the standard HTML attributes that are allowed on any element.
 417 +_common_attributes = Set('accesskey', 'class', 'dir', 'disabled', 'id', 'lang',
 418 +                         'style', 'tabindex', 'title')
 419 +
 420 +
 421 +def rewrite_attribute_name(name, default_namespace='html'):
 422 +    """Takes an attribute name and tries to make it HTML correct.
 423 +
 424 +    This function takes an attribute name as a string, as it may be
 425 +    passed in to a formatting method using a keyword-argument syntax,
 426 +    and tries to convert it into a real attribute name.  This is
 427 +    necessary because some attributes may conflict with Python
 428 +    reserved words or variable syntax (such as 'for', 'class', or
 429 +    'z-index'); and also to help with backwards compatibility with
 430 +    older versions of MoinMoin where different names may have been
 431 +    used (such as 'content_id' or 'css').
 432 +
 433 +    Returns a tuple of strings: (namespace, attribute).
 434 +
 435 +    Namespaces: The default namespace is always assumed to be 'html',
 436 +    unless the input string contains a colon or a double-underscore;
 437 +    in which case the first such occurance is assumed to separate the
 438 +    namespace prefix from name.  So, for example, to get the HTML
 439 +    attribute 'for' (as on a <label> element), you can pass in the
 440 +    string 'html__for' or even '__for'.
 441 +
 442 +    Hyphens:  To better support hyphens (which are not allowed in Python
 443 +    variable names), all occurances of two underscores will be replaced
 444 +    with a hyphen.  If you use this, then you must also provide a
 445 +    namespace since the first occurance of '__' separates a namespace
 446 +    from the name.
 447 +
 448 +    Special cases: Within the 'html' namespace, mainly to preserve
 449 +    backwards compatibility, these exceptions ars recognized:
 450 +    'content_type', 'content_id', 'css_class', and 'css'.
 451 +    Additionally all html attributes starting with 'on' are forced to
 452 +    lower-case.  Also the string 'xmlns' is recognized as having
 453 +    no namespace.
 454 +
 455 +    Examples:
 456 +        'id' -> ('html', 'id')
 457 +        'css_class' -> ('html', 'class')
 458 +        'content_id' -> ('html', 'id')
 459 +        'content_type' -> ('html', 'type')
 460 +        'html__for' -> ('html', 'for)
 461 +        'xml__space' -> ('xml', 'space')
 462 +        '__z__index' -> ('html', 'z-index')
 463 +        '__http__equiv' -> ('html', 'http-equiv')
 464 +        'onChange' -> ('html', 'onchange')
 465 +        'xmlns' -> ('', 'xmlns')
 466 +        'xmlns__abc' -> ('xmlns', 'abc')
 467 +
 468 +    (In actuality we only deal with namespace prefixes, not any real
 469 +    namespace URI...we only care about the syntax not the meanings.)
 470 +    """
 471 +
 472 +    # Handle any namespaces (just in case someday we support XHTML)
 473 +    if ':' in name:
 474 +        ns, name = name.split(':',1)
 475 +    elif '__' in name:
 476 +        ns, name = name.split('__',1)
 477 +    elif name == 'xmlns':
 478 +        ns = ''
 479 +    else:
 480 +        ns = default_namespace
 481 +
 482 +    name.replace('__', '-')
 483 +    if ns == 'html':
 484 +        # We have an HTML attribute, fix according to DTD
 485 +        if name == 'content_type': # MIME type such as in <a> and <link> elements
 486 +            name =  'type'
 487 +        elif name == 'content_id': # moin historical convention
 488 +            name =  'id'
 489 +        elif name in ('css_class', 'css'): # to avoid python word 'class'
 490 +            name = 'class'
 491 +        elif name.startswith('on'): # event handler hook
 492 +            name = name.lower()
 493 +    return ns, name
 494 +
 495 +
 496 +def extend_attribute_dictionary(attributedict, ns, name, value):
 497 +    """Add a new attribute to an attribute dictionary, merging values where possible.
 498 +
 499 +    The attributedict must be a dictionary with tuple-keys of the form:
 500 +    (namespace, attrname).
 501 +
 502 +    The given ns, name, and value will be added to the dictionary.  It
 503 +    will replace the old value if it existed, except for a few special
 504 +    cases where the values are logically merged instead (CSS class
 505 +    names and style rules).
 506 +
 507 +    As a special case, if value is None (not just ''), then the
 508 +    attribute is actually deleted from the dictionary.
 509 +    """
 510 +
 511 +    key = ns, name
 512 +    if value is None:
 513 +        if attributedict.has_key(key):
 514 +            del attributedict[key]
 515 +    else:
 516 +        if ns == 'html' and attributedict.has_key(key):
 517 +            if name == 'class':
 518 +                # CSS classes are appended by space-separated list
 519 +                value = attributedict[key] + ' ' + value
 520 +            elif name == 'style':
 521 +                # CSS styles are appended by semicolon-separated rules list
 522 +                value = attributedict[key] + '; ' + value
 523 +            elif name in _html_attribute_boolflags:
 524 +                # All attributes must have a value. According to XHTML those
 525 +                # traditionally used as flags should have their value set to
 526 +                # the same as the attribute name.
 527 +                value = name
 528 +        attributedict[key] = value
 529 +
 530 +
 531  class Formatter(FormatterBase):
 532      """
 533          Send HTML data.
 534      """
 535  
 536      hardspace = '&nbsp;'
 537 +    indentspace = '  '
 538  
 539      def __init__(self, request, **kw):
 540          apply(FormatterBase.__init__, (self, request), kw)
 541 @@ -26,8 +185,12 @@
 542          # the stack are closed.
 543          self._inlineStack = []
 544  
 545 -        self._in_li = 0
 546 -        self._in_code = 0
 547 +        # stack of all tags
 548 +        self._tag_stack = []
 549 +        self._indent_level = 0
 550 +
 551 +        self._in_li = 0  # not useful here, but used by text_gedit
 552 +        self._in_code = 0 # used by text_gedit
 553          self._in_code_area = 0
 554          self._in_code_line = 0
 555          self._code_area_num = 0
 556 @@ -67,36 +230,115 @@
 557                  # lang is inherited from content div
 558                  return {}
 559  
 560 -        attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
 561 +        attr = {'xml:lang': lang, 'lang': lang, 'dir': i18n.getDirection(lang),}
 562          return attr
 563  
 564 -    def formatAttributes(self, attr=None):
 565 -        """ Return formatted attributes string
 566 +    def formatAttributes(self, attr=None, allowed_attrs=None, **kw):
 567 +        """ Return HTML attributes formatted as a single string.
 568  
 569          @param attr: dict containing keys and values
 570 -        @rtype: string ?
 571 +        @param allowed_attrs: A list of allowable attribute names
 572 +        @param **kw: other arbitrary attributes expressed as keyword arguments.
 573 +        @rtype: string
 574          @return: formated attributes or empty string
 575 +
 576 +        The attributes and their values can either be given in the
 577 +        'attr' dictionary, or as extra keyword arguments.  They are
 578 +        both merged together.  See the function
 579 +        rewrite_attribute_name() for special notes on how to name
 580 +        attributes.
 581 +
 582 +        Setting a value to None rather than a string (or string
 583 +        coercible) will remove that attribute from the list.
 584 +        
 585 +        If the list of allowed_attrs is provided, then an error is
 586 +        raised if an HTML attribute is encountered that is not in that
 587 +        list (or is not a common attribute which is always allowed or
 588 +        is not in another XML namespace using the double-underscore
 589 +        syntax).
 590          """
 591 +
 592 +        # Merge the attr dict and kw dict into a single attributes
 593 +        # dictionary (rewriting any attribute names, extracting
 594 +        # namespaces, and merging some values like css classes).
 595 +        attributes = {} # dict of key=(namespace,name): value=attribute_value
 596          if attr:
 597 -            attr = [' %s="%s"' % (k, v) for k, v in attr.items()]           
 598 -            return ''.join(attr)
 599 +            for a, v in attr.items():
 600 +                a_ns, a_name = rewrite_attribute_name(a)
 601 +                extend_attribute_dictionary(attributes, a_ns, a_name, v)
 602 +        if kw:
 603 +            for a, v in kw.items():
 604 +                a_ns, a_name = rewrite_attribute_name(a)
 605 +                extend_attribute_dictionary(attributes, a_ns, a_name, v)
 606 +
 607 +        # Add title attribute if missing, but it has an alt.
 608 +        if attributes.has_key(('html','alt')) and not attributes.has_key(('html','title')):
 609 +            attributes[('html','title')] = attributes[('html','alt')]
 610 +
 611 +        # Force both lang and xml:lang to be present and identical if
 612 +        # either exists.  The lang takes precedence over xml:lang if
 613 +        # both exist.
 614 +        if attributes.has_key(('html','lang')):
 615 +            attributes[('xml','lang')] = attributes[('html','lang')]
 616 +        elif attributes.has_key(('xml','lang')):
 617 +            attributes[('html','lang')] = attributes[('xml','lang')]
 618 +
 619 +        # Check all the HTML attributes to see if they are known and
 620 +        # allowed.  Ignore attributes if in non-HTML namespaces.
 621 +        if allowed_attrs:
 622 +            for name in [key[1] for key in attributes.keys() if key[0] == 'html']:
 623 +                if name in _common_attributes or name in allowed_attrs:
 624 +                    pass
 625 +                elif name.startswith('on'):
 626 +                    pass  # Too many event handlers to enumerate, just let them all pass.
 627 +                else:
 628 +                    # Unknown or unallowed attribute.
 629 +                    err = 'Illegal HTML attribute "%s" passed to formatter' % name
 630 +                    raise ValueError(err)
 631 +
 632 +        # Finally, format them all as a single string.
 633 +        if attributes:
 634 +            # Construct a formatted string containing all attributes
 635 +            # with their values escaped.  Any html:* namespace
 636 +            # attributes drop the namespace prefix.  We build this by
 637 +            # separating the attributes into three categories:
 638 +            #
 639 +            #  * Those without any namespace (should only be xmlns attributes)
 640 +            #  * Those in the HTML namespace (we drop the html: prefix for these)
 641 +            #  * Those in any other non-HTML namespace, including xml:
 642 +
 643 +            xmlnslist = ['%s="%s"' % (k[1], wikiutil.escape(v,1))
 644 +                         for k, v in attributes.items() if not k[0]]
 645 +            htmllist = ['%s="%s"' % (k[1], wikiutil.escape(v,1))
 646 +                        for k, v in attributes.items() if k[0] == 'html']
 647 +            otherlist = ['%s:%s="%s"' % (k[0], k[1], wikiutil.escape(v,1))
 648 +                         for k, v in attributes.items() if k[0] and k[0] != 'html']
 649 +
 650 +            # Join all these lists together in a space-separated string.  Also
 651 +            # prefix the whole thing with a space too.
 652 +            htmllist.sort()
 653 +            otherlist.sort()
 654 +            all = [''] + xmlnslist + htmllist + otherlist
 655 +            return ' '.join(all)
 656          return ''
 657  
 658 -    # TODO: use set when we require Python 2.3
 659 -    # TODO: The list is not complete, add missing from dtd
 660 -    _blocks = 'p div pre table tr td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
 661 -    _blocks = dict(zip(_blocks.split(), [1] * len(_blocks)))
 662 -
 663 -    def open(self, tag, newline=False, attr=None):
 664 +    def open(self, tag, newline=False, attr=None, allowed_attrs=None, **kw):
 665          """ Open a tag with optional attributes
 666          
 667          @param tag: html tag, string
 668 -        @param newline: render tag on a separate line
 669 -        @parm attr: dict with tag attributes
 670 +        @param newline: render tag so following data is on a separate line
 671 +        @param attr: dict with tag attributes
 672 +        @param allowed_attrs: list of allowed attributes for this element
 673 +        @param kw: arbitrary attributes and values
 674          @rtype: string ?
 675 -        @return: open tag with attributes
 676 +        @return: open tag with attributes as a string
 677          """
 678 -        if tag in self._blocks:
 679 +        is_self_closing = ''
 680 +        if tag in _self_closing_tags:
 681 +            # Don't expect a closing tag later on.
 682 +            is_self_closing = ' /'
 683 +
 684 +        if tag in _blocks:
 685              # Block elements
 686              result = []
 687              
 688 @@ -106,26 +348,39 @@
 689                  attributes.update(attr)
 690              
 691              # Format
 692 -            attributes = self.formatAttributes(attributes)
 693 -            result.append('<%s%s>' % (tag, attributes))
 694 +            attributes = self.formatAttributes(attributes, allowed_attrs=allowed_attrs, **kw)
 695 +            result.append('<%s%s%s>' % (tag, attributes, is_self_closing))
 696              if newline:
 697 -                result.append('\n')
 698 -            return ''.join(result)
 699 +                result.append(self._newline())
 700 +            tagstr = ''.join(result)
 701          else:
 702              # Inline elements
 703              # Add to inlineStack
 704 -            self._inlineStack.append(tag)
 705 +            if not is_self_closing:
 706 +                # Only push on stack if we expect a close-tag later
 707 +                self._inlineStack.append(tag)
 708              # Format
 709 -            return '<%s%s>' % (tag, self.formatAttributes(attr))
 710 -       
 711 +            tagstr = '<%s%s%s>' % (tag,
 712 +                                      self.formatAttributes(attr, allowed_attrs, **kw),
 713 +                                      is_self_closing)
 714 +        if not self.close:
 715 +            self._tag_stack.append(tag)
 716 +            if tag in _indenting_tags:
 717 +                self._indent_level = self._indent_level + 1
 718 +        return tagstr
 719 +
 720      def close(self, tag, newline=False):
 721          """ Close tag
 722  
 723          @param tag: html tag, string
 724 -        @rtype: string ?
 725 -        @return: closing tag
 726 +        @param newline: render tag so following data is on a separate line
 727 +        @rtype: string
 728 +        @return: closing tag as a string
 729          """
 730 -        if tag in self._blocks:
 731 +        if tag in _self_closing_tags:
 732 +            # This tag was already closed
 733 +            tagstr = ''
 734 +        elif tag in _blocks:
 735              # Block elements
 736              # Close all tags in inline stack
 737              # Work on a copy, because close(inline) manipulate the stack
 738 @@ -136,25 +391,38 @@
 739                  result.append(self.close(inline))
 740              # Format with newline
 741              if newline:
 742 -                result.append('\n')
 743 -            result.append('</%s>\n' % (tag))
 744 -            return ''.join(result)            
 745 +                result.append(self._newline())
 746 +            result.append('</%s>' % (tag))
 747 +            tagstr = ''.join(result)            
 748          else:
 749              # Inline elements 
 750              # Pull from stack, ignore order, that is not our problem.
 751              # The code that calls us should keep correct calling order.
 752              if tag in self._inlineStack:
 753                  self._inlineStack.remove(tag)
 754 -            return '</%s>' % tag
 755 +            tagstr = '</%s>' % tag
 756  
 757 +        if tag in _self_closing_tags:
 758 +            self._tag_stack.pop()
 759 +            if tag in _indenting_tags:
 760 +                # decrease indent level
 761 +                self._indent_level = self._indent_level - 1
 762 +        if newline:
 763 +            tagstr += self._newline()
 764 +        return tagstr
 765  
 766      # Public methods ###################################################
 767  
 768 -    def startContent(self, content_id='content', **kwargs):
 769 -        """ Start page content div """
 770 +    def startContent(self, content_id='content', newline=True, **kw):
 771 +        """ Start page content div.
 772 +
 773 +        A link anchor is provided at the beginning of the div, with
 774 +        an id of 'top' or 'top_xxxx' if the content_id argument is
 775 +        set to 'xxxx'.
 776 +        """
 777  
 778          # Setup id
 779 -        if content_id!='content':
 780 +        if content_id != 'content':
 781              aid = 'top_%s' % (content_id,)
 782          else:
 783              aid = 'top'
 784 @@ -163,12 +431,20 @@
 785          # Use the content language
 786          attr = self.langAttr(self.request.content_lang)
 787          attr['id'] = content_id
 788 -        result.append(self.open('div', newline=1, attr=attr))
 789 +        result.append(self.open('div', newline=False, attr=attr,
 790 +                                allowed_attrs=['align'], **kw))
 791          result.append(self.anchordef(aid))
 792 +        if newline:
 793 +            result.append('\n')
 794          return ''.join(result)
 795          
 796 -    def endContent(self):
 797 -        """ Close page content div """
 798 +    def endContent(self,newline=True):
 799 +        """ Close page content div.
 800 +
 801 +        A link anchor is provided at the end of the div, with
 802 +        an id of 'bottom' or 'bottom_xxxx' if the content_id argument
 803 +        to the previus startContent() call was set to 'xxxx'.
 804 +        """
 805  
 806          # Setup id
 807          try:
 808 @@ -182,7 +458,7 @@
 809  
 810          result = []
 811          result.append(self.anchordef(aid))
 812 -        result.append(self.close('div', newline=1))
 813 +        result.append(self.close('div', newline=newline))
 814          return ''.join(result) 
 815  
 816      def lang(self, on, lang_name):
 817 @@ -205,7 +481,7 @@
 818      def sysmsg(self, on, **kw):
 819          tag = 'div'
 820          if on:
 821 -            return self.open(tag, attr={'class': 'message'})
 822 +            return self.open(tag, attr={'class': 'message'}, **kw)
 823          return self.close(tag)
 824      
 825      # Links ##############################################################
 826 @@ -237,7 +513,7 @@
 827          @keyword title: override using the interwiki wikiname as title
 828          """
 829          if not on:
 830 -            return '</a>'
 831 +            return self.url(0)
 832          wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
 833          wikiurl = wikiutil.mapURL(self.request, wikiurl)
 834          if wikitag == 'Self': # for own wiki, do simple links
 835 @@ -259,41 +535,96 @@
 836              # unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
 837  
 838      def url(self, on, url=None, css=None, **kw):
 839 -        """
 840 +        """ Inserts an <a> element.
 841 +
 842 +            Call once with on=1 to start the link, and again with on=0
 843 +            to end it (no other arguments are needed when on==0).
 844 +
 845              Keyword params:
 846 -                title - <a> title attribute
 847 -                attrs -  just include those <a> attrs "as is"
 848 +                url - the URL to link to; will go through Wiki URL mapping.
 849 +                css - a space-separated list of CSS classes
 850 +                attrs -  just include this string verbatim inside
 851 +                         the <a> element; can be used for arbitrary attrs;
 852 +                         all escaping and quoting is the caller's responsibility.
 853 +
 854 +            Note that the 'attrs' keyword argument is for backwards compatibility
 855 +            only.  It should not be used for new code--instead just pass
 856 +            any attributes in as separate keyword arguments.
 857          """
 858 +        if not on:
 859 +            return self.close('a')
 860 +        attrs = self.langAttr()
 861 +
 862 +        # Handle the URL mapping
 863 +        if url is None and kw.has_key('href'):
 864 +            url = kw['href']
 865 +            del kw['href']
 866          if url is not None:
 867              url = wikiutil.mapURL(self.request, url)
 868 -        title = kw.get('title', None)
 869 -        attrs = kw.get('attrs', None)
 870 +            attrs['href'] = url
 871 +
 872 +        if css:
 873 +            attrs['class'] = css
 874 +        
 875 +        if kw.has_key('attrs'):
 876 +            # for backwards compatibility, raw pre-formated attribute string
 877 +            extra_attrs = kw['attrs']
 878 +            del kw['attrs']
 879 +        else:
 880 +            extra_attrs = None
 881 +
 882 +        # create link
 883          if on:
 884 -            str = '<a'
 885 -            if css: 
 886 -                str = '%s class="%s"' % (str, css)
 887 -            if title:
 888 -                str = '%s title="%s"' % (str, title)
 889 -            if attrs:
 890 -                str = '%s %s' % (str, attrs)
 891 -            str = '%s href="%s">' % (str, wikiutil.escape(url, 1))
 892 +            str = self.open('a', attr=attrs, **kw)
 893 +            if extra_attrs:
 894 +                # insert this into the tag verbatim (messy)
 895 +                if str[-2:] == '/>':
 896 +                    str = '%s %s />' % (str[:-2], extra_attrs)
 897 +                else:
 898 +                    str = '%s %s>' % (str[:-1], extra_attrs)
 899          else:
 900 -            str = '</a>'
 901 +            str = self.close('a')
 902          return str
 903  
 904      def anchordef(self, id):
 905 -        #return '<a id="%s"></a>' % (id, ) # this breaks PRE sections for IE
 906 -        # do not add a \n here, it breaks pre sections with line_anchordef
 907 -        return '<span id="%s" class="anchor"></span>' % (id, )
 908 +        """Inserts an invisible element used as a link target.
 909 +
 910 +        Inserts an empty <span> element with an id attribute, used as an anchor
 911 +        for link references.  We use <span></span> rather than <span/>
 912 +        for browser portability.
 913 +        """
 914 +        # Don't add newlines, \n, as it will break pre and
 915 +        # line-numbered code sections (from line_achordef() method).
 916 +        return '<span class="anchor" id="%s"></span>' % wikiutil.escape(id, 1)
 917  
 918      def line_anchordef(self, lineno):
 919          return self.anchordef("line-%d" % lineno)
 920  
 921 -    def anchorlink(self, on, name='', id=None):
 922 -        extra = ''
 923 -        if id:
 924 -            extra = ' id="%s"' % id
 925 -        return ['<a href="#%s"%s>' % (name, extra), '</a>'][not on]
 926 +    def anchorlink(self, on, name='', **kw):
 927 +        """Insert an <a> link pointing to an anchor on the same page.
 928 +
 929 +        Call once with on=1 to start the link, and a second time with
 930 +        on=0 to end it.  No other arguments are needed on the second
 931 +        call.
 932 +
 933 +        The name argument should be the same as the id provided to the
 934 +        anchordef() method, or some other elment.  It should NOT start
 935 +        with '#' as that will be added automatically.
 936 +
 937 +        The id argument, if provided, is instead the id of this link
 938 +        itself and not of the target element the link references.
 939 +        """
 940 +
 941 +        attrs = self.langAttr()
 942 +        if name:
 943 +            attrs['href'] = '#%s' % name
 944 +        if kw.has_key('href'):
 945 +            del kw['href']
 946 +        if on:
 947 +            str = self.open('a', attr=attrs, **kw)
 948 +        else:
 949 +            str = self.close('a')
 950 +        return str
 951  
 952      def line_anchorlink(self, on, lineno=0):
 953          return self.anchorlink(on, name="line-%d" % lineno)
 954 @@ -415,81 +746,143 @@
 955  
 956      # Inline ###########################################################
 957          
 958 -    def strong(self, on):
 959 +    def strong(self, on, **kw):
 960 +        """Creates an HTML <strong> element.
 961 +
 962 +        Call once with on=1 to start the region, and a second time
 963 +        with on=0 to end it.
 964 +        """
 965 +
 966          tag = 'strong'
 967          if on:
 968 -            return self.open(tag)
 969 +            return self.open(tag, allowed_attrs=[], **kw)
 970          return self.close(tag)
 971  
 972 -    def emphasis(self, on):
 973 +    def emphasis(self, on, **kw):
 974 +        """Creates an HTML <em> element.
 975 +
 976 +        Call once with on=1 to start the region, and a second time
 977 +        with on=0 to end it.
 978 +        """
 979 +
 980          tag = 'em'
 981          if on:
 982 -            return self.open(tag)
 983 +            return self.open(tag, allowed_attrs=[], **kw)
 984          return self.close(tag)
 985  
 986 -    def underline(self, on):
 987 +    def underline(self, on, **kw):
 988 +        """Creates a text span for underlining (css class "u").
 989 +
 990 +        Call once with on=1 to start the region, and a second time
 991 +        with on=0 to end it.
 992 +        """
 993          tag = 'span'
 994          if on:
 995 -            return self.open(tag, attr={'class': 'u'})
 996 +            return self.open(tag, attr={'class': 'u'}, allowed_attrs=[], **kw)
 997          return self.close(tag)
 998  
 999 -    def highlight(self, on):
1000 +    def highlight(self, on, **kw):
1001 +        """Creates a text span for highlighting (css class "highlight").
1002 +
1003 +        Call once with on=1 to start the region, and a second time
1004 +        with on=0 to end it.
1005 +        """
1006          tag = 'strong'
1007          if on:
1008 -            return self.open(tag, attr={'class': 'highlight'})
1009 +            return self.open(tag, attr={'class': 'highlight'}, allowed_attrs=[], **kw)
1010          return self.close(tag)
1011  
1012 -    def sup(self, on):
1013 +    def sup(self, on, **kw):
1014 +        """Creates a <sup> element for superscript text.
1015 +
1016 +        Call once with on=1 to start the region, and a second time
1017 +        with on=0 to end it.
1018 +        """
1019          tag = 'sup'
1020          if on:
1021 -            return self.open(tag)
1022 +            return self.open(tag, allowed_attrs=[], **kw)
1023          return self.close(tag)
1024  
1025 -    def sub(self, on):
1026 +    def sub(self, on, **kw):
1027 +        """Creates a <sub> element for subscript text.
1028 +
1029 +        Call once with on=1 to start the region, and a second time
1030 +        with on=0 to end it.
1031 +        """
1032          tag = 'sub'
1033          if on:
1034 -            return self.open(tag)
1035 +            return self.open(tag, allowed_attrs=[], **kw)
1036          return self.close(tag)
1037  
1038 -    def strike(self, on):
1039 -        tag = 'strike'
1040 +    def strike(self, on, **kw):
1041 +        """Creates a text span for line-through (strikeout) text (css class 'strike').
1042 +
1043 +        Call once with on=1 to start the region, and a second time
1044 +        with on=0 to end it.
1045 +        """
1046 +        # This does not use <strike> because has been deprecated in standard HTML.
1047 +        tag = 'span'
1048          if on:
1049 -            return self.open(tag)
1050 +            return self.open(tag, attr={'class': 'strike'},
1051 +                             allowed_attrs=[], **kw)
1052          return self.close(tag)
1053  
1054      def code(self, on, **kw):
1055 +        """Creates a <tt> element for inline code or monospaced text.
1056 +
1057 +        Call once with on=1 to start the region, and a second time
1058 +        with on=0 to end it.
1059 +
1060 +        Any text within this section will have spaces converted to
1061 +        non-break spaces.
1062 +        """
1063          tag = 'tt'
1064          # Maybe we don't need this, because we have tt will be in inlineStack.
1065          self._in_code = on        
1066          if on:
1067 -            return self.open(tag)
1068 +            return self.open(tag, allowed_attrs=[], **kw)
1069          return self.close(tag)
1070          
1071 -    def small(self, on):
1072 +    def small(self, on, **kw):
1073 +        """Creates a <small> element for smaller font.
1074 +
1075 +        Call once with on=1 to start the region, and a second time
1076 +        with on=0 to end it.
1077 +        """
1078          tag = 'small'
1079          if on:
1080 -            return self.open(tag)
1081 +            return self.open(tag, allowed_attrs=[], **kw)
1082          return self.close(tag)
1083  
1084 -    def big(self, on):
1085 +    def big(self, on, **kw):
1086 +        """Creates a <big> element for larger font.
1087 +
1088 +        Call once with on=1 to start the region, and a second time
1089 +        with on=0 to end it.
1090 +        """
1091          tag = 'big'
1092          if on:
1093 -            return self.open(tag)
1094 +            return self.open(tag, allowed_attrs=[], **kw)
1095          return self.close(tag)
1096  
1097  
1098      # Block elements ####################################################
1099  
1100 -    def preformatted(self, on, attr=None):
1101 +    def preformatted(self, on, **kw):
1102 +        """Creates a preformatted text region, with a <pre> element.
1103 +
1104 +        Call once with on=1 to start the region, and a second time
1105 +        with on=0 to end it.
1106 +        """
1107          FormatterBase.preformatted(self, on)
1108          tag = 'pre'
1109          if on:
1110 -            return self.open(tag, newline=1, attr=attr)
1111 +            return self.open(tag, newline=1, **kw)
1112          return self.close(tag)
1113                  
1114      # Use by code area
1115      _toggleLineNumbersScript = """
1116 -<script type="text/JavaScript">
1117 +<script type="text/javascript">
1118  function isnumbered(obj) {
1119    return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber';
1120  }
1121 @@ -541,6 +934,19 @@
1122  """
1123      
1124      def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
1125 +        """Creates a formatted code region, with line numbering.
1126 +
1127 +        This region is formatted as a <div> with a <pre> inside it.  The
1128 +        code_id argument is assigned to the 'id' of the div element, and
1129 +        must be unique within the document.  The show, start, and step are
1130 +        used for line numbering.
1131 +
1132 +        Note this is not like most formatter methods, it can not take any
1133 +        extra keyword arguments.
1134 +
1135 +        Call once with on=1 to start the region, and a second time
1136 +        with on=0 to end it.
1137 +        """
1138          res = []
1139          ci = self.request.makeUniqueID('CA-%s_%03d' % (code_id, self._code_area_num))
1140          if on:
1141 @@ -562,7 +968,7 @@
1142              if self._code_area_state[1] >= 0:
1143                  toggleLineNumbersLink = r'''
1144  <script type="text/javascript">
1145 -document.write('<a href="#" onClick="return togglenumber(\'%s\', %d, %d);" \
1146 +document.write('<a href="#" onclick="return togglenumber(\'%s\', %d, %d);" \
1147                  class="codenumbers">Toggle line numbers<\/a>');
1148  </script>
1149  ''' % (self._code_area_state[0], self._code_area_state[2], self._code_area_state[3])
1150 @@ -602,12 +1008,32 @@
1151  
1152      # Paragraphs, Lines, Rules ###########################################
1153      
1154 +    def _indent_spaces(self):
1155 +        """Returns space(s) for indenting the html source so list nesting is easy to read.
1156 +
1157 +        Note that this mostly works, but because of caching may not always be accurate."""
1158 +        return self.indentspace * self._indent_level
1159 +
1160 +    def _newline(self):
1161 +        """Returns the whitespace for starting a new html source line, properly indented."""
1162 +        return '\n' + self._indent_spaces()
1163 +
1164      def linebreak(self, preformatted=1):
1165 +        """Creates a line break in the HTML output.
1166 +        
1167 +        If preformatted is true a <br> element is inserted, otherwise
1168 +        the linebreak will only be visible in the HTML source.
1169 +        """
1170          if self._in_code_area:
1171              preformatted = 1
1172 -        return ['\n', '<br>\n'][not preformatted]
1173 +        return ['\n', '<br />\n'][not preformatted] + self._indent_spaces()
1174 +        
1175 +    def paragraph(self, on, **kw):
1176 +        """Creates a paragraph with a <p> element.
1177          
1178 -    def paragraph(self, on):
1179 +        Call once with on=1 to start the region, and a second time
1180 +        with on=0 to end it.
1181 +        """
1182          if self._terse:
1183              return ''
1184          FormatterBase.paragraph(self, on)
1185 @@ -615,14 +1041,22 @@
1186              self._in_li = self._in_li + 1
1187          tag = 'p'
1188          if on:
1189 -            return self.open(tag)
1190 -        return self.close(tag)
1191 -            
1192 -    def rule(self, size=None):
1193 -        if size:
1194 +            tagstr = self.open(tag, **kw)
1195 +        else:
1196 +            tagstr = self.close(tag)
1197 +        return tagstr
1198 +
1199 +    def rule(self, size=None, **kw):
1200 +        """Creates a horizontal rule with an <hr> element.
1201 +
1202 +        If size is a number in the range [1..6], the CSS class of the rule
1203 +        is set to 'hr1' through 'hr6'.  The intent is that the larger the
1204 +        size number the thicker or bolder the rule will be.
1205 +        """
1206 +        if size and 1 <= size <= 6:
1207              # Add hr class: hr1 - hr6
1208 -            return self.open('hr', newline=1, attr={'class': 'hr%d' % size})
1209 -        return self.open('hr', newline=1)
1210 +            return self.open('hr', newline=1, attr={'class': 'hr%d' % size}, **kw)
1211 +        return self.open('hr', newline=1, **kw)
1212                  
1213      def icon(self, type):
1214          return self.request.theme.make_icon(type)
1215 @@ -634,9 +1068,30 @@
1216              href = self.request.theme.img_url(img)
1217          return self.image(src=href, alt=text, width=str(w), height=str(h))
1218  
1219 +    def image(self, src=None, **kw):
1220 +        """Creates an inline image with an <img> element.
1221 +
1222 +        The src argument must be the URL to the image file.
1223 +        """
1224 +        if src:
1225 +            kw['src']=src
1226 +        return self.open('img',**kw)
1227 +
1228      # Lists ##############################################################
1229  
1230 -    def number_list(self, on, type=None, start=None):
1231 +
1232 +    def number_list(self, on, type=None, start=None, **kw):
1233 +        """Creates an HTML ordered list, <ol> element.
1234 +
1235 +        The 'type' if specified can be any legal numbered
1236 +        list-style-type, such as 'decimal','lower-roman', etc.
1237 +
1238 +        The 'start' argument if specified gives the numeric value of
1239 +        the first list item (default is 1).
1240 +
1241 +        Call once with on=1 to start the list, and a second time
1242 +        with on=0 to end it.
1243 +        """
1244          tag = 'ol'
1245          if on:
1246              attr = {}
1247 @@ -644,49 +1099,82 @@
1248                  attr['type'] = type
1249              if start is not None:
1250                  attr['start'] = start
1251 -            return self.open(tag, newline=1, attr=attr)
1252 -        return self.close(tag)
1253 +            tagstr =self.open(tag, newline=1, attr=attr, **kw)
1254 +        else:
1255 +            tagstr = self.close(tag, newline=1)
1256 +        return tagstr
1257      
1258 -    def bullet_list(self, on):
1259 +    def bullet_list(self, on, **kw):
1260 +        """Creates an HTML ordered list, <ol> element.
1261 +
1262 +        The 'type' if specified can be any legal unnumbered
1263 +        list-style-type, such as 'disc','square', etc.
1264 +
1265 +        Call once with on=1 to start the list, and a second time
1266 +        with on=0 to end it.
1267 +        """
1268          tag = 'ul'
1269          if on:
1270 -            return self.open(tag, newline=1)
1271 -        return self.close(tag)
1272 -           
1273 +            tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1274 +        else:
1275 +            tagstr = '\n' + self._indent_spaces() + self.close(tag, newline=0)
1276 +        return tagstr
1277 +
1278      def listitem(self, on, **kw):
1279 -        """ List item inherit its lang from the list. """
1280 +        """Adds a list item, <li> element, to a previously opened
1281 +        bullet or number list.
1282 +
1283 +        Call once with on=1 to start the region, and a second time
1284 +        with on=0 to end it.
1285 +        """
1286          tag = 'li'
1287          self._in_li = on != 0
1288          if on:
1289 -            attr = {}
1290 -            css_class = kw.get('css_class', None)
1291 -            if css_class:
1292 -                attr['class'] = css_class
1293 -            style = kw.get('style', None)
1294 -            if style:
1295 -                attr['style'] = style
1296 -            return self.open(tag, attr=attr)
1297 -        return self.close(tag)
1298 +            tagstr =  self.open(tag, newline=1, **kw)
1299 +        else:
1300 +            tagstr = self.close(tag, newline=1)
1301 +        return tagstr
1302 +
1303 +    def definition_list(self, on, **kw):
1304 +        """Creates an HTML definition list, <dl> element.
1305  
1306 -    def definition_list(self, on):
1307 +        Call once with on=1 to start the list, and a second time
1308 +        with on=0 to end it.
1309 +        """
1310          tag = 'dl'
1311          if on:
1312 -            return self.open(tag, newline=1)
1313 -        return self.close(tag)
1314 +            tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1315 +        else:
1316 +            tagstr = '\n' + self._indent_spaces() + self.close(tag, newline=0)
1317 +        return tagstr
1318 +
1319 +    def definition_term(self, on, **kw):
1320 +        """Adds a new term to a definition list, HTML element <dt>.
1321  
1322 -    def definition_term(self, on):
1323 +        Call once with on=1 to start the term, and a second time
1324 +        with on=0 to end it.
1325 +        """
1326          tag = 'dt'
1327          if on:
1328 -            return self.open(tag)
1329 -        return self.close(tag)
1330 -        
1331 -    def definition_desc(self, on):
1332 +            tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1333 +        else:
1334 +            tagstr = self.close(tag, newline=0)
1335 +        return tagstr
1336 +
1337 +    def definition_desc(self, on, **kw):
1338 +        """Gives the definition to a definition item, HTML element <dd>.
1339 +
1340 +        Call once with on=1 to start the definition, and a second time
1341 +        with on=0 to end it.
1342 +        """
1343          tag = 'dd'
1344          if on:
1345 -            return self.open(tag)
1346 -        return self.close(tag)
1347 +            tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1348 +        else:
1349 +            tagstr = self.close(tag, newline=0)
1350 +        return tagstr
1351  
1352 -    def heading(self, on, depth, id = None, **kw):
1353 +    def heading(self, on, depth, **kw):
1354          # remember depth of first heading, and adapt counting depth accordingly
1355          if not self._base_depth:
1356              self._base_depth = depth
1357 @@ -722,11 +1210,8 @@
1358              number = '.'.join(map(str, self.request._fmt_hd_counters[self._show_section_numbers-1:]))
1359              if number: number += ". "
1360  
1361 -        attr = {}
1362 -        if id:
1363 -            attr['id'] = id
1364          # Add space before heading, easier to check source code
1365 -        result = '\n' + self.open('h%d' % heading_depth, attr=attr)
1366 +        result = '\n' + self.open('h%d' % heading_depth, **kw)
1367  
1368          # TODO: convert this to readable code
1369          if self.request.user.show_topbottom:
1370 @@ -795,7 +1280,7 @@
1371          return result
1372  
1373  
1374 -    def table(self, on, attrs=None):
1375 +    def table(self, on, attrs=None, **kw):
1376          """ Create table
1377  
1378          @param on: start table
1379 @@ -814,36 +1299,53 @@
1380                  attrs = {}
1381              else:
1382                  attrs = self._checkTableAttr(attrs, 'table')
1383 -            result.append(self.open('table', newline=1, attr=attrs))
1384 +            result.append(self.open('table', newline=1, attr=attrs,
1385 +                                    allowed_attrs=self._allowed_table_attrs['table'],
1386 +                                    **kw))
1387 +            result.append(self.open('tbody', newline=1))
1388          else:
1389 -            # Close table then div
1390 +            # Close tbody, table, and then div
1391 +            result.append(self.close('tbody'))
1392              result.append(self.close('table'))
1393              result.append(self.close('div'))
1394  
1395          return ''.join(result)    
1396      
1397 -    def table_row(self, on, attrs=None):
1398 +    def table_row(self, on, attrs=None, **kw):
1399          tag = 'tr'
1400          if on:
1401              if not attrs:
1402                  attrs = {}
1403              else:
1404                  attrs = self._checkTableAttr(attrs, 'row')
1405 -            return self.open(tag, newline=1, attr=attrs)
1406 -        return self.close(tag)
1407 +            return self.open(tag, newline=1, attr=attrs,
1408 +                             allowed_attrs=self._allowed_table_attrs['row'],
1409 +                             **kw)
1410 +        return self.close(tag) + '\n'
1411      
1412 -    def table_cell(self, on, attrs=None):
1413 +    def table_cell(self, on, attrs=None, **kw):
1414          tag = 'td'
1415          if on:
1416              if not attrs:
1417                  attrs = {}
1418              else:
1419                  attrs = self._checkTableAttr(attrs, '')
1420 -            return self.open(tag, newline=1, attr=attrs)
1421 -        return self.close(tag)
1422 -
1423 -    def escapedText(self, text):
1424 -        return wikiutil.escape(text)
1425 +            return '  ' + self.open(tag, attr=attrs,
1426 +                             allowed_attrs=self._allowed_table_attrs[''],
1427 +                             **kw)
1428 +        return self.close(tag) + '\n'
1429 +
1430 +    def text(self, text, **kw):
1431 +        txt = FormatterBase.text( self, text, **kw )
1432 +        if kw:
1433 +            return self.open('span',**kw) + txt + self.close('span')
1434 +        return txt
1435 +
1436 +    def escapedText(self, text, **kw):
1437 +        txt = wikiutil.escape(text)
1438 +        if kw:
1439 +            return self.open('span',**kw) + txt + self.close('span')
1440 +        return txt
1441  
1442      def rawHTML(self, markup):
1443          return markup
1444 diff -urN moin-1.5.0/MoinMoin/formatter/text_plain.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_plain.py
1445 --- moin-1.5.0/MoinMoin/formatter/text_plain.py	2005-11-18 14:24:24.000000000 -0500
1446 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_plain.py	2006-01-19 14:59:50.000000000 -0500
1447 @@ -73,33 +73,38 @@
1448  
1449      def attachment_link(self, url, text, **kw):
1450          return "[%s]" % text
1451 +
1452      def attachment_image(self, url, **kw):
1453 -        return "[image:%s]" % url
1454 +        title = ''
1455 +        for a in (u'title', u'html__title', u'alt', u'html_alt'):
1456 +            if kw.has_key(a):
1457 +                title = ':' + kw[a]
1458 +        return "[image:%s%s]" % (url, title)
1459  
1460      def attachment_drawing(self, url, text, **kw):
1461          return "[drawing:%s]" % text
1462      
1463 -    def text(self, text):
1464 +    def text(self, text, **kw):
1465          self._did_para = 0
1466          if self._text is not None:
1467              self._text.append(text)
1468          return text
1469  
1470 -    def rule(self, size=0):
1471 +    def rule(self, size=0, **kw):
1472          size = min(size, 10)
1473          ch = u"---~=*+#####"[size]
1474          return (ch * 79) + u'\n'
1475  
1476 -    def strong(self, on):
1477 +    def strong(self, on, **kw):
1478          return u'*'
1479  
1480 -    def emphasis(self, on):
1481 +    def emphasis(self, on, **kw):
1482          return u'/'
1483  
1484 -    def highlight(self, on):
1485 +    def highlight(self, on, **kw):
1486          return u''
1487  
1488 -    def number_list(self, on, type=None, start=None):
1489 +    def number_list(self, on, type=None, start=None, **kw):
1490          if on:
1491              self._in_list = 1
1492              return [u'\n', u'\n\n'][not self._did_para]
1493 @@ -110,7 +115,7 @@
1494                  return u'\n'
1495          return u''
1496  
1497 -    def bullet_list(self, on):
1498 +    def bullet_list(self, on, **kw):
1499          if on:
1500              self._in_list = -1
1501              return [u'\n', u'\n\n'][not self._did_para]
1502 @@ -136,20 +141,20 @@
1503              self._did_para = 1
1504              return u'\n'
1505          
1506 -    def sup(self, on):
1507 +    def sup(self, on, **kw):
1508          return u'^'
1509  
1510 -    def sub(self, on):
1511 +    def sub(self, on, **kw):
1512          return u'_'
1513  
1514 -    def strike(self, on):
1515 +    def strike(self, on, **kw):
1516          return u'__'
1517  
1518      def code(self, on, **kw):
1519          #return [unichr(0x60), unichr(0xb4)][not on]
1520          return u"'" # avoid high-ascii
1521  
1522 -    def preformatted(self, on):
1523 +    def preformatted(self, on, **kw):
1524          FormatterBase.preformatted(self, on)
1525          snip = u'---%<'
1526          snip = snip + (u'-' * (78 - len(snip)))
1527 @@ -158,10 +163,10 @@
1528          else:
1529              return snip + u'\n'
1530  
1531 -    def small(self, on):
1532 +    def small(self, on, **kw):
1533          return u''
1534  
1535 -    def big(self, on):
1536 +    def big(self, on, **kw):
1537          return u''
1538  
1539      def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
1540 @@ -191,7 +196,7 @@
1541      def code_token(self, on, tok_type):
1542          return ""
1543  
1544 -    def paragraph(self, on):
1545 +    def paragraph(self, on, **kw):
1546          FormatterBase.paragraph(self, on)
1547          if self._did_para:
1548              on = 0
1549 @@ -212,33 +217,34 @@
1550              self._text = None
1551              return result
1552  
1553 -    def table(self, on, attrs={}):
1554 +    def table(self, on, attrs={}, **kw):
1555          return u''
1556  
1557 -    def table_row(self, on, attrs={}):
1558 +    def table_row(self, on, attrs={}, **kw):
1559          return u''
1560  
1561 -    def table_cell(self, on, attrs={}):
1562 +    def table_cell(self, on, attrs={}, **kw):
1563          return u''
1564  
1565 -    def underline(self, on):
1566 +    def underline(self, on, **kw):
1567          return u'_'
1568  
1569 -    def definition_list(self, on):
1570 +    def definition_list(self, on, **kw):
1571          return u''
1572  
1573 -    def definition_term(self, on, compact=0):
1574 +    def definition_term(self, on, compact=0, **kw):
1575          result = u''
1576          if not compact: result = result + u'\n'
1577          if not on: result = result + u':\n'
1578          return result
1579  
1580 -    def definition_desc(self, on):
1581 +    def definition_desc(self, on, **kw):
1582          return [u'    ', u'\n'][not on]
1583  
1584 -    def image(self, **kw):
1585 -        if kw.has_key(u'alt'):
1586 -            return kw[u'alt']
1587 +    def image(self, src=None, **kw):
1588 +        for a in (u'title', u'html__title', u'alt', u'html_alt'):
1589 +            if kw.has_key(a):
1590 +                return kw[a]
1591          return u''
1592  
1593      def lang(self, on, lang_name):
1594 diff -urN moin-1.5.0/MoinMoin/formatter/text_xml.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_xml.py
1595 --- moin-1.5.0/MoinMoin/formatter/text_xml.py	2005-12-01 20:30:07.000000000 -0500
1596 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_xml.py	2006-01-19 15:03:41.000000000 -0500
1597 @@ -77,12 +77,12 @@
1598          return '<attachmentdrawing href="%s">%s</attachmentdrawing>' % (
1599              url, text)
1600  
1601 -    def text(self, text):
1602 +    def text(self, text, **kw):
1603          if self.in_pre:
1604              return text.replace(']]>', ']]>]]&gt;<![CDATA[')
1605          return self._escape(text)
1606  
1607 -    def rule(self, size=0):
1608 +    def rule(self, size=0, **kw):
1609          return "\n<br/>%s<br/>\n" % ("-"*78,) # <hr/> not supported in stylebook
1610          if size:
1611              return '<hr size="%d"/>\n' % (size,)
1612 @@ -92,21 +92,21 @@
1613      def icon(self, type):
1614          return '<icon type="%s" />' % type            
1615  
1616 -    def strong(self, on):
1617 +    def strong(self, on, **kw):
1618          return ['<strong>', '</strong>'][not on]
1619  
1620 -    def emphasis(self, on):
1621 +    def emphasis(self, on, **kw):
1622          return ['<em>', '</em>'][not on]
1623  
1624 -    def highlight(self, on):
1625 +    def highlight(self, on, **kw):
1626          return ['<strong>', '</strong>'][not on]
1627  
1628 -    def number_list(self, on, type=None, start=None):
1629 +    def number_list(self, on, type=None, start=None, **kw):
1630          result = ''
1631          if self.in_p: result = self.paragraph(0)
1632          return result + ['<ol>', '</ol>\n'][not on]
1633  
1634 -    def bullet_list(self, on):
1635 +    def bullet_list(self, on, **kw):
1636          result = ''
1637          if self.in_p: result = self.paragraph(0)
1638          return result + ['<ul>', '</ul>\n'][not on]
1639 @@ -117,22 +117,22 @@
1640      def code(self, on, **kw):
1641          return ['<code>', '</code>'][not on]
1642  
1643 -    def sup(self, on):
1644 +    def sup(self, on, **kw):
1645          return ['<sup>', '</sup>'][not on]
1646  
1647 -    def sub(self, on):
1648 +    def sub(self, on, **kw):
1649          return ['<sub>', '</sub>'][not on]
1650  
1651 -    def strike(self, on):
1652 +    def strike(self, on, **kw):
1653          return ['<strike>', '</strike>'][not on]
1654  
1655 -    def preformatted(self, on):
1656 +    def preformatted(self, on, **kw):
1657          FormatterBase.preformatted(self, on)
1658          result = ''
1659          if self.in_p: result = self.paragraph(0)
1660          return result + ['<source><![CDATA[', ']]></source>'][not on]
1661  
1662 -    def paragraph(self, on):
1663 +    def paragraph(self, on, **kw):
1664          FormatterBase.paragraph(self, on)
1665          return ['<p>', '</p>\n'][not on]
1666  
1667 @@ -160,41 +160,42 @@
1668  
1669          return result + '<s%d%s title="' % (depth, id_text)
1670  
1671 -    def table(self, on, attrs={}):
1672 +    def table(self, on, attrs={}, **kw):
1673          return ['<table>', '</table>'][not on]
1674  
1675 -    def table_row(self, on, attrs={}):
1676 +    def table_row(self, on, attrs={}, **kw):
1677          return ['<tr>', '</tr>'][not on]
1678  
1679 -    def table_cell(self, on, attrs={}):
1680 +    def table_cell(self, on, attrs={}, **kw):
1681          return ['<td>', '</td>'][not on]
1682  
1683      def anchordef(self, id):
1684          return '<anchor id="%s"/>' % id
1685  
1686 -    def anchorlink(self, on, name='', id=None):
1687 +    def anchorlink(self, on, name='', **kw):
1688 +        id = kw.get('id',None)
1689          extra = ''
1690          if id:
1691              extra = ' id="%s"' % id
1692          return ('<link anchor="%s"%s>' % (name, extra) ,'</link>') [not on]
1693  
1694 -    def underline(self, on):
1695 +    def underline(self, on, **kw):
1696          return self.strong(on) # no underline in StyleBook
1697  
1698 -    def definition_list(self, on):
1699 +    def definition_list(self, on, **kw):
1700          result = ''
1701          if self.in_p: result = self.paragraph(0)
1702          return result + ['<gloss>', '</gloss>'][not on]
1703  
1704 -    def definition_term(self, on, compact=0):
1705 +    def definition_term(self, on, compact=0, **kw):
1706          return ['<label>', '</label>'][not on]
1707  
1708 -    def definition_desc(self, on):
1709 +    def definition_desc(self, on, **kw):
1710          return ['<item>', '</item>'][not on]
1711  
1712 -    def image(self, **kw):
1713 -        valid_attrs = ['src', 'width', 'height', 'alt']
1714 -        attrs = {}
1715 +    def image(self, src=None, **kw):
1716 +        valid_attrs = ['src', 'width', 'height', 'alt', 'title']
1717 +        attrs = {'src':src}
1718          for key, value in kw.items():
1719              if key in valid_attrs:
1720                  attrs[key] = value
1721 diff -urN moin-1.5.0/MoinMoin/formatter/xml_docbook.py moin-1.5.0-formatter-patch/MoinMoin/formatter/xml_docbook.py
1722 --- moin-1.5.0/MoinMoin/formatter/xml_docbook.py	2005-12-01 20:30:07.000000000 -0500
1723 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/xml_docbook.py	2006-01-19 15:02:51.000000000 -0500
1724 @@ -132,7 +132,7 @@
1725      def endDocument(self):
1726          return self.outputFormatter.getFooter()
1727  
1728 -    def text(self, text):
1729 +    def text(self, text, **kw):
1730          if text == "\\n":
1731              srcText = "\n"
1732          else:
1733 @@ -183,7 +183,7 @@
1734  
1735          return ""
1736  
1737 -    def paragraph(self, on):
1738 +    def paragraph(self, on, **kw):
1739          FormatterBase.paragraph(self, on)
1740          if on:
1741              para = self.doc.createElement("para")
1742 @@ -269,40 +269,40 @@
1743  
1744          return self._handleNode(name, on, attributes)
1745  
1746 -    def strong(self, on):
1747 +    def strong(self, on, **kw):
1748          return self._handleFormatting("emphasis", on, (('role','strong'), ))
1749  
1750 -    def emphasis(self, on):
1751 +    def emphasis(self, on, **kw):
1752          return self._handleFormatting("emphasis", on)
1753  
1754 -    def underline(self, on):
1755 +    def underline(self, on, **kw):
1756          return self._handleFormatting("emphasis", on, (('role','underline'), ))
1757  
1758 -    def highlight(self, on):
1759 +    def highlight(self, on, **kw):
1760          return self._handleFormatting("emphasis", on, (('role','highlight'), ))
1761  
1762 -    def sup(self, on):
1763 +    def sup(self, on, **kw):
1764          return self._handleFormatting("superscript", on)
1765  
1766 -    def sub(self, on):
1767 +    def sub(self, on, **kw):
1768          return self._handleFormatting("subscript", on)
1769  
1770 -    def strike(self, on):
1771 +    def strike(self, on, **kw):
1772          # does not yield <strike> using the HTML XSLT files here ...
1773          # but seems to be correct
1774          return self._handleFormatting("emphasis", on,
1775                                        (('role','strikethrough'), ))
1776  
1777 -    def code(self, on, **kwargs):
1778 +    def code(self, on, **kw):
1779          return self._handleFormatting("code", on)
1780  
1781 -    def preformatted(self, on):
1782 +    def preformatted(self, on, **kw):
1783          return self._handleFormatting("screen", on)
1784  
1785  
1786  ### Lists ###########################################################
1787  
1788 -    def number_list(self, on, type=None, start=None):
1789 +    def number_list(self, on, type=None, start=None, **kw):
1790          docbook_ol_types = {'1': "arabic", 
1791                              'a': "loweralpha", 
1792                              'A': "upperalpha",
1793 @@ -316,16 +316,16 @@
1794  
1795          return self._handleNode('orderedlist', on, attrs)
1796  
1797 -    def bullet_list(self, on):
1798 +    def bullet_list(self, on, **kw):
1799          return self._handleNode("itemizedlist", on)
1800  
1801 -    def definition_list(self, on):
1802 +    def definition_list(self, on, **kw):
1803          return self._handleNode("glosslist", on)        
1804  
1805      '''When on is false, we back out just on level. This is
1806         ok because we know definition_desc gets called, and we
1807         back out two levels there'''
1808 -    def definition_term(self, on, compact=0):
1809 +    def definition_term(self, on, compact=0, **kw):
1810          if on:
1811              entry=self.doc.createElement('glossentry')
1812              term=self.doc.createElement('glossterm')
1813 @@ -337,7 +337,7 @@
1814          return ""
1815     
1816      '''We backout two levels when 'on' is false, to leave the glossentry stuff'''
1817 -    def definition_desc(self, on):
1818 +    def definition_desc(self, on, **kw):
1819          if on:
1820              return self._handleNode("glossdef", on)
1821          else:
1822 @@ -381,7 +381,8 @@
1823          self._handleNode("ulink", False)
1824          return ""
1825  
1826 -    def anchorlink(self, on, name='', id=None):
1827 +    def anchorlink(self, on, name='', **kw):
1828 +        id = kw.get('id',None)
1829          attrs = []
1830          if name != '':
1831              attrs.append(('endterm', name))
1832 @@ -438,7 +439,9 @@
1833  
1834  
1835  ### Images and Smileys ##############################################
1836 -    def image(self, **kw):
1837 +    def image(self, src=None, **kw):
1838 +        if src:
1839 +            kw['src']=src
1840          media = self.doc.createElement('inlinemediaobject')
1841  
1842          imagewrap = self.doc.createElement('imageobject')
1843 @@ -452,12 +455,17 @@
1844          if kw.has_key('height'):
1845              image.setAttribute('depth', kw['height'])
1846          imagewrap.appendChild(image)
1847 -        
1848 -        if kw.has_key('alt'):
1849 +
1850 +        title = ''
1851 +        for a in ('title', 'html_title', 'alt', 'html_alt'):
1852 +            if kw.has_key(titleattr):
1853 +                title = kw[a]
1854 +                break
1855 +        if title:
1856              txtcontainer = self.doc.createElement('textobject')
1857              media.appendChild(txtcontainer)        
1858              txtphrase = self.doc.createElement('phrase')
1859 -            txtphrase.appendChild(self.doc.createTextNode(kw['alt']))
1860 +            txtphrase.appendChild(self.doc.createTextNode(title))
1861              txtcontainer.appendChild(txtphrase)        
1862          
1863          self.cur.appendChild(media)
1864 @@ -477,7 +485,7 @@
1865  
1866      #FIXME: We should copy code from text_html.py for attr handling
1867  
1868 -    def table(self, on, attrs=None):
1869 +    def table(self, on, attrs=None, **kw):
1870          sanitized_attrs = []
1871          if attrs and attrs.has_key('id'):
1872              sanitized_attrs[id] = attrs['id']
1873 @@ -489,14 +497,14 @@
1874          self._handleNode("tbody", on)
1875          return ""
1876      
1877 -    def table_row(self, on, attrs=None):
1878 +    def table_row(self, on, attrs=None, **kw):
1879          self.table_current_row_cells = 0
1880          sanitized_attrs = []
1881          if attrs and attrs.has_key('id'):
1882              sanitized_attrs[id] = attrs['id']
1883          return self._handleNode("row", on, sanitized_attrs)
1884  
1885 -    def table_cell(self, on, attrs=None):
1886 +    def table_cell(self, on, attrs=None, **kw):
1887          # Finish row definition
1888          sanitized_attrs = []
1889          if attrs and attrs.has_key('id'):
1890 @@ -572,11 +580,11 @@
1891          return u""
1892  
1893  ### Not supported ###################################################
1894 -    def rule(self, size = 0):
1895 +    def rule(self, size = 0, **kw):
1896          return ""
1897  
1898 -    def small(self, on):
1899 +    def small(self, on, **kw):
1900          return ""
1901  
1902 -    def big(self, on):
1903 +    def big(self, on, **kw):
1904          return ""
1905 diff -urN moin-1.5.0/wiki/htdocs/classic/css/common.css moin-1.5.0-formatter-patch/wiki/htdocs/classic/css/common.css
1906 --- moin-1.5.0/wiki/htdocs/classic/css/common.css	2005-12-17 16:22:37.000000000 -0500
1907 +++ moin-1.5.0-formatter-patch/wiki/htdocs/classic/css/common.css	2006-01-18 17:12:49.000000000 -0500
1908 @@ -110,8 +110,9 @@
1909  .hr5 {height: 7px;}
1910  .hr6 {height: 8px;}
1911  
1912 -/* Replacement for html 3 u tag */
1913 +/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
1914  .u {text-decoration: underline;}
1915 +.strike {text-decoration: line-through;}
1916  
1917  .footnotes ul {
1918  	padding: 0 2em;
1919 diff -urN moin-1.5.0/wiki/htdocs/modern/css/common.css moin-1.5.0-formatter-patch/wiki/htdocs/modern/css/common.css
1920 --- moin-1.5.0/wiki/htdocs/modern/css/common.css	2005-12-17 16:18:10.000000000 -0500
1921 +++ moin-1.5.0-formatter-patch/wiki/htdocs/modern/css/common.css	2006-01-18 17:12:23.000000000 -0500
1922 @@ -187,8 +187,9 @@
1923  .hr5 {height: 6pt;}
1924  .hr6 {height: 7pt;}
1925  
1926 -/* Replacement for html 3 u tag */
1927 +/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
1928  .u {text-decoration: underline;}
1929 +.strike {text-decoration: line-through;}
1930  
1931  /* eye catchers */
1932  .warning 
1933 diff -urN moin-1.5.0/wiki/htdocs/rightsidebar/css/common.css moin-1.5.0-formatter-patch/wiki/htdocs/rightsidebar/css/common.css
1934 --- moin-1.5.0/wiki/htdocs/rightsidebar/css/common.css	2005-12-17 16:24:52.000000000 -0500
1935 +++ moin-1.5.0-formatter-patch/wiki/htdocs/rightsidebar/css/common.css	2006-01-18 17:13:22.000000000 -0500
1936 @@ -123,8 +123,9 @@
1937  .hr5 {height: 6px;}
1938  .hr6 {height: 7px;}
1939  
1940 -/* Replacement for html 3 u tag */
1941 +/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
1942  .u {text-decoration: underline;}
1943 +.strike {text-decoration: line-through;}
1944  
1945  .footnotes ul {
1946  	padding: 0 2em;

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2006-01-12 20:03:22, 4.3 KB) [[attachment:base.py.diff]]
  • [get | view] (2006-01-12 20:03:35, 4.0 KB) [[attachment:dom_xml.py.diff]]
  • [get | view] (2006-01-19 20:12:28, 67.9 KB) [[attachment:formatter-patch-r4.diff]]
  • [get | view] (2006-01-16 20:11:38, 58.7 KB) [[attachment:formatter-patch.diff]]
  • [get | view] (2006-01-12 20:03:43, 2.0 KB) [[attachment:text_gedit.py.diff]]
  • [get | view] (2006-01-12 20:03:51, 25.7 KB) [[attachment:text_html.py.diff]]
  • [get | view] (2006-01-12 20:03:58, 3.7 KB) [[attachment:text_plain.py.diff]]
  • [get | view] (2006-01-12 20:04:06, 4.0 KB) [[attachment:text_xml.py.diff]]
  • [get | view] (2006-01-12 20:04:11, 5.1 KB) [[attachment:xml_docbook.py.diff]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.