This is what changed between 1.2-latest and 1.3-latest:

   1 --- moin--main--1.2--patch-364/MoinMoin/parser/wiki.py	2005-01-16 16:07:50.096079352 +0100
   2 +++ moin--main--1.3--patch-546/MoinMoin/parser/wiki.py	2005-01-16 16:08:19.182657520 +0100
   3 @@ -6,17 +6,11 @@
   4      @license: GNU GPL, see COPYING for details.
   5  """
   6  
   7 -# Imports
   8  import os, re
   9  from MoinMoin import config, wikimacro, wikiutil
  10  from MoinMoin.Page import Page
  11  from MoinMoin.util import web
  12  
  13 -
  14 -#############################################################################
  15 -### MoinMoin Wiki Markup Parser
  16 -#############################################################################
  17 -
  18  class Parser:
  19      """
  20          Object that turns Wiki markup into HTML.
  21 @@ -34,29 +28,29 @@
  22      # some common strings
  23      PARENT_PREFIX = wikiutil.PARENT_PREFIX
  24      attachment_schemas = ["attachment", "inline", "drawing"]
  25 -    punct_pattern = re.escape('''"\'}]|:,.)?!''')
  26 -    url_pattern = ('http|https|ftp|nntp|news|mailto|telnet|wiki|file|' +
  27 -            '|'.join(attachment_schemas) + 
  28 -            (config.url_schemas and '|' + '|'.join(config.url_schemas) or ''))
  29 +    punct_pattern = re.escape(u'''"\'}]|:,.)?!''')
  30 +    url_pattern = (u'http|https|ftp|nntp|news|mailto|telnet|wiki|file|' +
  31 +            u'|'.join(attachment_schemas) + 
  32 +            (config.url_schemas and u'|' + u'|'.join(config.url_schemas) or ''))
  33  
  34      # some common rules
  35 -    word_rule = r'(?:(?<![%(l)s])|^)%(parent)s(?:%(subpages)s(?:[%(u)s][%(l)s]+){2,})+(?![%(u)s%(l)s]+)' % {
  36 -        'u': config.upperletters,
  37 -        'l': config.lowerletters,
  38 +    word_rule = ur'(?:(?<![%(l)s])|^)%(parent)s(?:%(subpages)s(?:[%(u)s][%(l)s]+){2,})+(?![%(u)s%(l)s]+)' % {
  39 +        'u': config.chars_upper,
  40 +        'l': config.chars_lower,
  41          'subpages': config.allow_subpages and (wikiutil.CHILD_PREFIX + '?') or '',
  42 -        'parent': config.allow_subpages and (r'(?:%s)?' % re.escape(PARENT_PREFIX)) or '',
  43 +        'parent': config.allow_subpages and (ur'(?:%s)?' % re.escape(PARENT_PREFIX)) or '',
  44      }
  45 -    url_rule = r'%(url_guard)s(%(url)s)\:([^\s\<%(punct)s]|([%(punct)s][^\s\<%(punct)s]))+' % {
  46 -        'url_guard': '(^|(?<!\w))',
  47 +    url_rule = ur'%(url_guard)s(%(url)s)\:([^\s\<%(punct)s]|([%(punct)s][^\s\<%(punct)s]))+' % {
  48 +        'url_guard': u'(^|(?<!\w))',
  49          'url': url_pattern,
  50          'punct': punct_pattern,
  51      }
  52  
  53 -    ol_rule = r"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
  54 -    dl_rule = r"^\s+.*?::\s"
  55 +    ol_rule = ur"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
  56 +    dl_rule = ur"^\s+.*?::\s"
  57  
  58      # the big, fat, ugly one ;)
  59 -    formatting_rules = r"""(?:(?P<emph_ibb>'''''(?=[^']+'''))
  60 +    formatting_rules = ur"""(?:(?P<emph_ibb>'''''(?=[^']+'''))
  61  (?P<emph_ibi>'''''(?=[^']+''))
  62  (?P<emph_ib_or_bi>'{5}(?=[^']))
  63  (?P<emph>'{2,3})
  64 @@ -66,37 +60,44 @@
  65  (?P<tt>\{\{\{.*?\}\}\})
  66  (?P<processor>(\{\{\{(#!.*|\s*$)))
  67  (?P<pre>(\{\{\{ ?|\}\}\}))
  68 +(?P<small>(\~- ?|-\~))
  69 +(?P<big>(\~\+ ?|\+\~))
  70  (?P<rule>-{4,})
  71  (?P<comment>^\#\#.*$)
  72 -(?P<macro>\[\[(%(macronames)s)(?:\(.*?\))?\]\]))
  73 -(?P<li>^\s+\*)
  74 +(?P<macro>\[\[(%%(macronames)s)(?:\(.*?\))?\]\]))
  75  (?P<ol>%(ol_rule)s)
  76  (?P<dl>%(dl_rule)s)
  77 +(?P<li>^\s+\*?)
  78  (?P<tableZ>\|\| $)
  79 -(?P<table>(?:\|\|)+(?:<[^>]*?>)?(?=.))
  80 +(?P<table>(?:\|\|)+(?:<[^>]*?>)?(?!\|? $))
  81  (?P<heading>^\s*(?P<hmarker>=+)\s.*\s(?P=hmarker) $)
  82  (?P<interwiki>[A-Z][a-zA-Z]+\:[^\s'\"\:\<\|]([^\s%(punct)s]|([%(punct)s][^\s%(punct)s]))+)
  83  (?P<word>%(word_rule)s)
  84  (?P<url_bracket>\[((%(url)s)\:|#|\:)[^\s\]]+(\s[^\]]+)?\])
  85  (?P<url>%(url_rule)s)
  86 -(?P<email>[-\w._+]+\@[\w-]+\.[\w.-]+)
  87 +(?P<email>[-\w._+]+\@[\w-]+(\.[\w-]+)+)
  88  (?P<smiley>(?<=\s)(%(smiley)s)(?=\s))
  89  (?P<smileyA>^(%(smiley)s)(?=\s))
  90  (?P<ent>[<>&])"""  % {
  91          'url': url_pattern,
  92          'punct': punct_pattern,
  93 -        'macronames': '|'.join(wikimacro.names),
  94          'ol_rule': ol_rule,
  95          'dl_rule': dl_rule,
  96          'url_rule': url_rule,
  97          'word_rule': word_rule,
  98 -        'smiley': '|'.join(map(re.escape, config.smileys.keys()))}
  99 +        'smiley': u'|'.join(map(re.escape, config.smileys.keys()))}
 100 +
 101 +    # Don't start p before these 
 102 +    no_new_p_before = ("heading rule table tableZ tr td ul ol dl dt dd li "
 103 +                       "processor macro pre")
 104 +    no_new_p_before = dict(zip(no_new_p_before.split(), [1] * len(no_new_p_before)))
 105  
 106      def __init__(self, raw, request, **kw):
 107          self.raw = raw
 108          self.request = request
 109          self.form = request.form
 110          self._ = request.getText
 111 +        self.cfg = request.cfg
 112  
 113          self.macro = None
 114  
 115 @@ -108,22 +109,31 @@
 116          self.in_dd = 0
 117          self.in_pre = 0
 118          self.in_table = 0
 119 +        self.is_big = False
 120 +        self.is_small = False
 121          self.inhibit_p = 0 # if set, do not auto-create a <p>aragraph
 122 -        self.titles = {}
 123 +        self.titles = request._page_headings
 124  
 125          # holds the nesting level (in chars) of open lists
 126          self.list_indents = []
 127          self.list_types = []
 128 +        
 129 +        self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(wikimacro.getNames(self.cfg))}
 130  
 131      def _close_item(self, result):
 132          #result.append("<!-- close item begin -->\n")
 133 -        if self.formatter.in_p:
 134 -            result.append(self.formatter.paragraph(0))
 135 +        if self.in_table:
 136 +            result.append(self.formatter.table(0))
 137 +            self.in_table = 0
 138          if self.in_li:
 139              self.in_li = 0
 140 +            if self.formatter.in_p:
 141 +                result.append(self.formatter.paragraph(0))
 142              result.append(self.formatter.listitem(0))
 143          if self.in_dd:
 144              self.in_dd = 0
 145 +            if self.formatter.in_p:
 146 +                result.append(self.formatter.paragraph(0))
 147              result.append(self.formatter.definition_desc(0))
 148          #result.append("<!-- close item end -->\n")
 149  
 150 @@ -147,12 +157,11 @@
 151          elif config.allow_subpages and url[0] == wikiutil.CHILD_PREFIX:
 152              # fancy link to subpage [wiki:/SubPage text]
 153              return self._word_repl(url, text)
 154 -        elif Page(url).exists():
 155 +        elif Page(self.request, url).exists():
 156              # fancy link to local page [wiki:LocalPage text]
 157              return self._word_repl(url, text)
 158  
 159          wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, url)
 160 -        wikiurl = wikiutil.mapURL(wikiurl)
 161          href = wikiutil.join_wiki(wikiurl, wikitail)
 162  
 163          # check for image URL, and possibly return IMG tag
 164 @@ -163,19 +172,9 @@
 165          if wikitag is None:
 166              return self._word_repl(wikitail)
 167                
 168 -        # return InterWiki hyperlink
 169 -        if wikitag_bad:
 170 -            html_class = 'badinterwiki'
 171 -        else:
 172 -            html_class = 'interwiki'
 173 -        text = self.highlight_text(text) # also cgi.escapes if necessary
 174 -
 175 -        icon = ''
 176 -        if self.request.user.show_fancy_links:
 177 -            icon = self.request.theme.make_icon('interwiki', {'wikitag': wikitag}) 
 178 -        return self.formatter.url(href, icon + text,
 179 -            title=wikitag, unescaped=1, pretty_url=kw.get('pretty_url', 0), css = html_class)
 180 -
 181 +        return (self.formatter.interwikilink(1, wikitag, wikitail) + 
 182 +                self.formatter.text(text) +
 183 +                self.formatter.interwikilink(0))
 184  
 185      def attachment(self, url_and_text, **kw):
 186          """ This gets called on attachment URLs.
 187 @@ -209,15 +208,15 @@
 188              fname = fname + ".png"
 189              url = url + ".png"
 190              # fallback for old gif drawings (1.1 -> 1.2)
 191 -            fpath = os.path.join(AttachFile.getAttachDir(pagename), fname)
 192 +            fpath = AttachFile.getFilename(self.request, pagename, fname)
 193              if not os.path.exists(fpath):
 194                  gfname = fname[:-4] + ".gif"
 195                  gurl = url[:-4] + ".gif"
 196 -                gfpath = os.path.join(AttachFile.getAttachDir(pagename), gfname)
 197 +                gfpath = AttachFile.getFilename(self.request, pagename, gfname)
 198                  if os.path.exists(gfpath):
 199                      fname, url, fpath = gfname, gurl, gfpath
 200          else:
 201 -            fpath = os.path.join(AttachFile.getAttachDir(pagename), fname)
 202 +            fpath = AttachFile.getFilename(self.request, pagename, fname)
 203  
 204          # check whether attachment exists, possibly point to upload form
 205          if not os.path.exists(fpath):
 206 @@ -226,19 +225,19 @@
 207              else:
 208                  linktext = _('Upload new attachment "%(filename)s"')
 209              return wikiutil.link_tag(self.request,
 210 -                '%s?action=AttachFile&amp;rename=%s%s' % (
 211 -                    wikiutil.quoteWikiname(pagename),
 212 -                    urllib.quote_plus(fname),
 213 -                    drawing and ('&amp;drawing=%s' % urllib.quote(drawing)) or ''),
 214 -                linktext % {'filename': fname})
 215 +                self.formatter.text('%s?action=AttachFile&rename=%s%s' % (
 216 +                    wikiutil.quoteWikinameURL(pagename),
 217 +                    urllib.quote_plus(fname.encode(config.charset)),
 218 +                    drawing and ('&drawing=%s' % urllib.quote(drawing.encode(config.charset))) or '')),
 219 +                linktext % {'filename': self.formatter.text(fname)})
 220  
 221          # check for image URL, and possibly return IMG tag
 222          # (images are always inlined, just like for other URLs)
 223          if not kw.get('pretty_url', 0) and wikiutil.isPicture(url):
 224              if drawing:
 225                  # check for map file
 226 -                mappath = os.path.join(AttachFile.getAttachDir(pagename), drawing + '.map')
 227 -                edit_link = '%s?action=AttachFile&amp;rename=%s&amp;drawing=%s' % (wikiutil.quoteWikiname(pagename), urllib.quote_plus(fname), urllib.quote(drawing))
 228 +                mappath = AttachFile.getFilename(self.request, pagename, drawing + '.map')
 229 +                edit_link = self.formatter.text('%s?action=AttachFile&rename=%s&drawing=%s' % (wikiutil.quoteWikinameURL(pagename), urllib.quote_plus(fname.encode(config.charset)), urllib.quote(drawing.encode(config.charset))))
 230                  if os.path.exists(mappath):
 231                      # we have a image map. inline it and add a map ref
 232                      # to the img tag
 233 @@ -255,7 +254,7 @@
 234                          # add alt and title tags to areas
 235                          map = re.sub('href\s*=\s*"((?!%TWIKIDRAW%).+?)"',r'href="\1" alt="\1" title="\1"',map)
 236                          # add in edit links plus alt and title attributes
 237 -                        map = map.replace('%TWIKIDRAW%"', edit_link + '" alt="' + _('Edit drawing %(filename)s') % {'filename': fname} + '" title="' + _('Edit drawing %(filename)s') % {'filename': fname} + '"')
 238 +                        map = map.replace('%TWIKIDRAW%"', edit_link + '" alt="' + _('Edit drawing %(filename)s') % {'filename': self.formatter.text(fname)} + '" title="' + _('Edit drawing %(filename)s') % {'filename': self.formatter.text(fname)} + '"')
 239                          # unxml, because 4.01 concrete will not validate />
 240                          map = map.replace('/>','>')
 241                          return map + self.formatter.image(alt=drawing,
 242 @@ -265,36 +264,51 @@
 243                          edit_link,
 244                          self.formatter.image(alt=url,
 245                              src=AttachFile.getAttachUrl(pagename, url, self.request, addts=1), html_class="drawing"),
 246 -                        attrs='title="%s"' % (_('Edit drawing %(filename)s') % {'filename': fname}))
 247 +                        attrs='title="%s"' % (_('Edit drawing %(filename)s') % {'filename': self.formatter.text(fname)}))
 248              else:
 249                  return self.formatter.image(alt=url,
 250                      src=AttachFile.getAttachUrl(pagename, url, self.request, addts=1))
 251  
 252 -        # try to inline the attachment (we only accept a list
 253 -        # of known extensions)
 254 +        # try to inline the attachment (parser know what they
 255 +        # can handle)
 256          base, ext = os.path.splitext(url)
 257 -        if inline and ext in ['.py']:
 258 -            if ext == '.py':
 259 -                import cStringIO
 260 -                from MoinMoin.parser import python
 261 -
 262 -                buff = cStringIO.StringIO()
 263 -                colorizer = python.Parser(open(fpath, 'r').read(), self.request, out = buff)
 264 +        if inline:
 265 +            Parser = wikiutil.getParserForExtension(self.cfg, ext)
 266 +            if Parser is not None:
 267 +                content = file(fpath, 'r').read()
 268 +                # Try to decode text. It might return junk, but we don't
 269 +                # have enough information with attachments.
 270 +                content = wikiutil.decodeUnknownInput(content)
 271 +                colorizer = Parser(content, self.request)
 272                  colorizer.format(self.formatter)
 273 -                return self.formatter.preformatted(1) + \
 274 -                    self.formatter.rawHTML(buff.getvalue()) + \
 275 -                    self.formatter.preformatted(0)
 276 -
 277 -        return self.formatter.url(
 278 -            AttachFile.getAttachUrl(pagename, url, self.request),
 279 -            text, pretty_url=kw.get('pretty_url', 0))
 280  
 281 +        url = AttachFile.getAttachUrl(pagename, url, self.request)
 282 +
 283 +        if kw.get('pretty_url', 0) and wikiutil.isPicture(url):
 284 +            return self.formatter.image(src=url)
 285 +        else:
 286 +            return (self.formatter.url(1, url) +
 287 +                    self.formatter.text(text) +
 288 +                    self.formatter.url(0))
 289  
 290      def _u_repl(self, word):
 291          """Handle underline."""
 292          self.is_u = not self.is_u
 293          return self.formatter.underline(self.is_u)
 294  
 295 +    def _small_repl(self, word):
 296 +        """Handle small."""
 297 +        if word.strip() == '~-' and self.is_small: return word
 298 +        if word.strip() == '-~' and not self.is_small: return word
 299 +        self.is_small = not self.is_small
 300 +        return self.formatter.small(self.is_small)
 301 +
 302 +    def _big_repl(self, word):
 303 +        """Handle big."""
 304 +        if word.strip() == '~+' and self.is_big: return word
 305 +        if word.strip() == '+~' and not self.is_big: return word
 306 +        self.is_big = not self.is_big
 307 +        return self.formatter.big(self.is_big)
 308  
 309      def _emph_repl(self, word):
 310          """Handle emphasis, i.e. '' and '''."""
 311 @@ -337,25 +351,27 @@
 312      def _sup_repl(self, word):
 313          """Handle superscript."""
 314          return self.formatter.sup(1) + \
 315 -            self.highlight_text(word[1:-1]) + \
 316 +            self.formatter.text(word[1:-1]) + \
 317              self.formatter.sup(0)
 318  
 319  
 320      def _sub_repl(self, word):
 321          """Handle subscript."""
 322          return self.formatter.sub(1) + \
 323 -            self.highlight_text(word[2:-2]) + \
 324 +            self.formatter.text(word[2:-2]) + \
 325              self.formatter.sub(0)
 326  
 327  
 328      def _rule_repl(self, word):
 329          """Handle sequences of dashes."""
 330 -        self.inhibit_p = 1
 331 -        result = self._undent()
 332 +        ##self.inhibit_p = 1
 333 +        result = self._undent() + self._closeP()
 334          if len(word) <= 4:
 335              result = result + self.formatter.rule()
 336          else:
 337 -            result = result + self.formatter.rule(min(len(word), 10) - 2)
 338 +            # Create variable rule size 1 - 6. Actual size defined in css.
 339 +            size = min(len(word), 10) - 4
 340 +            result = result + self.formatter.rule(size)
 341          return result
 342  
 343  
 344 @@ -372,20 +388,17 @@
 345          if not text:
 346              # if a simple, self-referencing link, emit it as plain text
 347              if word == self.formatter.page.page_name:
 348 -                return word
 349 +                return self.formatter.text(word)
 350              text = word
 351          if config.allow_subpages and word.startswith(wikiutil.CHILD_PREFIX):
 352              word = self.formatter.page.page_name + word
 353 -        text = self.highlight_text(text)
 354 -        if word == text:
 355 -            return self.formatter.pagelink(word)
 356 -        else:
 357 -            return self.formatter.pagelink(word, text)
 358 +        return (self.formatter.pagelink(1, word) +
 359 +                self.formatter.text(text) +
 360 +                self.formatter.pagelink(0, word))
 361  
 362      def _notword_repl(self, word):
 363          """Handle !NotWikiNames."""
 364 -        return self.highlight_text(word[1:])
 365 -
 366 +        return self.formatter.text(word[1:])
 367  
 368      def _interwiki_repl(self, word):
 369          """Handle InterWiki links."""
 370 @@ -400,7 +413,15 @@
 371          if scheme in self.attachment_schemas:
 372              return self.attachment([word])
 373  
 374 -        return self.formatter.url(word, text=self.highlight_text(word))
 375 +        if wikiutil.isPicture(word):
 376 +            # Get image name http://here.com/dir/image.gif -> image
 377 +            name = word.split('/')[-1]
 378 +            name = ''.join(name.split('.')[:-1])
 379 +            return self.formatter.image(src=word, alt=name)
 380 +        else:
 381 +            return (self.formatter.url(1, word, type='www') +
 382 +                    self.formatter.text(word) +
 383 +                    self.formatter.url(0))
 384  
 385  
 386      def _wikiname_bracket_repl(self, word):
 387 @@ -427,7 +448,9 @@
 388  
 389          if words[0][0] == '#':
 390              # anchor link
 391 -            return self.formatter.url(words[0], self.highlight_text(words[1]))
 392 +            return (self.formatter.url(1, words[0]) +
 393 +                    self.formatter.text(words[1]) +
 394 +                    self.formatter.url(0))
 395  
 396          scheme = words[0].split(":", 1)[0]
 397          if scheme == "wiki": return self.interwiki(words, pretty_url=1)
 398 @@ -435,16 +458,21 @@
 399              return self.attachment(words, pretty_url=1)
 400  
 401          if wikiutil.isPicture(words[1]) and re.match(self.url_rule, words[1]):
 402 -            text = self.formatter.image(title=words[0], alt=words[0], src=words[1])
 403 +            return (self.formatter.url(1, words[0], 'external', unescaped=1) +
 404 +                    self.formatter.image(title=words[0], alt=words[0], src=words[1]) +
 405 +                    self.formatter.url(0))
 406          else:
 407 -            text = web.getLinkIcon(self.request, self.formatter, scheme)
 408 -            text += self.highlight_text(words[1])
 409 -        return self.formatter.url(words[0], text, 'external', pretty_url=1, unescaped=1)
 410 +            return (self.formatter.url(1, words[0], 'external',
 411 +                                       type='www', unescaped=1) +
 412 +                    self.formatter.text(words[1]) +
 413 +                    self.formatter.url(0))
 414  
 415  
 416      def _email_repl(self, word):
 417          """Handle email addresses (without a leading mailto:)."""
 418 -        return self.formatter.url("mailto:" + word, self.highlight_text(word))
 419 +        return (self.formatter.url(1, "mailto:" + word, type='mailto') +
 420 +                self.formatter.text(word) +
 421 +                self.formatter.url(0))
 422  
 423  
 424      def _ent_repl(self, word):
 425 @@ -463,15 +491,22 @@
 426      def _li_repl(self, match):
 427          """Handle bullet lists."""
 428          result = []
 429 +        indented_only = (match == (" " * len(match)))
 430 +        if indented_only and self.in_li: return ''
 431 +            
 432          self._close_item(result)
 433 -        self.inhibit_p = 1
 434 +        #self.inhibit_p = 1
 435          self.in_li = 1
 436          css_class = ''
 437          if self.line_was_empty and not self.first_list_item:
 438              css_class = 'gap'
 439 -        result.append(" "*4*self._indent_level())
 440 -        result.append(self.formatter.listitem(1, css_class=css_class))
 441 -        result.append(self.formatter.paragraph(1))
 442 +        if indented_only:
 443 +            result.append(self.formatter.listitem(1, css_class=css_class,
 444 +                                             style="list-style-type:none"))
 445 +        else:
 446 +            result.append(self.formatter.listitem(1, css_class=css_class))
 447 +        # Suspected p!
 448 +        ## result.append(self.formatter.paragraph(1))
 449          return ''.join(result)
 450  
 451  
 452 @@ -484,15 +519,15 @@
 453          """Handle definition lists."""
 454          result = []
 455          self._close_item(result)
 456 -        self.inhibit_p = 1
 457 +        #self.inhibit_p = 1
 458          self.in_dd = 1
 459          result.extend([
 460 -            " "*4*self._indent_level(),
 461              self.formatter.definition_term(1),
 462 -            self.formatter.text(match[:-3]),
 463 +            self.formatter.text(match[1:-3]),
 464              self.formatter.definition_term(0),
 465              self.formatter.definition_desc(1),
 466 -            self.formatter.paragraph(1)
 467 +            ## CHANGE: no automatic paragraph
 468 +            ##self.formatter.paragraph(1)
 469          ])
 470          return ''.join(result)
 471  
 472 @@ -507,73 +542,72 @@
 473          open = []   # don't make one out of these two statements!
 474          close = []
 475  
 476 -        # Close open paragraphs and list items
 477 -        if self._indent_level() != new_level:
 478 -            self._close_item(close)
 479 -        else:
 480 -            if not self.line_was_empty:                                                                                                       
 481 -                self.inhibit_p = 1                                                                                                            
 482 +
 483 +        if self._indent_level() != new_level and self.in_table:
 484 +            close.append(self.formatter.table(0))
 485 +            self.in_table = 0
 486 +        #    #self._close_item(close)
 487 +        #else:
 488 +        #    if not self.line_was_empty:
 489 +        #        self.inhibit_p = 1
 490      
 491          # Close lists while char-wise indent is greater than the current one
 492 -        while self._indent_level() > new_level:
 493 -            indentstr = " "*4*self._indent_level()
 494 +        while ((self._indent_level() > new_level) or
 495 +               ( new_level and
 496 +                (self._indent_level() == new_level) and
 497 +                (self.list_types[-1]) != list_type)):
 498 +            self._close_item(close)
 499              if self.list_types[-1] == 'ol':
 500                  tag = self.formatter.number_list(0)
 501              elif self.list_types[-1] == 'dl':
 502                  tag = self.formatter.definition_list(0)
 503              else:
 504                  tag = self.formatter.bullet_list(0)
 505 -            close.append("\n%s%s\n" % (indentstr, tag))
 506 +            close.append(tag)
 507  
 508              del(self.list_indents[-1])
 509              del(self.list_types[-1])
 510              
 511 -            if new_level:
 512 -                self.inhibit_p = 1
 513 -            else:
 514 -                self.inhibit_p = 0
 515 +            #if new_level:
 516 +            #    self.inhibit_p = 1
 517 +            #else:
 518 +            #    self.inhibit_p = 0
 519 +
 520 +            if self.list_types: # we are still in a list
 521 +                if self.list_types[-1] == 'dl':
 522 +                    self.in_dd = 1
 523 +                else:
 524 +                    self.in_li = 1
 525                  
 526 -            # XXX This would give valid, but silly looking html.
 527 -            # the right way is that inner list has to be CONTAINED in outer li -
 528 -            # but in the one before, not a new one, like this code does:
 529 -            #if self.list_types: # we are still in a list, bracket with li /li
 530 -            #    if self.list_types[-1] in ['ol', 'ul']:
 531 -            #        open.append(" "*4*new_level)
 532 -            #        open.append(self.formatter.listitem(0))
 533 -            #    elif self.list_types[-1] == 'dl':
 534 -            #        open.append(" "*4*new_level)
 535 -            #        open.append(self.formatter.definition_desc(0))
 536 -
 537          # Open new list, if necessary
 538          if self._indent_level() < new_level:
 539 -            # XXX see comment 10 lines above
 540 -            #if self.list_types: # we already are in a list, bracket with li /li
 541 -            #    if self.list_types[-1] in ['ol', 'ul']:
 542 -            #        open.append(" "*4*new_level)
 543 -            #        open.append(self.formatter.listitem(1))
 544 -            #    elif self.list_types[-1] == 'dl':
 545 -            #        open.append(" "*4*new_level)
 546 -            #        open.append(self.formatter.definition_desc(1))
 547                      
 548              self.list_indents.append(new_level)
 549              self.list_types.append(list_type)
 550 +
 551 +            if self.formatter.in_p:
 552 +                close.append(self.formatter.paragraph(0))
 553              
 554 -            indentstr = " "*4*new_level
 555              if list_type == 'ol':
 556                  tag = self.formatter.number_list(1, numtype, numstart)
 557              elif list_type == 'dl':
 558                  tag = self.formatter.definition_list(1)
 559              else:
 560                  tag = self.formatter.bullet_list(1)
 561 -            open.append("\n%s%s\n" % (indentstr, tag))
 562 +            open.append(tag)
 563              
 564              self.first_list_item = 1
 565 -            self.inhibit_p = 1
 566 -            
 567 +            ## Maybe this prevent p creation in lists?
 568 +            ##self.inhibit_p = 1
 569 +            self.in_li = 0
 570 +            self.in_dd = 0
 571          # If list level changes, close an open table
 572          if self.in_table and (open or close):
 573              close[0:0] = [self.formatter.table(0)]
 574              self.in_table = 0
 575 +        
 576 +        ## Maybe this prevent p creation in lists?
 577 +        ##self.inhibit_p = bool(self.list_types)
 578  
 579          return ''.join(close) + ''.join(open)
 580  
 581 @@ -599,7 +633,7 @@
 582      def _tt_repl(self, word):
 583          """Handle inline code."""
 584          return self.formatter.code(1) + \
 585 -            self.highlight_text(word[3:-3]) + \
 586 +            self.formatter.text(word[3:-3]) + \
 587              self.formatter.code(0)
 588  
 589  
 590 @@ -607,7 +641,7 @@
 591          """Handle backticked inline code."""
 592          if len(word) == 2: return ""
 593          return self.formatter.code(1) + \
 594 -            self.highlight_text(word[1:-1]) + \
 595 +            self.formatter.text(word[1:-1]) + \
 596              self.formatter.code(0)
 597  
 598  
 599 @@ -689,22 +723,32 @@
 600      def _tableZ_repl(self, word):
 601          """Handle table row end."""
 602          if self.in_table:
 603 -            return self.formatter.table_cell(0) + self.formatter.table_row(0)
 604 +            result = ''
 605 +            # REMOVED: check for self.in_li, p should always close
 606 +            if self.formatter.in_p:
 607 +                result = self.formatter.paragraph(0)
 608 +            result += self.formatter.table_cell(0) + self.formatter.table_row(0)
 609 +            return result
 610          else:
 611              return word
 612  
 613      def _table_repl(self, word):
 614          """Handle table cell separator."""
 615          if self.in_table:
 616 +            result = []
 617              # check for attributes
 618              attrs, attrerr = self._getTableAttrs(word)
 619  
 620              # start the table row?
 621              if self.table_rowstart:
 622                  self.table_rowstart = 0
 623 -                leader = self.formatter.table_row(1, attrs)
 624 +                result.append(self.formatter.table_row(1, attrs))
 625              else:
 626 -                leader = self.formatter.table_cell(0)
 627 +                # Close table cell, first closing open p
 628 +                # REMOVED check for self.in_li, paragraph should close always!
 629 +                if self.formatter.in_p:
 630 +                    result.append(self.formatter.paragraph(0))
 631 +                result.append(self.formatter.table_cell(0))
 632  
 633              # check for adjacent cell markers
 634              if word.count("|") > 2:
 635 @@ -713,8 +757,9 @@
 636                  if not attrs.has_key('colspan'):
 637                      attrs['colspan'] = '"%d"' % (word.count("|")/2)
 638  
 639 -            # return the complete cell markup           
 640 -            return leader + self.formatter.table_cell(1, attrs) + attrerr
 641 +            # return the complete cell markup
 642 +            result.append(self.formatter.table_cell(1, attrs) + attrerr)         
 643 +            return ''.join(result) 
 644          else:
 645              return word
 646  
 647 @@ -723,13 +768,7 @@
 648          """Handle section headings."""
 649          import sha
 650  
 651 -        self.inhibit_p = 1
 652 -        icons = ''
 653 -        if self.request.user.show_topbottom:
 654 -            bottom = self.request.theme.make_icon('bottom')
 655 -            icons = icons + self.formatter.url("#bottom", bottom, unescaped=1)
 656 -            top = self.request.theme.make_icon('top')
 657 -            icons = icons + self.formatter.url("#top", top, unescaped=1)
 658 +        ##self.inhibit_p = 1
 659  
 660          h = word.strip()
 661          level = 1
 662 @@ -737,46 +776,57 @@
 663              level = level+1
 664          depth = min(5,level)
 665  
 666 +        # this is needed for Included pages
 667 +        # TODO but it might still result in unpredictable results
 668 +        # when included the same page multiple times
 669          title_text = h[level:-level].strip()
 670 -        self.titles.setdefault(title_text, 0)
 671 -        self.titles[title_text] += 1
 672 +        pntt = self.formatter.page.page_name + title_text
 673 +        self.titles.setdefault(pntt, 0)
 674 +        self.titles[pntt] += 1
 675  
 676          unique_id = ''
 677 -        if self.titles[title_text] > 1:
 678 -            unique_id = '-%d' % self.titles[title_text]
 679 -
 680 -        return self.formatter.heading(depth, self.highlight_text(title_text), icons=icons, id="head-"+sha.new(title_text).hexdigest()+unique_id)
 681 -
 682 -
 683 +        if self.titles[pntt] > 1:
 684 +            unique_id = '-%d' % self.titles[pntt]
 685 +        result = self._closeP()
 686 +        result += self.formatter.heading(1, depth, id="head-"+sha.new(pntt.encode(config.charset)).hexdigest()+unique_id)
 687 +                                     
 688 +        return (result + self.formatter.text(title_text) +
 689 +                self.formatter.heading(0, depth))
 690 +    
 691      def _processor_repl(self, word):
 692          """Handle processed code displays."""
 693          if word[:3] == '{{{': word = word[3:]
 694  
 695          self.processor = None
 696          self.processor_name = None
 697 +        self.processor_is_parser = 0
 698          s_word = word.strip()
 699          if s_word == '#!':
 700              # empty bang paths lead to a normal code display
 701              # can be used to escape real, non-empty bang paths
 702              word = ''
 703              self.in_pre = 3
 704 -            return  self.formatter.preformatted(1)
 705 +            return self._closeP() + self.formatter.preformatted(1)
 706          elif s_word[:2] == '#!':
 707 +            # first try to find a processor for this (will go away in 1.4)
 708              processor_name = s_word[2:].split()[0]
 709 -            self.processor = wikiutil.importPlugin("processor", processor_name, "process")
 710 -            if not self.processor and s_word.find('python') > 0:
 711 -                from MoinMoin.processor.Colorize import process
 712 -                self.processor = process
 713 -                self.processor_name = "Colorize"
 714 +            self.processor = wikiutil.importPlugin(
 715 +                self.request.cfg, "processor", processor_name, "process")
 716 +            # now look for a parser with that name
 717 +            if self.processor is None:
 718 +                self.processor = wikiutil.importPlugin(
 719 +                    self.request.cfg, "parser", processor_name, "Parser")
 720 +                if self.processor:
 721 +                    self.processor_is_parser = 1
 722  
 723          if self.processor:
 724              self.processor_name = processor_name
 725              self.in_pre = 2
 726              self.colorize_lines = [word]
 727              return ''
 728 -        elif  s_word:
 729 +        elif s_word:
 730              self.in_pre = 3
 731 -            return self.formatter.preformatted(1) + \
 732 +            return self._closeP() + self.formatter.preformatted(1) + \
 733                     self.formatter.text(s_word + ' (-)')
 734          else:
 735              self.in_pre = 1
 736 @@ -787,17 +837,18 @@
 737          word = word.strip()
 738          if word == '{{{' and not self.in_pre:
 739              self.in_pre = 3
 740 -            return self.formatter.preformatted(self.in_pre)
 741 +            ##self.inhibit_p = 1
 742 +            return self._closeP() + self.formatter.preformatted(self.in_pre)
 743          elif word == '}}}' and self.in_pre:
 744              self.in_pre = 0
 745 -            self.inhibit_p = 1
 746 +            self.inhibit_p = 0
 747              return self.formatter.preformatted(self.in_pre)
 748          return word
 749  
 750  
 751      def _smiley_repl(self, word):
 752          """Handle smileys."""
 753 -        return wikiutil.getSmiley(word, self.formatter)
 754 +        return self.formatter.smiley(word)
 755  
 756      _smileyA_repl = _smiley_repl
 757  
 758 @@ -805,7 +856,11 @@
 759      def _comment_repl(self, word):
 760          return ''
 761  
 762 -
 763 +    def _closeP(self):
 764 +        if self.formatter.in_p:
 765 +            return self.formatter.paragraph(0)
 766 +        return ''
 767 +        
 768      def _macro_repl(self, word):
 769          """Handle macros ([[macroname]])."""
 770          macro_name = word[2:-2]
 771 @@ -820,71 +875,63 @@
 772          # create macro instance
 773          if self.macro is None:
 774              self.macro = wikimacro.Macro(self)
 775 -
 776 -        # call the macro
 777          return self.formatter.macro(self.macro, macro_name, args)
 778  
 779 -
 780 -    def highlight_text(self, text, **kw):
 781 -        if not self.hilite_re: return self.formatter.text(text)
 782 -        
 783 -        # work around for dom/xml formatter
 784 -        # if not self.hilite_re: return text
 785 -        # XXX bad idea: this allowed `<b>raw html</b>` to get through!
 786 -        
 787 -        result = []
 788 -        lastpos = 0
 789 -        match = self.hilite_re.search(text)
 790 -        while match and lastpos < len(text):
 791 -            # add the match we found
 792 -            result.append(self.formatter.text(text[lastpos:match.start()]))
 793 -            result.append(self.formatter.highlight(1))
 794 -            result.append(self.formatter.text(match.group(0)))
 795 -            result.append(self.formatter.highlight(0))
 796 -
 797 -            # search for the next one
 798 -            lastpos = match.end() + (match.end() == lastpos)
 799 -            match = self.hilite_re.search(text, lastpos)
 800 -
 801 -        result.append(self.formatter.text(text[lastpos:]))
 802 -        return ''.join(result)
 803 -
 804      def scan(self, scan_re, line):
 805 -        """ scans the line for wiki syntax and replaces the
 806 -            found regular expressions
 807 -            calls highlight_text if self.hilite_re is set
 808 +        """ Scans one line
 809 +        
 810 +        Append text before match, invoke replace() with match, and 
 811 +        add text after match.
 812          """
 813          result = []
 814          lastpos = 0
 815 -        match = scan_re.search(line)
 816 -        while match and lastpos < len(line):
 817 -            # add the match we found
 818 -            if self.hilite_re:
 819 -                result.append(self.highlight_text(line[lastpos:match.start()]))
 820 -            else:
 821 +
 822 +        ###result.append(u'<span class="info">[scan: <tt>"%s"</tt>]</span>' % line)
 823 +      
 824 +        for match in scan_re.finditer(line):
 825 +            # Add text before the match
 826 +            if lastpos < match.start():
 827 +                
 828 +                ###result.append(u'<span class="info">[add text before match: <tt>"%s"</tt>]</span>' % line[lastpos:match.start()])
 829 +                
 830 +                if not (self.inhibit_p or self.in_pre or self.formatter.in_p):
 831 +                    result.append(self.formatter.paragraph(1))
 832                  result.append(self.formatter.text(line[lastpos:match.start()]))
 833 +            
 834 +            # Replace match with markup
 835              result.append(self.replace(match))
 836 -
 837 -            # search for the next one
 838 -            lastpos = match.end() + (match.end() == lastpos)
 839 -            match = scan_re.search(line, lastpos)
 840 -
 841 -        if self.hilite_re:
 842 -            result.append(self.highlight_text(line[lastpos:]))
 843 -        else:
 844 -            result.append(self.formatter.text(line[lastpos:]))
 845 -        return ''.join(result)
 846 +            lastpos = match.end()
 847 +        
 848 +        ###result.append('<span class="info">[no match, add rest: <tt>"%s"<tt>]</span>' % line[lastpos:])
 849 +        
 850 +        # No match: Add paragraph with the text of the line
 851 +        if not (self.in_pre or self.inhibit_p or
 852 +                self.formatter.in_p) and lastpos < len(line):
 853 +            result.append(self.formatter.paragraph(1))
 854 +        result.append(self.formatter.text(line[lastpos:]))
 855 +        return u''.join(result)
 856  
 857      def replace(self, match):
 858 -        #hit = filter(lambda g: g[1], match.groupdict().items())
 859 +        """ Replace match using type name """
 860 +        result = []
 861          for type, hit in match.groupdict().items():
 862              if hit is not None and type != "hmarker":
 863 -                ##print "###", cgi.escape(`type`), cgi.escape(`hit`), "###"
 864 +                
 865 +                ###result.append(u'<span class="info">[replace: %s: "%s"]</span>' % (type, hit))
 866                  if self.in_pre and type not in ['pre', 'ent']:
 867 -                    return self.highlight_text(hit)
 868 +                    return self.formatter.text(hit) 
 869                  else:
 870 -                    return getattr(self, '_' + type + '_repl')(hit)
 871 +                    # Open p for certain types
 872 +                    if not (self.inhibit_p or self.formatter.in_p
 873 +                            or self.in_pre or (type in self.no_new_p_before)):
 874 +                        result.append(self.formatter.paragraph(1))
 875 +                    
 876 +                    # Get replace method and replece hit
 877 +                    replace = getattr(self, '_' + type + '_repl')
 878 +                    result.append(replace(hit))
 879 +                    return ''.join(result)
 880          else:
 881 +            # We should never get here
 882              import pprint
 883              raise Exception("Can't handle match " + `match`
 884                  + "\n" + pprint.pformat(match.groupdict())
 885 @@ -892,7 +939,6 @@
 886  
 887          return ""
 888  
 889 -
 890      def format(self, formatter):
 891          """ For each line, scan through looking for magic
 892              strings, outputting verbatim any intervening text.
 893 @@ -902,23 +948,25 @@
 894  
 895          # prepare regex patterns
 896          rules = self.formatting_rules.replace('\n', '|')
 897 -        if config.allow_extended_names:
 898 -            rules = rules + r'|(?P<wikiname_bracket>\[".*?"\])'
 899 -        if config.bang_meta:
 900 -            rules = r'(?P<notword>!%(word_rule)s)|%(rules)s' % {
 901 +        if self.cfg.allow_extended_names:
 902 +            rules = rules + ur'|(?P<wikiname_bracket>\[".*?"\])'
 903 +        if self.cfg.bang_meta:
 904 +            rules = ur'(?P<notword>!%(word_rule)s)|%(rules)s' % {
 905                  'word_rule': self.word_rule,
 906                  'rules': rules,
 907              }
 908 -        if config.backtick_meta:
 909 -            rules = rules + r'|(?P<tt_bt>`.*?`)'
 910 -        if config.allow_numeric_entities:
 911 -            rules = r'(?P<ent_numeric>&#\d{1,5};)|' + rules
 912 -
 913 -        scan_re = re.compile(rules)
 914 -        number_re = re.compile(self.ol_rule)
 915 -        term_re = re.compile(self.dl_rule)
 916 -        indent_re = re.compile("^\s*")
 917 -        eol_re = re.compile(r'\r?\n')
 918 +        if self.cfg.backtick_meta:
 919 +            rules = rules + ur'|(?P<tt_bt>`.*?`)'
 920 +        if self.cfg.allow_numeric_entities:
 921 +            rules = ur'(?P<ent_numeric>&#\d{1,5};)|' + rules
 922 +
 923 +        self.request.clock.start('compile_huge_and_ugly')        
 924 +        scan_re = re.compile(rules, re.UNICODE)
 925 +        number_re = re.compile(self.ol_rule, re.UNICODE)
 926 +        term_re = re.compile(self.dl_rule, re.UNICODE)
 927 +        indent_re = re.compile("^\s*", re.UNICODE)
 928 +        eol_re = re.compile(r'\r?\n', re.UNICODE)
 929 +        self.request.clock.stop('compile_huge_and_ugly')        
 930  
 931          # get text and replace TABs
 932          rawtext = self.raw.expandtabs()
 933 @@ -928,6 +976,7 @@
 934          self.lines = eol_re.split(rawtext)
 935          self.line_is_empty = 0
 936  
 937 +        # Main loop
 938          for line in self.lines:
 939              self.lineno = self.lineno + 1
 940              self.table_rowstart = 1
 941 @@ -937,25 +986,32 @@
 942              self.inhibit_p = 0
 943  
 944              if self.in_pre:
 945 +                # TODO: move this into function
 946                  # still looking for processing instructions
 947 +                # TODO: use strings for pre state, not numbers
 948                  if self.in_pre == 1:
 949                      self.processor = None
 950 +                    self.processor_is_parser = 0
 951                      processor_name = ''
 952                      if (line.strip()[:2] == "#!"):
 953 -                        from MoinMoin.processor import processors
 954                          processor_name = line.strip()[2:].split()[0]
 955 -                        self.processor = wikiutil.importPlugin("processor", processor_name, "process")
 956 -                        if not self.processor and (line.find('python') > 0):
 957 -                            from MoinMoin.processor.Colorize import process
 958 -                            self.processor = process
 959 -                            processor_name = "Colorize"
 960 +                        self.processor = wikiutil.importPlugin(
 961 +                            self.request.cfg, "processor", processor_name, "process")
 962 +                                                               
 963 +                        # now look for a parser with that name
 964 +                        if self.processor is None:
 965 +                            self.processor = wikiutil.importPlugin(
 966 +                                self.request.cfg, "parser", processor_name, "Parser") 
 967 +                            if self.processor:
 968 +                                self.processor_is_parser = 1
 969                      if self.processor:
 970                          self.in_pre = 2
 971                          self.colorize_lines = [line]
 972                          self.processor_name = processor_name
 973                          continue
 974                      else:
 975 -                        self.request.write(self.formatter.preformatted(1))
 976 +                        self.request.write(self._closeP() +
 977 +                                           self.formatter.preformatted(1))
 978                          self.in_pre = 3
 979                  if self.in_pre == 2:
 980                      # processing mode
 981 @@ -965,8 +1021,14 @@
 982                          continue
 983                      if line[:endpos]:
 984                          self.colorize_lines.append(line[:endpos])
 985 -                    self.request.write(
 986 -                        self.formatter.processor(self.processor_name, self.colorize_lines))
 987 +                    
 988 +                    # Close p before calling processor
 989 +                    # TODO: do we really need this?
 990 +                    self.request.write(self._closeP())
 991 +                    res = self.formatter.processor(self.processor_name,
 992 +                                                   self.colorize_lines, 
 993 +                                                   self.processor_is_parser)
 994 +                    self.request.write(res)
 995                      del self.colorize_lines
 996                      self.in_pre = 0
 997                      self.processor = None
 998 @@ -974,19 +1036,23 @@
 999                      # send rest of line through regex machinery
1000                      line = line[endpos+3:]                    
1001              else:
1002 -                # paragraph break on empty lines
1003 +                # we don't have \n as whitespace any more
1004 +                # This is the space between lines we join to one paragraph
1005 +                line = line + ' '
1006 +                
1007 +                # Paragraph break on empty lines
1008                  if not line.strip():
1009 -                    #self.request.write("<!-- empty line start -->\n")
1010 -                    if self.formatter.in_p:
1011 -                        self.request.write(self.formatter.paragraph(0))
1012                      if self.in_table:
1013                          self.request.write(self.formatter.table(0))
1014                          self.in_table = 0
1015 +                    # CHANGE: removed check for not self.list_types
1016 +                    # p should close on every empty line
1017 +                    if (self.formatter.in_p):
1018 +                        self.request.write(self.formatter.paragraph(0))
1019                      self.line_is_empty = 1
1020 -                    #self.request.write("<!-- empty line end -->\n")
1021                      continue
1022  
1023 -                # check indent level
1024 +                # Check indent level
1025                  indent = indent_re.match(line)
1026                  indlen = len(indent.group(0))
1027                  indtype = "ul"
1028 @@ -1010,45 +1076,50 @@
1029                              indtype = "dl"
1030  
1031                  # output proper indentation tags
1032 -                #self.request.write("<!-- inhibit_p==%d -->\n" % self.inhibit_p)
1033 -                #self.request.write("<!-- #%d calling _indent_to -->\n" % self.lineno)
1034 -                self.request.write(self._indent_to(indlen, indtype, numtype, numstart))
1035 -                #self.request.write("<!-- #%d after calling _indent_to -->\n" % self.lineno)
1036 -                #self.request.write("<!-- inhibit_p==%d -->\n" % self.inhibit_p)
1037 +                self.request.write(self._indent_to(indlen, indtype, numtype,
1038 +                                                   numstart))
1039  
1040 -                # start or end table mode
1041 -                if not self.in_table and line[indlen:indlen+2] == "||" and line[-2:] == "||":
1042 +                # Table mode
1043 +                # TODO: move into function?                
1044 +                if (not self.in_table and line[indlen:indlen + 2] == "||"
1045 +                    and line[-3:] == "|| " and len(line) >= 5 + indlen):
1046 +                    # Start table
1047 +                    if self.list_types and not self.in_li:
1048 +                        self.request.write(self.formatter.listitem
1049 +                                           (1, style="list-style-type:none"))
1050 +                        ## CHANGE: no automatic p on li
1051 +                        ##self.request.write(self.formatter.paragraph(1))
1052 +                        self.in_li = 1
1053 +                        
1054 +                    # CHANGE: removed check for self.in_li
1055 +                    # paragraph should end before table, always!
1056 +                    if self.formatter.in_p:
1057 +                        self.request.write(self.formatter.paragraph(0))
1058                      attrs, attrerr = self._getTableAttrs(line[indlen+2:])
1059                      self.request.write(self.formatter.table(1, attrs) + attrerr)
1060 -                    self.in_table = self.lineno
1061 -                elif self.in_table and not(line[:2]=="##" or # intra-table comments should not break a table 
1062 -                    line[indlen:indlen+2] == "||" and line[-2:] == "||"):
1063 +                    self.in_table = True # self.lineno
1064 +                elif (self.in_table and not
1065 +                      # intra-table comments should not break a table
1066 +                      (line[:2]=="##" or  
1067 +                       line[indlen:indlen + 2] == "||" and
1068 +                       line[-3:] == "|| " and
1069 +                       len(line) >= 5 + indlen)):
1070 +                    
1071 +                    # Close table
1072                      self.request.write(self.formatter.table(0))
1073                      self.in_table = 0
1074 -
1075 -            # convert line from wiki markup to HTML and print it
1076 -            if not self.in_pre:   # we don't want to have trailing blanks in pre
1077 -                line = line + " " # we don't have \n as whitespace any more
1078 -
1079 -            formatted_line = self.scan(scan_re, line) # this also sets self.inhibit_p as side effect!
1080 -            
1081 -            #self.request.write("<!-- inhibit_p==%d -->\n" % self.inhibit_p)
1082 -            if not (self.inhibit_p or self.in_pre or self.in_table or self.formatter.in_p):
1083 -                self.request.write(self.formatter.paragraph(1))
1084 -
1085 -            #self.request.write("<!-- %s\n     start -->\n" % line)
1086 +                                            
1087 +            # Scan line, format and write
1088 +            formatted_line = self.scan(scan_re, line)
1089              self.request.write(formatted_line)
1090 -            #self.request.write("<!-- end -->\n")
1091  
1092              if self.in_pre:
1093                  self.request.write(self.formatter.linebreak())
1094 -            #if self.in_li:
1095 -            #    self.in_li = 0
1096 -            #    self.request.write(self.formatter.listitem(0))
1097  
1098 -        # close code displays, paragraphs, tables and open lists
1099 +        # Close code displays, paragraphs, tables and open lists
1100 +        self.request.write(self._undent())
1101          if self.in_pre: self.request.write(self.formatter.preformatted(0))
1102          if self.formatter.in_p: self.request.write(self.formatter.paragraph(0))
1103          if self.in_table: self.request.write(self.formatter.table(0))
1104 -        self.request.write(self._undent())
1105 +
1106  
parser_12_13.diff

   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 
formatter_12_13.diff

MoinMoin: MoinMoinBugs/AttachFileLink/DiffsFrom12To13 (last edited 2007-10-29 19:07:17 by localhost)