Attachment 'formatter_12_13.diff'

Download

   1 --- moin--main--1.2--patch-364/MoinMoin/formatter/text_html.py	2005-01-16 16:07:49.999094096 +0100
   2 +++ moin--main--1.3--patch-546/MoinMoin/formatter/text_html.py	2005-01-16 16:08:19.141663752 +0100
   3 @@ -2,206 +2,583 @@
   4  """
   5      MoinMoin - "text/html+css" Formatter
   6  
   7 -    This is a cleaned up version of text_html.py.
   8 -
   9      @copyright: 2000 - 2004 by Jürgen Hermann <jh@web.de>
  10      @license: GNU GPL, see COPYING for details.
  11  """
  12  
  13 -# Imports
  14  from MoinMoin.formatter.base import FormatterBase
  15 -from MoinMoin import wikiutil, config, i18n
  16 +from MoinMoin import wikiutil, i18n, config
  17  from MoinMoin.Page import Page
  18  
  19 -
  20 -#############################################################################
  21 -### HTML Formatter
  22 -#############################################################################
  23 -
  24  class Formatter(FormatterBase):
  25      """
  26          Send HTML data.
  27      """
  28  
  29 -    hardspace = '&nbsp;' # XXX was: '&#160;', but that breaks utf-8
  30 +    hardspace = '&nbsp;'
  31  
  32      def __init__(self, request, **kw):
  33          apply(FormatterBase.__init__, (self, request), kw)
  34 +
  35 +        # inline tags stack. When an inline tag is called, it goes into
  36 +        # the stack. When a block element starts, all inline tags in
  37 +        # the stack are closed.
  38 +        self._inlineStack = []
  39 +
  40          self._in_li = 0
  41          self._in_code = 0
  42 -        self._base_depth = 0
  43 +        self._in_code_area = 0
  44 +        self._in_code_line = 0
  45 +        self._code_area_num = 0
  46 +        self._code_area_js = 0
  47 +        self._code_area_state = ['', 0, -1, -1, 0]
  48          self._show_section_numbers = None
  49 +        self._content_ids = []
  50 +        self.pagelink_preclosed = False
  51 +        self._is_included = kw.get('is_included',False)
  52 +        self.request = request
  53 +        self.cfg = request.cfg
  54  
  55          if not hasattr(request, '_fmt_hd_counters'):
  56              request._fmt_hd_counters = []
  57  
  58 -    def _langAttr(self):
  59 -        result = ''
  60 -        lang = self.request.current_lang
  61 -        if lang != config.default_lang:
  62 -            result = ' lang="%s" dir="%s"' % (lang, i18n.getDirection(lang))
  63 +    # Primitive formatter functions #####################################
  64  
  65 -        return result
  66 +    # all other methods should use these to format tags. This keeps the
  67 +    # code clean and handle pathological cases like unclosed p and
  68 +    # inline tags.
  69 +
  70 +    def langAttr(self, lang=None):
  71 +        """ Return lang and dir attribute
  72 +
  73 +        Must be used on all block elements - div, p, table, etc.
  74 +        @param lang: if defined, will return attributes for lang. if not
  75 +            defined, will return attributes only if the current lang is
  76 +            different from the content lang.
  77 +        @rtype: dict
  78 +        @retrun: language attributes
  79 +        """
  80 +        if not lang:
  81 +            lang = self.request.current_lang
  82 +            # Actions that generate content in user language should change
  83 +            # the content lang from the default defined in cfg.
  84 +            if lang == self.request.content_lang:
  85 +                # lang is inherited from content div
  86 +                return {}
  87 +
  88 +        attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
  89 +        return attr
  90 +
  91 +    def formatAttributes(self, attr=None):
  92 +        """ Return formatted attributes string
  93 +
  94 +        @param attr: dict containing keys and values
  95 +        @rtype: string ?
  96 +        @return: formated attributes or empty string
  97 +        """
  98 +        if attr:
  99 +            attr = [' %s="%s"' % (k, v) for k, v in attr.items()]           
 100 +            return ''.join(attr)
 101 +        return ''
 102 +
 103 +    # TODO: use set when we require Python 2.3
 104 +    # TODO: The list is not complete, add missing from dtd
 105 +    _blocks = 'p div pre table tr td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
 106 +    _blocks = dict(zip(_blocks.split(), [1] * len(_blocks)))
 107 +
 108 +    def open(self, tag, newline=False, attr=None):
 109 +        """ Open a tag with optional attributes
 110 +        
 111 +        @param tag: html tag, string
 112 +        @param newline: render tag on a separate line
 113 +        @parm attr: dict with tag attributes
 114 +        @rtype: string ?
 115 +        @return: open tag with attributes
 116 +        """
 117 +        if tag in self._blocks:
 118 +            # Block elements
 119 +            result = []
 120 +            
 121 +            # Add language attributes, but let caller overide the default
 122 +            attributes = self.langAttr()
 123 +            if attr:
 124 +                attributes.update(attr)
 125 +            
 126 +            # Format
 127 +            attributes = self.formatAttributes(attributes)
 128 +            result.append('<%s%s>' % (tag, attributes))
 129 +            if newline:
 130 +                result.append('\n')
 131 +            return ''.join(result)
 132 +        else:
 133 +            # Inline elements
 134 +            # Add to inlineStack
 135 +            self._inlineStack.append(tag)
 136 +            # Format
 137 +            return '<%s%s>' % (tag, self.formatAttributes(attr))
 138 +       
 139 +    def close(self, tag, newline=False):
 140 +        """ Close tag
 141 +
 142 +        @param tag: html tag, string
 143 +        @rtype: string ?
 144 +        @return: closing tag
 145 +        """
 146 +        if tag in self._blocks:
 147 +            # Block elements
 148 +            # Close all tags in inline stack
 149 +            # Work on a copy, because close(inline) manipulate the stack
 150 +            result = []
 151 +            stack = self._inlineStack[:]
 152 +            stack.reverse()
 153 +            for inline in stack:
 154 +                result.append(self.close(inline))
 155 +            # Format with newline
 156 +            if newline:
 157 +                result.append('\n')
 158 +            result.append('</%s>\n' % (tag))
 159 +            return ''.join(result)            
 160 +        else:
 161 +            # Inline elements 
 162 +            # Pull from stack, ignore order, that is not our problem.
 163 +            # The code that calls us should keep correct calling order.
 164 +            if tag in self._inlineStack:
 165 +                self._inlineStack.remove(tag)
 166 +            return '</%s>' % tag
 167 +
 168 +
 169 +    # Public methods ###################################################
 170 +
 171 +    def startContent(self, content_id='content', **kwargs):
 172 +        """ Start page content div """
 173 +
 174 +        # Setup id
 175 +        if content_id!='content':
 176 +            aid = 'top_%s' % (content_id,)
 177 +        else:
 178 +            aid = 'top'
 179 +        self._content_ids.append(content_id)
 180 +        result = []
 181 +        # Use the content language
 182 +        attr = self.langAttr(self.request.content_lang)
 183 +        attr['id'] = content_id
 184 +        result.append(self.open('div', newline=1, attr=attr))
 185 +        result.append(self.anchordef(aid))
 186 +        return ''.join(result)
 187 +        
 188 +    def endContent(self):
 189 +        """ Close page content div """
 190 +
 191 +        # Setup id
 192 +        try:
 193 +            cid = self._content_ids.pop()
 194 +        except:
 195 +            cid = 'content'
 196 +        if cid!='content':
 197 +            aid = 'bottom_%s' % (cid,)
 198 +        else:
 199 +            aid = 'bottom'
 200  
 201 -    def lang(self, lang_name, text):
 202 +        result = []
 203 +        result.append(self.anchordef(aid))
 204 +        result.append(self.close('div', newline=1))
 205 +        return ''.join(result) 
 206 +
 207 +    def lang(self, on, lang_name):
 208          """ Insert text with specific lang and direction.
 209          
 210              Enclose within span tag if lang_name is different from
 211              the current lang    
 212          """
 213 -        
 214 +        tag = 'span'
 215          if lang_name != self.request.current_lang:
 216 -            dir = i18n.getDirection(lang_name)
 217 -            text = wikiutil.escape(text)
 218 -            return '<span lang="%(lang_name)s" dir="%(dir)s">%(text)s</span>' % {
 219 -                'lang_name': lang_name, 'dir': dir, 'text': text}
 220 -        
 221 -        return text            
 222 -                
 223 -    def sysmsg(self, text, **kw):
 224 -        return '\n<div class="message">%s</div>\n' % wikiutil.escape(text)
 225 +            # Enclose text in span using lang attributes
 226 +            if on:
 227 +                attr = self.langAttr(lang=lang_name)
 228 +                return self.open(tag, attr=attr)
 229 +            return self.close(tag)
 230  
 231 +        # Direction did not change, no need for span
 232 +        return ''            
 233 +                
 234 +    def sysmsg(self, on, **kw):
 235 +        tag = 'div'
 236 +        if on:
 237 +            return self.open(tag, attr={'class': 'message'})
 238 +        return self.close(tag)
 239      
 240      # Links ##############################################################
 241      
 242 -    def pagelink(self, pagename, text=None, **kw):
 243 +    def pagelink(self, on, pagename='', page=None, **kw):
 244          """ Link to a page.
 245  
 246 +            formatter.text_python will use an optimized call with a page!=None
 247 +            parameter. DO NOT USE THIS YOURSELF OR IT WILL BREAK.
 248 +
 249              See wikiutil.link_tag() for possible keyword parameters.
 250          """
 251 -        apply(FormatterBase.pagelink, (self, pagename, text), kw)
 252 -        return Page(pagename).link_to(self.request, text, **kw)
 253 +        apply(FormatterBase.pagelink, (self, on, pagename, page), kw)
 254 +        if page is None:
 255 +            page = Page(self.request, pagename, formatter=self);
 256 +            
 257 +        if self.request.user.show_nonexist_qm and on and not page.exists():
 258 +            self.pagelink_preclosed = True
 259 +            return (page.link_to(self.request, on=1, **kw) +
 260 +                    self.text("?") +
 261 +                    page.link_to(self.request, on=0, **kw))
 262 +        elif not on and self.pagelink_preclosed:
 263 +            self.pagelink_preclosed = False
 264 +            return ""
 265 +        else:
 266 +            return page.link_to(self.request, on=on, **kw)
 267 +
 268 +    def interwikilink(self, on, interwiki='', pagename='', **kw):
 269 +        if not on: return '</a>'
 270 +        
 271 +        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
 272 +        wikiurl = wikiutil.mapURL(self.request, wikiurl)
 273 +        href = wikiutil.join_wiki(wikiurl, wikitail)
 274 +
 275 +        # return InterWiki hyperlink
 276 +        if wikitag_bad:
 277 +            html_class = 'badinterwiki'
 278 +        else:
 279 +            html_class = 'interwiki'
 280 +
 281 +        icon = ''
 282 +        if self.request.user.show_fancy_links:
 283 +            icon = self.request.theme.make_icon('interwiki', {'wikitag': wikitag}) 
 284 +        return (self.url(1, href, title=wikitag, unescaped=0,
 285 +                        pretty_url=kw.get('pretty_url', 0), css = html_class) +
 286 +                icon)
 287 +        # unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
 288  
 289 -    def url(self, url, text=None, css=None, **kw):
 290 +    def url(self, on, url=None, css=None, **kw):
 291          """
 292              Keyword params:
 293                  title - title attribute
 294                  ... some more (!!! TODO) 
 295          """
 296 -        url = wikiutil.mapURL(url)
 297 +        url = wikiutil.mapURL(self.request, url)
 298          pretty = kw.get('pretty_url', 0)
 299          title = kw.get('title', None)
 300  
 301 -        if not pretty and wikiutil.isPicture(url):
 302 -            return '<img src="%s" alt="%s">' % (url,url)
 303 -
 304 -        if text is None: text = url
 305 +        #if not pretty and wikiutil.isPicture(url):
 306 +        #    # XXX
 307 +        #    return '<img src="%s" alt="%s">' % (url,url)
 308  
 309          # create link
 310 +        if not on:
 311 +            return '</a>'
 312          str = '<a'
 313 -        if css: str = '%s class="%s"' % (str, css)
 314 -        if title: str = '%s title="%s"' % (str, title)
 315 -        str = '%s href="%s">%s</a>' % (str, wikiutil.escape(url, 1), text)
 316 +        if css: 
 317 +            str = '%s class="%s"' % (str, css)
 318 +        if title:
 319 +            str = '%s title="%s"' % (str, title)
 320 +        str = '%s href="%s">' % (str, wikiutil.escape(url, 1))
 321 +
 322 +        type = kw.get('type', '')
 323 +
 324 +        if type=='www':
 325 +            str = '%s%s ' % (str, self.icon("www"))
 326 +        elif type=='mailto':
 327 +            str = '%s%s ' % (str, self.icon('mailto'))
 328  
 329          return str
 330  
 331      def anchordef(self, id):
 332 -        return '<a id="%s"></a>' % id
 333 +        return '<a id="%s"></a>\n' % (id, )
 334  
 335 -    def anchorlink(self, name, text, id = None):
 336 +    def anchorlink(self, on, name='', id = None):
 337          extra = ''
 338          if id:
 339              extra = ' id="%s"' % id
 340 -        return '<a href="#%s"%s>%s</a>' % (name, extra, wikiutil.escape(text))
 341 +        return ['<a href="#%s"%s>' % (name, extra), '</a>'][not on]
 342  
 343 -    # Text and Text Attributes ###########################################
 344 +    # Text ##############################################################
 345      
 346 -    def text(self, text):
 347 +    def _text(self, text):
 348          if self._in_code:
 349              return wikiutil.escape(text).replace(' ', self.hardspace)
 350          return wikiutil.escape(text)
 351  
 352 +    # Inline ###########################################################
 353 +        
 354      def strong(self, on):
 355 -        return ['<strong>', '</strong>'][not on]
 356 +        tag = 'strong'
 357 +        if on:
 358 +            return self.open(tag)
 359 +        return self.close(tag)
 360  
 361      def emphasis(self, on):
 362 -        return ['<em>', '</em>'][not on]
 363 +        tag = 'em'
 364 +        if on:
 365 +            return self.open(tag)
 366 +        return self.close(tag)
 367  
 368      def underline(self, on):
 369 -        return ['<u>', '</u>'][not on]
 370 +        tag = 'span'
 371 +        if on:
 372 +            return self.open(tag, attr={'class': 'u'})
 373 +        return self.close(tag)
 374  
 375      def highlight(self, on):
 376 -        return ['<strong class="highlight">', '</strong>'][not on]
 377 +        tag = 'strong'
 378 +        if on:
 379 +            return self.open(tag, attr={'class': 'highlight'})
 380 +        return self.close(tag)
 381  
 382      def sup(self, on):
 383 -        return ['<sup>', '</sup>'][not on]
 384 +        tag = 'sup'
 385 +        if on:
 386 +            return self.open(tag)
 387 +        return self.close(tag)
 388  
 389      def sub(self, on):
 390 -        return ['<sub>', '</sub>'][not on]
 391 +        tag = 'sub'
 392 +        if on:
 393 +            return self.open(tag)
 394 +        return self.close(tag)
 395  
 396      def code(self, on):
 397 -        self._in_code = on
 398 -        return ['<tt>', '</tt>'][not on]
 399 +        tag = 'tt'
 400 +        # Maybe we don't need this, because we have tt will be in inlineStack.
 401 +        self._in_code = on        
 402 +        if on:
 403 +            return self.open(tag)
 404 +        return self.close(tag)
 405 +        
 406 +    def small(self, on):
 407 +        tag = 'small'
 408 +        if on:
 409 +            return self.open(tag)
 410 +        return self.close(tag)
 411 +                                                                                
 412 +    def big(self, on):
 413 +        tag = 'big'
 414 +        if on:
 415 +            return self.open(tag)
 416 +        return self.close(tag)
 417 +
 418 +
 419 +    # Block elements ####################################################
 420  
 421      def preformatted(self, on):
 422          FormatterBase.preformatted(self, on)
 423 -        return ['<pre>', '</pre>'][not on]
 424 +        tag = 'pre'
 425 +        if on:
 426 +            return self.open(tag, newline=1)
 427 +        return self.close(tag)
 428 +                
 429 +    # Use by code area
 430 +    _toggleLineNumbersScript = """
 431 +<script type="text/JavaScript">
 432 +function isnumbered(obj) {
 433 +  return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber';
 434 +}
 435 +function nformat(num,chrs,add) {
 436 +  var nlen = Math.max(0,chrs-(''+num).length), res = '';
 437 +  while (nlen>0) { res += ' '; nlen-- }
 438 +  return res+num+add;
 439 +}
 440 +function addnumber(did, nstart, nstep) {
 441 +  var c = document.getElementById(did), l = c.firstChild, n = 1;
 442 +  if (!isnumbered(c))
 443 +    if (typeof nstart == 'undefined') nstart = 1;
 444 +    if (typeof nstep  == 'undefined') nstep = 1;
 445 +    n = nstart;
 446 +    while (l != null) {
 447 +      if (l.tagName == 'SPAN') {
 448 +        var s = document.createElement('SPAN');
 449 +        s.className = 'LineNumber'
 450 +        s.appendChild(document.createTextNode(nformat(n,4,' ')));
 451 +        n += nstep;
 452 +        if (l.childNodes.length)
 453 +          l.insertBefore(s, l.firstChild)
 454 +        else
 455 +          l.appendChild(s)
 456 +      }
 457 +      l = l.nextSibling;
 458 +    }
 459 +  return false;
 460 +}
 461 +function remnumber(did) {
 462 +  var c = document.getElementById(did), l = c.firstChild;
 463 +  if (isnumbered(c))
 464 +    while (l != null) {
 465 +      if (l.tagName == 'SPAN' && l.firstChild.className == 'LineNumber') l.removeChild(l.firstChild);
 466 +      l = l.nextSibling;
 467 +    }
 468 +  return false;
 469 +}
 470 +function togglenumber(did, nstart, nstep) {
 471 +  var c = document.getElementById(did);
 472 +  if (isnumbered(c)) {
 473 +    remnumber(did);
 474 +  } else {
 475 +    addnumber(did,nstart,nstep);
 476 +  }
 477 +  return false;
 478 +}
 479 +</script>
 480 +"""
 481 +    
 482 +    def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
 483 +        res = []
 484 +        ci = self.request.makeUniqueID('CA-%s_%03d' % (code_id, self._code_area_num))
 485 +        if on:
 486 +            # Open a code area
 487 +            self._in_code_area = 1
 488 +            self._in_code_line = 0
 489 +            self._code_area_state = [ci, show, start, step, start]
 490 +
 491 +            # Open the code div - using left to right always!
 492 +            attr = {'class': 'codearea', 'lang': 'en', 'dir': 'ltr'}
 493 +            res.append(self.open('div', attr=attr))
 494 +
 495 +            # Add the script only in the first code area on the page
 496 +            if self._code_area_js == 0 and self._code_area_state[1] >= 0:
 497 +                res.append(self._toggleLineNumbersScript)
 498 +                self._code_area_js = 1
 499 +
 500 +            # Add line number link, but only for JavaScript enabled browsers.
 501 +            if self._code_area_state[1] >= 0:
 502 +                toggleLineNumbersLink = r'''
 503 +<script type="text/javascript">
 504 +document.write('<a href="#" onClick="return togglenumber(\'%s\', %d, %d);" \
 505 +                class="codenumbers">Toggle line numbers<\/a>');
 506 +</script>
 507 +''' % (self._code_area_state[0], self._code_area_state[2], self._code_area_state[3])
 508 +                res.append(toggleLineNumbersLink)
 509 +
 510 +            # Open pre - using left to right always!
 511 +            attr = {'id': self._code_area_state[0], 'lang': 'en', 'dir': 'ltr'}
 512 +            res.append(self.open('pre', newline=True, attr=attr))
 513 +        else:
 514 +            # Close code area
 515 +            res = []
 516 +            if self._in_code_line:
 517 +                res.append(self.code_line(0))
 518 +            res.append(self.close('pre'))
 519 +            res.append(self.close('div'))
 520 +
 521 +            # Update state
 522 +            self._in_code_area = 0
 523 +            self._code_area_num += 1
 524 +
 525 +        return ''.join(res)
 526 +
 527 +    def code_line(self, on):
 528 +        res = ''
 529 +        if not on or (on and self._in_code_line):
 530 +            res += '</span>\n'
 531 +        if on:
 532 +            res += '<span class="line">'
 533 +            if self._code_area_state[1] > 0:
 534 +                res += '<span class="LineNumber">%4d </span>' % (self._code_area_state[4], )
 535 +                self._code_area_state[4] += self._code_area_state[3]
 536 +        self._in_code_line = on != 0
 537 +        return res
 538 +
 539 +    def code_token(self, on, tok_type):
 540 +        return ['<span class="%s">' % tok_type, '</span>'][not on]
 541  
 542      # Paragraphs, Lines, Rules ###########################################
 543      
 544      def linebreak(self, preformatted=1):
 545 +        if self._in_code_area:
 546 +            preformatted = 1
 547          return ['\n', '<br>\n'][not preformatted]
 548 -
 549 +        
 550      def paragraph(self, on):
 551 +        if self._terse:
 552 +            return ''
 553          FormatterBase.paragraph(self, on)
 554          if self._in_li:
 555              self._in_li = self._in_li + 1
 556 -        result = ['<p%s>' % self._langAttr(), '\n</p>'][not on]
 557 -        return '%s\n' % result
 558 -    
 559 -    def rule(self, size=0):
 560 +        tag = 'p'
 561 +        if on:
 562 +            return self.open(tag)
 563 +        return self.close(tag)
 564 +            
 565 +    def rule(self, size=None):
 566          if size:
 567 -            return '<hr size="%d">\n' % (size,)
 568 -        else:
 569 -            return '<hr>\n'
 570 +            # Add hr class: hr1 - hr6
 571 +            return self.open('hr', newline=1, attr={'class': 'hr%d' % size})
 572 +        return self.open('hr', newline=1)
 573 +                
 574 +    def icon(self, type):
 575 +        return self.request.theme.make_icon(type)
 576 +
 577 +    def smiley(self, text):
 578 +        w, h, b, img = config.smileys[text.strip()]
 579 +        href = img
 580 +        if not href.startswith('/'):
 581 +            href = self.request.theme.img_url(img)
 582 +        return self.image(src=href, alt=text, width=str(w), height=str(h))
 583  
 584      # Lists ##############################################################
 585  
 586      def number_list(self, on, type=None, start=None):
 587 +        tag = 'ol'
 588          if on:
 589 -            attrs = ''
 590 -            if type: attrs += ' type="%s"' % (type,)
 591 -            if start is not None: attrs += ' start="%d"' % (start,)
 592 -            result = '<ol%s%s>' % (self._langAttr(), attrs)
 593 -        else:    
 594 -            result = '</ol>\n'
 595 -        return '%s\n' % result
 596 +            attr = {}
 597 +            if type is not None:
 598 +                attr['type'] = type
 599 +            if start is not None:
 600 +                attr['start'] = start
 601 +            return self.open(tag, newline=1, attr=attr)
 602 +        return self.close(tag)
 603      
 604      def bullet_list(self, on):
 605 -        result = ['<ul%s>' % self._langAttr(), '</ul>\n'][not on]
 606 -        return '%s\n' % result
 607 -
 608 +        tag = 'ul'
 609 +        if on:
 610 +            return self.open(tag, newline=1)
 611 +        return self.close(tag)
 612 +           
 613      def listitem(self, on, **kw):
 614          """ List item inherit its lang from the list. """
 615 +        tag = 'li'
 616          self._in_li = on != 0
 617          if on:
 618 +            attr = {}
 619              css_class = kw.get('css_class', None)
 620 -            attrs = ''
 621 -            if css_class: attrs += ' class="%s"' % (css_class,)
 622 -            result = '<li%s>' % (attrs,)
 623 -        else:
 624 -            result = '</li>'
 625 -        return '%s\n' % result
 626 +            if css_class:
 627 +                attr['class'] = css_class
 628 +            style = kw.get('style', None)
 629 +            if style:
 630 +                attr['style'] = style
 631 +            return self.open(tag, attr=attr)
 632 +        return self.close(tag)
 633  
 634      def definition_list(self, on):
 635 -        result = ['<dl>', '</dl>'][not on]
 636 -        return '%s\n' % result
 637 +        tag = 'dl'
 638 +        if on:
 639 +            return self.open(tag, newline=1)
 640 +        return self.close(tag)
 641  
 642      def definition_term(self, on):
 643 -        return ['<dt%s>' % (self._langAttr()), '</dt>'][not on]
 644 -
 645 +        tag = 'dt'
 646 +        if on:
 647 +            return self.open(tag)
 648 +        return self.close(tag)
 649 +        
 650      def definition_desc(self, on):
 651 -        return ['<dd%s>\n' % self._langAttr(), '</dd>\n'][not on]
 652 +        tag = 'dd'
 653 +        if on:
 654 +            return self.open(tag)
 655 +        return self.close(tag)
 656  
 657 -    def heading(self, depth, title, id = None, **kw):
 658 +    def heading(self, on, depth, id = None, **kw):
 659          # remember depth of first heading, and adapt counting depth accordingly
 660          if not self._base_depth:
 661              self._base_depth = depth
 662 +
 663          count_depth = max(depth - (self._base_depth - 1), 1)
 664  
 665          # check numbering, possibly changing the default
 666          if self._show_section_numbers is None:
 667 -            self._show_section_numbers = config.show_section_numbers
 668 +            self._show_section_numbers = self.cfg.show_section_numbers
 669              numbering = self.request.getPragma('section-numbers', '').lower()
 670              if numbering in ['0', 'off']:
 671                  self._show_section_numbers = 0
 672 @@ -211,6 +588,12 @@
 673                  # explicit base level for section number display
 674                  self._show_section_numbers = int(numbering)
 675  
 676 +        heading_depth = depth + 1
 677 +
 678 +        # closing tag, with empty line after, to make source more readable
 679 +        if not on:
 680 +            return self.close('h%d' % heading_depth) + '\n'
 681 +            
 682          # create section number
 683          number = ''
 684          if self._show_section_numbers:
 685 @@ -222,67 +605,123 @@
 686              number = '.'.join(map(str, self.request._fmt_hd_counters[self._show_section_numbers-1:]))
 687              if number: number += ". "
 688  
 689 -        id_text = ''
 690 +        attr = {}
 691          if id:
 692 -          id_text = ' id="%s"' % id
 693 +            attr['id'] = id
 694 +        # Add space before heading, easier to check source code
 695 +        result = '\n' + self.open('h%d' % heading_depth, attr=attr)
 696 +
 697 +        # TODO: convert this to readable code
 698 +        if self.request.user.show_topbottom:
 699 +            # TODO change top/bottom refs to content-specific top/bottom refs?
 700 +            result = ("%s%s%s%s%s%s%s%s" %
 701 +                      (result,
 702 +                       kw.get('icons',''),
 703 +                       self.url(1, "#bottom", unescaped=1),
 704 +                       self.icon('bottom'),
 705 +                       self.url(0),
 706 +                       self.url(1, "#top", unescaped=1),
 707 +                       self.icon('top'),
 708 +                       self.url(0)))
 709 +        return "%s%s%s" % (result, kw.get('icons',''), number)
 710  
 711 -        heading_depth = depth + 1
 712 -        if kw.has_key('on'):
 713 -            if kw['on']:
 714 -                result = '<h%d%s>' % (heading_depth, id_text)
 715 -            else:
 716 -                result = '</h%d>' % heading_depth
 717 -        else:
 718 -            result = '<h%d%s%s>%s%s%s</h%d>\n' % (
 719 -                heading_depth, self._langAttr(), id_text, kw.get('icons', ''), number, title, heading_depth)
 720 -        return result
 721      
 722      # Tables #############################################################
 723  
 724 -    # XXX TODO find better solution for bgcolor, align, valign (deprecated in html4)
 725 -    # do not remove current code before making working compliant code
 726 -
 727      _allowed_table_attrs = {
 728 -        'table': ['class', 'width', 'bgcolor'],
 729 -        'row': ['class', 'width', 'align', 'valign', 'bgcolor'],
 730 -        '': ['colspan', 'rowspan', 'class', 'width', 'align', 'valign', 'bgcolor'],
 731 +        'table': ['class', 'id', 'style'],
 732 +        'row': ['class', 'id', 'style'],
 733 +        '': ['colspan', 'rowspan', 'class', 'id', 'style'],
 734      }
 735  
 736      def _checkTableAttr(self, attrs, prefix):
 737 -        if not attrs: return ''
 738 +        """ Check table attributes
 739 +
 740 +        Convert from wikitable attributes to html 4 attributes.
 741 +
 742 +        @param attrs: attribute dict
 743 +        @param prefix: used in wiki table attributes
 744 +        @rtyp: dict
 745 +        @return: valid table attributes
 746 +        """
 747 +        if not attrs:
 748 +            return {}
 749  
 750 -        result = ''
 751 +        result = {}
 752 +        s = "" # we collect synthesized style in s
 753          for key, val in attrs.items():
 754 -            if prefix and key[:len(prefix)] != prefix: continue
 755 +            # Ignore keys that don't start with prefix
 756 +            if prefix and key[:len(prefix)] != prefix:
 757 +                continue
 758              key = key[len(prefix):]
 759 -            if key not in self._allowed_table_attrs[prefix]: continue
 760 -            result = '%s %s=%s' % (result, key, val)
 761 +            val = val.strip('"')
 762 +            # remove invalid attrs from dict and synthesize style
 763 +            if key == 'width':
 764 +                s += "width: %s;" % val
 765 +            elif key == 'bgcolor':
 766 +                s += "background-color: %s;" % val
 767 +            elif key == 'align':
 768 +                s += "text-align: %s;" % val
 769 +            elif key == 'valign':
 770 +                s += "vertical-align: %s;" % val
 771 +            # Ignore unknown keys
 772 +            if key not in self._allowed_table_attrs[prefix]:
 773 +                continue
 774 +            result[key] = val
 775 +        if s:
 776 +            if result.has_key('style'):
 777 +                result['style'] += s
 778 +            else:
 779 +                result['style'] = s
 780          return result
 781  
 782 -    def table(self, on, attrs={}):
 783 +
 784 +    def table(self, on, attrs=None):
 785 +        """ Create table
 786 +
 787 +        @param on: start table
 788 +        @param attrs: table attributes
 789 +        @rtype: string
 790 +        @return start or end tag of a table
 791 +        """
 792 +        result = []
 793          if on:
 794 -            # Enclose table inside a div to get correct alignment
 795 -            # when using language macros
 796 -            attrs = attrs and attrs.copy() or {}
 797 -            result = '\n<div%(lang)s>\n<table%(tableAttr)s>' % {
 798 -                'lang': self._langAttr(),
 799 -                'tableAttr': self._checkTableAttr(attrs, 'table')
 800 -            }
 801 +            # Open div to get correct alignment with table width smaller
 802 +            # then 100%
 803 +            result.append(self.open('div', newline=1))
 804 +
 805 +            # Open table
 806 +            if not attrs:
 807 +                attrs = {}
 808 +            else:
 809 +                attrs = self._checkTableAttr(attrs, 'table')
 810 +            result.append(self.open('table', newline=1, attr=attrs))
 811          else:
 812 -            result = '</table>\n</div>'
 813 -        return '%s\n' % result
 814 +            # Close table then div
 815 +            result.append(self.close('table'))
 816 +            result.append(self.close('div'))
 817 +
 818 +        return ''.join(result)    
 819      
 820 -    def table_row(self, on, attrs={}):
 821 +    def table_row(self, on, attrs=None):
 822 +        tag = 'tr'
 823          if on:
 824 -            result = '<tr%s>' % self._checkTableAttr(attrs, 'row')
 825 -        else:
 826 -            result = '</tr>'
 827 -        return '%s\n' % result
 828 -
 829 -    def table_cell(self, on, attrs={}):
 830 +            if not attrs:
 831 +                attrs = {}
 832 +            else:
 833 +                attrs = self._checkTableAttr(attrs, 'row')
 834 +            return self.open(tag, newline=1, attr=attrs)
 835 +        return self.close(tag)
 836 +    
 837 +    def table_cell(self, on, attrs=None):
 838 +        tag = 'td'
 839          if on:
 840 -            result = '<td%s>' % self._checkTableAttr(attrs, '')
 841 -        else:
 842 -            result = '</td>'
 843 -        return '%s\n' % result
 844 +            if not attrs:
 845 +                attrs = {}
 846 +            else:
 847 +                attrs = self._checkTableAttr(attrs, '')
 848 +            return self.open(tag, newline=1, attr=attrs)
 849 +        return self.close(tag)
 850  
 851 +    def escapedText(self, text):
 852 +        return wikiutil.escape(text)
 853 

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] (2005-01-16 16:33:25, 29.3 KB) [[attachment:formatter_12_13.diff]]
  • [get | view] (2005-01-16 16:32:48, 46.9 KB) [[attachment:parser_12_13.diff]]
 All files | Selected Files: delete move to page copy to page

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