Attachment 'formatter-patch.diff'

Download

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

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.