Ticket #12713: trac_12713-sagenb.patch

File trac_12713-sagenb.patch, 44.4 KB (added by jhpalmieri, 4 years ago)

sagenb

  • deleted file sagenb/notebook/wiki2html.py

    # HG changeset patch
    # User J. H. Palmieri <palmieri@math.washington.edu>
    # Date 1333576641 25200
    # Node ID ece99a0458fc05e1e6c11ca675aec439bc0fb85c
    # Parent  2bfe429e479ac4a21ba0d797c2097f4943ee4007
    Remove MoinMoin
    
    diff --git a/sagenb/notebook/wiki2html.py b/sagenb/notebook/wiki2html.py
    deleted file mode 100644
    + -  
    1 # -*- coding: utf-8 -*
    2 """nodoctest
    3 """
    4 
    5 #############################################################################
    6 #       Copyright (C) 2007 William Stein <wstein@gmail.com>
    7 #  Distributed under the terms of the GNU General Public License (GPL)
    8 #  The full text of the GPL is available at:
    9 #                  http://www.gnu.org/licenses/
    10 #############################################################################
    11 
    12 """
    13  Wiki to HTML converter
    14 
    15  Adopted from the Moin Moin Markup Parser (which is GPL'd) and
    16     @copyright: 2000, 2001, 2002 by Jurgen Hermann <jh@web.de>
    17     @license: GNU GPL, see COPYING for details.
    18 
    19 AUTHOR:
    20     -- Jurgen Hermann: Moin Moin version
    21     -- William Stein: adoption for Sage
    22 """
    23 
    24 import os, re
    25 from MoinMoin import config, wikimacro, wikiutil
    26 from MoinMoin.Page import Page
    27 from MoinMoin.util import web
    28 
    29 Dependencies = []
    30 
    31 class Parser:
    32     """
    33         Object that turns Wiki markup into HTML.
    34 
    35         All formatting commands can be parsed one line at a time, though
    36         some state is carried over between lines.
    37 
    38         Methods named like _*_repl() are responsible to handle the named regex
    39         patterns defined in print_html().
    40     """
    41 
    42     # allow caching
    43     caching = 1
    44     Dependencies = []
    45 
    46     # some common strings
    47     PARENT_PREFIX = wikiutil.PARENT_PREFIX
    48     attachment_schemas = ["attachment", "inline", "drawing"]
    49     punct_pattern = re.escape(u'''"\'}]|:,.)?!''')
    50     url_pattern = (u'http|https|ftp|nntp|news|mailto|telnet|wiki|file|irc|' +
    51             u'|'.join(attachment_schemas) +
    52             (config.url_schemas and u'|' + u'|'.join(config.url_schemas) or ''))
    53 
    54     # some common rules
    55     word_rule = ur'(?:(?<![%(u)s%(l)s])|^)%(parent)s(?:%(subpages)s(?:[%(u)s][%(l)s]+){2,})+(?![%(u)s%(l)s]+)' % {
    56         'u': config.chars_upper,
    57         'l': config.chars_lower,
    58         'subpages': wikiutil.CHILD_PREFIX + '?',
    59         'parent': ur'(?:%s)?' % re.escape(PARENT_PREFIX),
    60     }
    61     url_rule = ur'%(url_guard)s(%(url)s)\:([^\s\<%(punct)s]|([%(punct)s][^\s\<%(punct)s]))+' % {
    62         'url_guard': u'(^|(?<!\w))',
    63         'url': url_pattern,
    64         'punct': punct_pattern,
    65     }
    66 
    67     ol_rule = ur"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
    68     dl_rule = ur"^\s+.*?::\s"
    69 
    70     # the big, fat, ugly one ;)
    71     formatting_rules = ur"""(?P<ent_numeric>&#(\d{1,5}|x[0-9a-fA-F]+);)
    72 (?:(?P<emph_ibb>'''''(?=[^']+'''))
    73 (?P<emph_ibi>'''''(?=[^']+''))
    74 (?P<emph_ib_or_bi>'{5}(?=[^']))
    75 (?P<emph>'{2,3})
    76 (?P<u>__)
    77 (?P<sup>\^.*?\^)
    78 (?P<sub>,,[^,]{1,40},,)
    79 (?P<tt>\{\{\{.*?\}\}\})
    80 (?P<processor>(\{\{\{(#!.*|\s*$)))
    81 (?P<pre>(\{\{\{ ?|\}\}\}))
    82 (?P<small>(\~- ?|-\~))
    83 (?P<big>(\~\+ ?|\+\~))
    84 (?P<strike>(--\(|\)--))
    85 (?P<rule>-{4,})
    86 (?P<comment>^\#\#.*$)
    87 (?P<macro>\[\[(%%(macronames)s)(?:\(.*?\))?\]\]))
    88 (?P<ol>%(ol_rule)s)
    89 (?P<dl>%(dl_rule)s)
    90 (?P<li>^\s+\*\s*)
    91 (?P<li_none>^\s+\.\s*)
    92 (?P<indent>^\s+)
    93 (?P<tableZ>\|\| $)
    94 (?P<table>(?:\|\|)+(?:<[^>]*?>)?(?!\|? $))
    95 (?P<heading>^\s*(?P<hmarker>=+)\s.*\s(?P=hmarker) $)
    96 (?P<interwiki>[A-Z][a-zA-Z]+\:[^\s'\"\:\<\|]([^\s%(punct)s]|([%(punct)s][^\s%(punct)s]))+)
    97 (?P<word>%(word_rule)s)
    98 (?P<url_bracket>\[((%(url)s)\:|#|\:)[^\s\]]+(\s[^\]]+)?\])
    99 (?P<url>%(url_rule)s)
    100 (?P<email>[-\w._+]+\@[\w-]+(\.[\w-]+)+)
    101 (?P<smiley>(?<=\s)(%(smiley)s)(?=\s))
    102 (?P<smileyA>^(%(smiley)s)(?=\s))
    103 (?P<ent_symbolic>&[a-zA-Z]+;)
    104 (?P<ent>[<>&])
    105 (?P<wikiname_bracket>\[".*?"\])
    106 (?P<tt_bt>`.*?`)"""  % {
    107 
    108         'url': url_pattern,
    109         'punct': punct_pattern,
    110         'ol_rule': ol_rule,
    111         'dl_rule': dl_rule,
    112         'url_rule': url_rule,
    113         'word_rule': word_rule,
    114         'smiley': u'|'.join(map(re.escape, config.smileys.keys()))}
    115 
    116     # Don't start p before these
    117     no_new_p_before = ("heading rule table tableZ tr td "
    118                        "ul ol dl dt dd li li_none indent "
    119                        "macro processor pre")
    120     no_new_p_before = no_new_p_before.split()
    121     no_new_p_before = dict(zip(no_new_p_before, [1] * len(no_new_p_before)))
    122 
    123     def __init__(self, raw, request, **kw):
    124         self.raw = raw
    125         self.request = request
    126         self.form = request.form
    127         self._ = request.getText
    128         self.cfg = request.cfg
    129         self.line_anchors = kw.get('line_anchors', True)
    130         self.macro = None
    131         self.start_line = kw.get('start_line', 0)
    132 
    133         self.is_em = 0
    134         self.is_b = 0
    135         self.is_u = 0
    136         self.is_strike = 0
    137         self.lineno = 0
    138         self.in_list = 0 # between <ul/ol/dl> and </ul/ol/dl>
    139         self.in_li = 0 # between <li> and </li>
    140         self.in_dd = 0 # between <dd> and </dd>
    141         self.in_pre = 0
    142         self.in_table = 0
    143         self.is_big = False
    144         self.is_small = False
    145         self.inhibit_p = 0 # if set, do not auto-create a <p>aragraph
    146         self.titles = request._page_headings
    147 
    148         # holds the nesting level (in chars) of open lists
    149         self.list_indents = []
    150         self.list_types = []
    151 
    152         self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(wikimacro.getNames(self.cfg))}
    153 
    154     def _close_item(self, result):
    155         #result.append("<!-- close item begin -->\n")
    156         if self.in_table:
    157             result.append(self.formatter.table(0))
    158             self.in_table = 0
    159         if self.in_li:
    160             self.in_li = 0
    161             if self.formatter.in_p:
    162                 result.append(self.formatter.paragraph(0))
    163             result.append(self.formatter.listitem(0))
    164         if self.in_dd:
    165             self.in_dd = 0
    166             if self.formatter.in_p:
    167                 result.append(self.formatter.paragraph(0))
    168             result.append(self.formatter.definition_desc(0))
    169         #result.append("<!-- close item end -->\n")
    170 
    171 
    172     def interwiki(self, url_and_text, **kw):
    173         # TODO: maybe support [wiki:Page http://wherever/image.png] ?
    174         if len(url_and_text) == 1:
    175             url = url_and_text[0]
    176             text = None
    177         else:
    178             url, text = url_and_text
    179 
    180         # keep track of whether this is a self-reference, so links
    181         # are always shown even the page doesn't exist.
    182         is_self_reference = 0
    183         url2 = url.lower()
    184         if url2.startswith('wiki:self:'):
    185             url = url[10:] # remove "wiki:self:"
    186             is_self_reference = 1
    187         elif url2.startswith('wiki:'):
    188             url = url[5:] # remove "wiki:"
    189            
    190         tag, tail = wikiutil.split_wiki(url)
    191         if text is None:
    192             if tag:
    193                 text = tail
    194             else:
    195                 text = url
    196                 url = ""
    197         elif (url.startswith(wikiutil.CHILD_PREFIX) or # fancy link to subpage [wiki:/SubPage text]
    198               is_self_reference or # [wiki:Self:LocalPage text] or [:LocalPage:text]
    199               Page(self.request, url).exists()): # fancy link to local page [wiki:LocalPage text]
    200             return self._word_repl(url, text)
    201 
    202         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, url)
    203         href = wikiutil.join_wiki(wikiurl, wikitail)
    204 
    205         # check for image URL, and possibly return IMG tag
    206         if not kw.get('pretty_url', 0) and wikiutil.isPicture(wikitail):
    207             return self.formatter.image(src=href)
    208 
    209         # link to self?
    210         if wikitag is None:
    211             return self._word_repl(wikitail)
    212              
    213         return (self.formatter.interwikilink(1, tag, tail) +
    214                 self.formatter.text(text) +
    215                 self.formatter.interwikilink(0, tag, tail))
    216 
    217     def attachment(self, url_and_text, **kw):
    218         """ This gets called on attachment URLs.
    219         """
    220         _ = self._
    221         if len(url_and_text) == 1:
    222             url = url_and_text[0]
    223             text = None
    224         else:
    225             url, text = url_and_text
    226 
    227         inline = url[0] == 'i'
    228         drawing = url[0] == 'd'
    229         url = url.split(":", 1)[1]
    230         url = wikiutil.url_unquote(url, want_unicode=True)
    231         text = text or url
    232 
    233         from MoinMoin.action import AttachFile
    234         if drawing:
    235             return self.formatter.attachment_drawing(url, text)
    236 
    237         # check for image URL, and possibly return IMG tag
    238         # (images are always inlined, just like for other URLs)
    239         if not kw.get('pretty_url', 0) and wikiutil.isPicture(url):
    240             return self.formatter.attachment_image(url)
    241                
    242         # inline the attachment
    243         if inline:
    244             return self.formatter.attachment_inlined(url, text)
    245 
    246         return self.formatter.attachment_link(url, text)
    247 
    248     def _u_repl(self, word):
    249         """Handle underline."""
    250         self.is_u = not self.is_u
    251         return self.formatter.underline(self.is_u)
    252 
    253     def _strike_repl(self, word):
    254         """Handle strikethrough."""
    255         # XXX we don't really enforce the correct sequence --( ... )-- here
    256         self.is_strike = not self.is_strike
    257         return self.formatter.strike(self.is_strike)
    258 
    259     def _small_repl(self, word):
    260         """Handle small."""
    261         if word.strip() == '~-' and self.is_small:
    262             return self.formatter.text(word)
    263         if word.strip() == '-~' and not self.is_small:
    264             return self.formatter.text(word)
    265         self.is_small = not self.is_small
    266         return self.formatter.small(self.is_small)
    267 
    268     def _big_repl(self, word):
    269         """Handle big."""
    270         if word.strip() == '~+' and self.is_big:
    271             return self.formatter.text(word)
    272         if word.strip() == '+~' and not self.is_big:
    273             return self.formatter.text(word)
    274         self.is_big = not self.is_big
    275         return self.formatter.big(self.is_big)
    276 
    277     def _emph_repl(self, word):
    278         """Handle emphasis, i.e. '' and '''."""
    279         ##print "#", self.is_b, self.is_em, "#"
    280         if len(word) == 3:
    281             self.is_b = not self.is_b
    282             if self.is_em and self.is_b:
    283                 self.is_b = 2
    284             return self.formatter.strong(self.is_b)
    285         else:
    286             self.is_em = not self.is_em
    287             if self.is_em and self.is_b:
    288                 self.is_em = 2
    289             return self.formatter.emphasis(self.is_em)
    290 
    291     def _emph_ibb_repl(self, word):
    292         """Handle mixed emphasis, i.e. ''''' followed by '''."""
    293         self.is_b = not self.is_b
    294         self.is_em = not self.is_em
    295         if self.is_em and self.is_b:
    296             self.is_b = 2
    297         return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
    298 
    299     def _emph_ibi_repl(self, word):
    300         """Handle mixed emphasis, i.e. ''''' followed by ''."""
    301         self.is_b = not self.is_b
    302         self.is_em = not self.is_em
    303         if self.is_em and self.is_b:
    304             self.is_em = 2
    305         return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
    306 
    307     def _emph_ib_or_bi_repl(self, word):
    308         """Handle mixed emphasis, exactly five '''''."""
    309         ##print "*", self.is_b, self.is_em, "*"
    310         b_before_em = self.is_b > self.is_em > 0
    311         self.is_b = not self.is_b
    312         self.is_em = not self.is_em
    313         if b_before_em:
    314             return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
    315         else:
    316             return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
    317 
    318 
    319     def _sup_repl(self, word):
    320         """Handle superscript."""
    321         return self.formatter.sup(1) + \
    322             self.formatter.text(word[1:-1]) + \
    323             self.formatter.sup(0)
    324 
    325     def _sub_repl(self, word):
    326         """Handle subscript."""
    327         return self.formatter.sub(1) + \
    328             self.formatter.text(word[2:-2]) + \
    329             self.formatter.sub(0)
    330 
    331 
    332     def _rule_repl(self, word):
    333         """Handle sequences of dashes."""
    334         result = self._undent() + self._closeP()
    335         if len(word) <= 4:
    336             result = result + self.formatter.rule()
    337         else:
    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     def _word_repl(self, word, text=None):
    345         """Handle WikiNames."""
    346 
    347         # check for parent links
    348         # !!! should use wikiutil.AbsPageName here, but setting `text`
    349         # correctly prevents us from doing this for now
    350         if word.startswith(wikiutil.PARENT_PREFIX):
    351             if not text:
    352                 text = word
    353             word = '/'.join(filter(None, self.formatter.page.page_name.split('/')[:-1] + [word[wikiutil.PARENT_PREFIX_LEN:]]))
    354 
    355         if not text:
    356             # if a simple, self-referencing link, emit it as plain text
    357             if word == self.formatter.page.page_name:
    358                 return self.formatter.text(word)
    359             text = word
    360         if word.startswith(wikiutil.CHILD_PREFIX):
    361             word = self.formatter.page.page_name + '/' + word[wikiutil.CHILD_PREFIX_LEN:]
    362 
    363         # handle anchors
    364         parts = word.split("#", 1)
    365         anchor = ""
    366         if len(parts)==2:
    367             word, anchor = parts
    368 
    369         return (self.formatter.pagelink(1, word, anchor=anchor) +
    370                 self.formatter.text(text) +
    371                 self.formatter.pagelink(0, word))
    372 
    373     def _notword_repl(self, word):
    374         """Handle !NotWikiNames."""
    375         return self.formatter.nowikiword(word[1:])
    376 
    377     def _interwiki_repl(self, word):
    378         """Handle InterWiki links."""
    379         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, word)
    380         if wikitag_bad:
    381             return self.formatter.text(word)
    382         else:
    383             return self.interwiki(["wiki:" + word])
    384 
    385 
    386     def _url_repl(self, word):
    387         """Handle literal URLs including inline images."""
    388         scheme = word.split(":", 1)[0]
    389 
    390         if scheme == "wiki":
    391             return self.interwiki([word])
    392         if scheme in self.attachment_schemas:
    393             return self.attachment([word])
    394 
    395         if wikiutil.isPicture(word):
    396             word = wikiutil.mapURL(self.request, word)
    397             # Get image name http://here.com/dir/image.gif -> image
    398             name = word.split('/')[-1]
    399             name = ''.join(name.split('.')[:-1])
    400             return self.formatter.image(src=word, alt=name)
    401         else:
    402             return (self.formatter.url(1, word, css=scheme) +
    403                     self.formatter.text(word) +
    404                     self.formatter.url(0))
    405 
    406 
    407     def _wikiname_bracket_repl(self, word):
    408         """Handle special-char wikinames."""
    409         wikiname = word[2:-2]
    410         if wikiname:
    411             return self._word_repl(wikiname)
    412         else:
    413             return self.formatter.text(word)
    414 
    415 
    416     def _url_bracket_repl(self, word):
    417         """Handle bracketed URLs."""
    418 
    419         # Local extended link?
    420         if word[1] == ':':
    421             words = word[2:-1].split(':', 1)
    422             if len(words) == 1:
    423                 words = words * 2
    424             words[0] = 'wiki:Self:%s' % words[0]
    425             return self.interwiki(words, pretty_url=1)
    426             #return self._word_repl(words[0], words[1])
    427 
    428         # Traditional split on space
    429         words = word[1:-1].split(None, 1)
    430         if len(words) == 1:
    431             words = words * 2
    432 
    433         if words[0][0] == '#':
    434             # anchor link
    435             return (self.formatter.url(1, words[0]) +
    436                     self.formatter.text(words[1]) +
    437                     self.formatter.url(0))
    438 
    439         scheme = words[0].split(":", 1)[0]
    440         if scheme == "wiki":
    441             return self.interwiki(words, pretty_url=1)
    442         if scheme in self.attachment_schemas:
    443             return self.attachment(words, pretty_url=1)
    444 
    445         if wikiutil.isPicture(words[1]) and re.match(self.url_rule, words[1]):
    446             return (self.formatter.url(1, words[0], css='external', do_escape=0) +
    447                     self.formatter.image(title=words[0], alt=words[0], src=words[1]) +
    448                     self.formatter.url(0))
    449         else:
    450             return (self.formatter.url(1, words[0], css=scheme, do_escape=0) +
    451                     self.formatter.text(words[1]) +
    452                     self.formatter.url(0))
    453 
    454 
    455     def _email_repl(self, word):
    456         """Handle email addresses (without a leading mailto:)."""
    457         return (self.formatter.url(1, "mailto:" + word, css='mailto') +
    458                 self.formatter.text(word) +
    459                 self.formatter.url(0))
    460 
    461 
    462     def _ent_repl(self, word):
    463         """Handle SGML entities."""
    464         return self.formatter.text(word)
    465         #return {'&': '&amp;',
    466         #        '<': '&lt;',
    467         #        '>': '&gt;'}[word]
    468 
    469     def _ent_numeric_repl(self, word):
    470         """Handle numeric (decimal and hexadecimal) SGML entities."""
    471         return self.formatter.rawHTML(word)
    472 
    473     def _ent_symbolic_repl(self, word):
    474         """Handle symbolic SGML entities."""
    475         return self.formatter.rawHTML(word)
    476    
    477     def _indent_repl(self, match):
    478         """Handle pure indentation (no - * 1. markup)."""
    479         result = []
    480         if not (self.in_li or self.in_dd):
    481             self._close_item(result)
    482             self.in_li = 1
    483             css_class = None
    484             if self.line_was_empty and not self.first_list_item:
    485                 css_class = 'gap'
    486             result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
    487         return ''.join(result)
    488 
    489     def _li_none_repl(self, match):
    490         """Handle type=none (" .") lists."""
    491         result = []
    492         self._close_item(result)
    493         self.in_li = 1
    494         css_class = None
    495         if self.line_was_empty and not self.first_list_item:
    496             css_class = 'gap'
    497         result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
    498         return ''.join(result)
    499 
    500     def _li_repl(self, match):
    501         """Handle bullet (" *") lists."""
    502         result = []
    503         self._close_item(result)
    504         self.in_li = 1
    505         css_class = None
    506         if self.line_was_empty and not self.first_list_item:
    507             css_class = 'gap'
    508         result.append(self.formatter.listitem(1, css_class=css_class))
    509         return ''.join(result)
    510 
    511     def _ol_repl(self, match):
    512         """Handle numbered lists."""
    513         return self._li_repl(match)
    514 
    515     def _dl_repl(self, match):
    516         """Handle definition lists."""
    517         result = []
    518         self._close_item(result)
    519         self.in_dd = 1
    520         result.extend([
    521             self.formatter.definition_term(1),
    522             self.formatter.text(match[1:-3].lstrip(' ')),
    523             self.formatter.definition_term(0),
    524             self.formatter.definition_desc(1),
    525         ])
    526         return ''.join(result)
    527 
    528 
    529     def _indent_level(self):
    530         """Return current char-wise indent level."""
    531         return len(self.list_indents) and self.list_indents[-1]
    532 
    533 
    534     def _indent_to(self, new_level, list_type, numtype, numstart):
    535         """Close and open lists."""
    536         open = []   # don't make one out of these two statements!
    537         close = []
    538 
    539         if self._indent_level() != new_level and self.in_table:
    540             close.append(self.formatter.table(0))
    541             self.in_table = 0
    542        
    543         while self._indent_level() > new_level:
    544             self._close_item(close)
    545             if self.list_types[-1] == 'ol':
    546                 tag = self.formatter.number_list(0)
    547             elif self.list_types[-1] == 'dl':
    548                 tag = self.formatter.definition_list(0)
    549             else:
    550                 tag = self.formatter.bullet_list(0)
    551             close.append(tag)
    552 
    553             del self.list_indents[-1]
    554             del self.list_types[-1]
    555            
    556             if self.list_types: # we are still in a list
    557                 if self.list_types[-1] == 'dl':
    558                     self.in_dd = 1
    559                 else:
    560                     self.in_li = 1
    561                
    562         # Open new list, if necessary
    563         if self._indent_level() < new_level:
    564             self.list_indents.append(new_level)
    565             self.list_types.append(list_type)
    566 
    567             if self.formatter.in_p:
    568                 close.append(self.formatter.paragraph(0))
    569            
    570             if list_type == 'ol':
    571                 tag = self.formatter.number_list(1, numtype, numstart)
    572             elif list_type == 'dl':
    573                 tag = self.formatter.definition_list(1)
    574             else:
    575                 tag = self.formatter.bullet_list(1)
    576             open.append(tag)
    577            
    578             self.first_list_item = 1
    579             self.in_li = 0
    580             self.in_dd = 0
    581            
    582         # If list level changes, close an open table
    583         if self.in_table and (open or close):
    584             close[0:0] = [self.formatter.table(0)]
    585             self.in_table = 0
    586        
    587         self.in_list = self.list_types != []
    588         return ''.join(close) + ''.join(open)
    589 
    590 
    591     def _undent(self):
    592         """Close all open lists."""
    593         result = []
    594         #result.append("<!-- _undent start -->\n")
    595         self._close_item(result)
    596         for type in self.list_types[::-1]:
    597             if type == 'ol':
    598                 result.append(self.formatter.number_list(0))
    599             elif type == 'dl':
    600                 result.append(self.formatter.definition_list(0))
    601             else:
    602                 result.append(self.formatter.bullet_list(0))
    603         #result.append("<!-- _undent end -->\n")
    604         self.list_indents = []
    605         self.list_types = []
    606         return ''.join(result)
    607 
    608 
    609     def _tt_repl(self, word):
    610         """Handle inline code."""
    611         return self.formatter.code(1) + \
    612             self.formatter.text(word[3:-3]) + \
    613             self.formatter.code(0)
    614 
    615 
    616     def _tt_bt_repl(self, word):
    617         """Handle backticked inline code."""
    618         # if len(word) == 2: return "" // removed for FCK editor
    619         return self.formatter.code(1, css="backtick") + \
    620             self.formatter.text(word[1:-1]) + \
    621             self.formatter.code(0)
    622 
    623 
    624     def _getTableAttrs(self, attrdef):
    625         # skip "|" and initial "<"
    626         while attrdef and attrdef[0] == "|":
    627             attrdef = attrdef[1:]
    628         if not attrdef or attrdef[0] != "<":
    629             return {}, ''
    630         attrdef = attrdef[1:]
    631 
    632         # extension for special table markup
    633         def table_extension(key, parser, attrs, wiki_parser=self):
    634             """ returns: tuple (found_flag, msg)
    635                 found_flag: whether we found something and were able to process it here
    636                   true for special stuff like 100% or - or #AABBCC
    637                   false for style xxx="yyy" attributes
    638                 msg: "" or an error msg
    639             """
    640             _ = wiki_parser._
    641             found = False
    642             msg = ''
    643             if key[0] in "0123456789":
    644                 token = parser.get_token()
    645                 if token != '%':
    646                     wanted = '%'
    647                     msg = _('Expected "%(wanted)s" after "%(key)s", got "%(token)s"') % {
    648                         'wanted': wanted, 'key': key, 'token': token}
    649                 else:
    650                     try:
    651                         dummy = int(key)
    652                     except ValueError:
    653                         msg = _('Expected an integer "%(key)s" before "%(token)s"') % {
    654                             'key': key, 'token': token}
    655                     else:
    656                         found = True
    657                         attrs['width'] = '"%s%%"' % key
    658             elif key == '-':
    659                 arg = parser.get_token()
    660                 try:
    661                     dummy = int(arg)
    662                 except ValueError:
    663                     msg = _('Expected an integer "%(arg)s" after "%(key)s"') % {
    664                         'arg': arg, 'key': key}
    665                 else:
    666                     found = True
    667                     attrs['colspan'] = '"%s"' % arg
    668             elif key == '|':
    669                 arg = parser.get_token()
    670                 try:
    671                     dummy = int(arg)
    672                 except ValueError:
    673                     msg = _('Expected an integer "%(arg)s" after "%(key)s"') % {
    674                         'arg': arg, 'key': key}
    675                 else:
    676                     found = True
    677                     attrs['rowspan'] = '"%s"' % arg
    678             elif key == '(':
    679                 found = True
    680                 attrs['align'] = '"left"'
    681             elif key == ':':
    682                 found = True
    683                 attrs['align'] = '"center"'
    684             elif key == ')':
    685                 found = True
    686                 attrs['align'] = '"right"'
    687             elif key == '^':
    688                 found = True
    689                 attrs['valign'] = '"top"'
    690             elif key == 'v':
    691                 found = True
    692                 attrs['valign'] = '"bottom"'
    693             elif key == '#':
    694                 arg = parser.get_token()
    695                 try:
    696                     if len(arg) != 6: raise ValueError
    697                     dummy = int(arg, 16)
    698                 except ValueError:
    699                     msg = _('Expected a color value "%(arg)s" after "%(key)s"') % {
    700                         'arg': arg, 'key': key}
    701                 else:
    702                     found = True
    703                     attrs['bgcolor'] = '"#%s"' % arg
    704             return found, self.formatter.rawHTML(msg)
    705 
    706         # scan attributes
    707         attr, msg = wikiutil.parseAttributes(self.request, attrdef, '>', table_extension)
    708         if msg:
    709             msg = '<strong class="highlight">%s</strong>' % msg
    710         #self.request.log("parseAttributes returned %r" % attr)
    711         return attr, msg
    712 
    713     def _tableZ_repl(self, word):
    714         """Handle table row end."""
    715         if self.in_table:
    716             result = ''
    717             # REMOVED: check for self.in_li, p should always close
    718             if self.formatter.in_p:
    719                 result = self.formatter.paragraph(0)
    720             result += self.formatter.table_cell(0) + self.formatter.table_row(0)
    721             return result
    722         else:
    723             return self.formatter.text(word)
    724 
    725     def _table_repl(self, word):
    726         """Handle table cell separator."""
    727         if self.in_table:
    728             result = []
    729             # check for attributes
    730             attrs, attrerr = self._getTableAttrs(word)
    731 
    732             # start the table row?
    733             if self.table_rowstart:
    734                 self.table_rowstart = 0
    735                 result.append(self.formatter.table_row(1, attrs))
    736             else:
    737                 # Close table cell, first closing open p
    738                 # REMOVED check for self.in_li, paragraph should close always!
    739                 if self.formatter.in_p:
    740                     result.append(self.formatter.paragraph(0))
    741                 result.append(self.formatter.table_cell(0))
    742 
    743             # check for adjacent cell markers
    744             if word.count("|") > 2:
    745                 if not attrs.has_key('align') and \
    746                    not (attrs.has_key('style') and 'text-align' in attrs['style'].lower()):
    747                     # add center alignment if we don't have some alignment already
    748                     attrs['align'] = '"center"'
    749                 if not attrs.has_key('colspan'):
    750                     attrs['colspan'] = '"%d"' % (word.count("|")/2)
    751 
    752             # return the complete cell markup
    753             result.append(self.formatter.table_cell(1, attrs) + attrerr)         
    754             result.append(self._line_anchordef())
    755             return ''.join(result)
    756         else:
    757             return self.formatter.text(word)
    758 
    759 
    760     def _heading_repl(self, word):
    761         """Handle section headings."""
    762         from hashlib import sha1
    763 
    764         h = word.strip()
    765         level = 1
    766         while h[level:level+1] == '=':
    767             level += 1
    768         depth = min(5,level)
    769 
    770         # this is needed for Included pages
    771         # TODO but it might still result in unpredictable results
    772         # when included the same page multiple times
    773         title_text = h[level:-level].strip()
    774         pntt = self.formatter.page.page_name + title_text
    775         self.titles.setdefault(pntt, 0)
    776         self.titles[pntt] += 1
    777 
    778         unique_id = ''
    779         if self.titles[pntt] > 1:
    780             unique_id = '-%d' % self.titles[pntt]
    781         result = self._closeP()
    782         result += self.formatter.heading(1, depth, id="head-"+sha1.new(pntt.encode(config.charset)).hexdigest()+unique_id)
    783 
    784         return (result + self.formatter.text(title_text) +
    785                 self.formatter.heading(0, depth))
    786    
    787     def _processor_repl(self, word):
    788         """Handle processed code displays."""
    789         if word[:3] == '{{{':
    790             word = word[3:]
    791 
    792         self.processor = None
    793         self.processor_name = None
    794         self.processor_is_parser = 0
    795         s_word = word.strip()
    796         if s_word == '#!':
    797             # empty bang paths lead to a normal code display
    798             # can be used to escape real, non-empty bang paths
    799             word = ''
    800             self.in_pre = 3
    801             return self._closeP() + self.formatter.preformatted(1)
    802         elif s_word[:2] == '#!':
    803             # First try to find a processor for this (will go away in 2.0)
    804             processor_name = s_word[2:].split()[0]
    805             self.setProcessor(processor_name)
    806 
    807         if self.processor:
    808             self.processor_name = processor_name
    809             self.in_pre = 2
    810             self.colorize_lines = [word]
    811             return ''
    812         elif s_word:
    813             self.in_pre = 3
    814             return self._closeP() + self.formatter.preformatted(1) + \
    815                    self.formatter.text(s_word + ' (-)')
    816         else:
    817             self.in_pre = 1
    818             return ''
    819 
    820     def _pre_repl(self, word):
    821         """Handle code displays."""
    822         word = word.strip()
    823         if word == '{{{' and not self.in_pre:
    824             self.in_pre = 3
    825             return self._closeP() + self.formatter.preformatted(self.in_pre)
    826         elif word == '}}}' and self.in_pre:
    827             self.in_pre = 0
    828             self.inhibit_p = 0
    829             return self.formatter.preformatted(self.in_pre)
    830         return self.formatter.text(word)
    831 
    832 
    833     def _smiley_repl(self, word):
    834         """Handle smileys."""
    835         return self.formatter.smiley(word)
    836 
    837     _smileyA_repl = _smiley_repl
    838 
    839 
    840     def _comment_repl(self, word):
    841         # if we are in a paragraph, we must close it so that normal text following
    842         # in the line below the comment will reopen a new paragraph.
    843         if self.formatter.in_p:
    844             self.formatter.paragraph(0)
    845         self.line_is_empty = 1 # markup following comment lines treats them as if they were empty
    846         return self.formatter.comment(word)
    847 
    848     def _closeP(self):
    849         if self.formatter.in_p:
    850             return self.formatter.paragraph(0)
    851         return ''
    852        
    853     def _macro_repl(self, word):
    854         """Handle macros ([[macroname]])."""
    855         macro_name = word[2:-2]
    856         self.inhibit_p = 0 # 1 fixes UserPreferences, 0 fixes paragraph formatting for macros
    857 
    858         # check for arguments
    859         args = None
    860         if macro_name.count("("):
    861             macro_name, args = macro_name.split('(', 1)
    862             args = args[:-1]
    863 
    864         # create macro instance
    865         if self.macro is None:
    866             self.macro = wikimacro.Macro(self)
    867         return self.formatter.macro(self.macro, macro_name, args)
    868 
    869     def scan(self, scan_re, line):
    870         """ Scans one line
    871        
    872         Append text before match, invoke replace() with match, and add text after match.
    873         """
    874         result = []
    875         lastpos = 0
    876 
    877         ###result.append(u'<span class="info">[scan: <tt>"%s"</tt>]</span>' % line)
    878      
    879         for match in scan_re.finditer(line):
    880             # Add text before the match
    881             if lastpos < match.start():
    882                
    883                 ###result.append(u'<span class="info">[add text before match: <tt>"%s"</tt>]</span>' % line[lastpos:match.start()])
    884                
    885                 if not (self.inhibit_p or self.in_pre or self.formatter.in_p):
    886                     result.append(self.formatter.paragraph(1, css_class="line862"))
    887                 result.append(self.formatter.text(line[lastpos:match.start()]))
    888            
    889             # Replace match with markup
    890             if not (self.inhibit_p or self.in_pre or self.formatter.in_p or
    891                     self.in_table or self.in_list):
    892                 result.append(self.formatter.paragraph(1, css_class="line867"))
    893             result.append(self.replace(match))
    894             lastpos = match.end()
    895        
    896         ###result.append('<span class="info">[no match, add rest: <tt>"%s"<tt>]</span>' % line[lastpos:])
    897        
    898         # Add paragraph with the remainder of the line
    899         if not (self.in_pre or self.in_li or self.in_dd or self.inhibit_p or
    900                 self.formatter.in_p) and lastpos < len(line):
    901             result.append(self.formatter.paragraph(1, css_class="line874"))
    902         result.append(self.formatter.text(line[lastpos:]))
    903         return u''.join(result)
    904 
    905     def replace(self, match):
    906         """ Replace match using type name """
    907         result = []
    908         for type, hit in match.groupdict().items():
    909             if hit is not None and type != "hmarker":
    910                
    911                 ###result.append(u'<span class="info">[replace: %s: "%s"]</span>' % (type, hit))
    912                 if self.in_pre and type not in ['pre', 'ent']:
    913                     return self.formatter.text(hit)
    914                 else:
    915                     # Open p for certain types
    916                     if not (self.inhibit_p or self.formatter.in_p
    917                             or self.in_pre or (type in self.no_new_p_before)):
    918                         result.append(self.formatter.paragraph(1, css_class="line891"))
    919                    
    920                     # Get replace method and replece hit
    921                     replace = getattr(self, '_' + type + '_repl')
    922                     result.append(replace(hit))
    923                     return ''.join(result)
    924         else:
    925             # We should never get here
    926             import pprint
    927             raise Exception("Can't handle match " + `match`
    928                 + "\n" + pprint.pformat(match.groupdict())
    929                 + "\n" + pprint.pformat(match.groups()) )
    930 
    931         return ""
    932 
    933     def _line_anchordef(self):
    934         if self.line_anchors and not self.line_anchor_printed:
    935             self.line_anchor_printed = 1
    936             return self.formatter.line_anchordef(self.lineno)
    937         else:
    938             return ''
    939 
    940     def format(self, formatter):
    941         """ For each line, scan through looking for magic
    942             strings, outputting verbatim any intervening text.
    943         """
    944         self.formatter = formatter
    945         self.hilite_re = self.formatter.page.hilite_re
    946 
    947         # prepare regex patterns
    948         rules = self.formatting_rules.replace('\n', '|')
    949         if self.cfg.bang_meta:
    950             rules = ur'(?P<notword>!%(word_rule)s)|%(rules)s' % {
    951                 'word_rule': self.word_rule,
    952                 'rules': rules,
    953             }
    954         self.request.clock.start('compile_huge_and_ugly')       
    955         scan_re = re.compile(rules, re.UNICODE)
    956         number_re = re.compile(self.ol_rule, re.UNICODE)
    957         term_re = re.compile(self.dl_rule, re.UNICODE)
    958         indent_re = re.compile("^\s*", re.UNICODE)
    959         eol_re = re.compile(r'\r?\n', re.UNICODE)
    960         self.request.clock.stop('compile_huge_and_ugly')       
    961 
    962         # get text and replace TABs
    963         rawtext = self.raw.expandtabs()
    964 
    965         # go through the lines
    966         self.lineno = self.start_line
    967         self.lines = eol_re.split(rawtext)
    968         self.line_is_empty = 0
    969 
    970         self.in_processing_instructions = 1
    971 
    972         # Main loop
    973         for line in self.lines:
    974             self.lineno += 1
    975             self.line_anchor_printed = 0
    976             if not self.in_table:
    977                 self.request.write(self._line_anchordef())
    978             self.table_rowstart = 1
    979             self.line_was_empty = self.line_is_empty
    980             self.line_is_empty = 0
    981             self.first_list_item = 0
    982             self.inhibit_p = 0
    983 
    984             # ignore processing instructions
    985             if self.in_processing_instructions:
    986                 found = False
    987                 for pi in ("##", "#format", "#refresh", "#redirect", "#deprecated",
    988                            "#pragma", "#form", "#acl", "#language"):
    989                     if line.lower().startswith(pi):
    990                         self.request.write(self.formatter.comment(line))
    991                         found = True
    992                         break
    993                 if not found:
    994                     self.in_processing_instructions = 0
    995                 else:
    996                     continue # do not parse this line
    997             if self.in_pre:
    998                 # TODO: move this into function
    999                 # still looking for processing instructions
    1000                 # TODO: use strings for pre state, not numbers
    1001                 if self.in_pre == 1:
    1002                     self.processor = None
    1003                     self.processor_is_parser = 0
    1004                     processor_name = ''
    1005                     if (line.strip()[:2] == "#!"):
    1006                         processor_name = line.strip()[2:].split()[0]
    1007                         self.setProcessor(processor_name)
    1008 
    1009                     if self.processor:
    1010                         self.in_pre = 2
    1011                         self.colorize_lines = [line]
    1012                         self.processor_name = processor_name
    1013                         continue
    1014                     else:
    1015                         self.request.write(self._closeP() +
    1016                                            self.formatter.preformatted(1))
    1017                         self.in_pre = 3
    1018                 if self.in_pre == 2:
    1019                     # processing mode
    1020                     endpos = line.find("}}}")
    1021                     if endpos == -1:
    1022                         self.colorize_lines.append(line)
    1023                         continue
    1024                     if line[:endpos]:
    1025                         self.colorize_lines.append(line[:endpos])
    1026                    
    1027                     # Close p before calling processor
    1028                     # TODO: do we really need this?
    1029                     self.request.write(self._closeP())
    1030                     res = self.formatter.processor(self.processor_name,
    1031                                                    self.colorize_lines,
    1032                                                    self.processor_is_parser)
    1033                     self.request.write(res)
    1034                     del self.colorize_lines
    1035                     self.in_pre = 0
    1036                     self.processor = None
    1037 
    1038                     # send rest of line through regex machinery
    1039                     line = line[endpos+3:]
    1040                     if not line.strip(): # just in the case "}}} " when we only have blanks left...
    1041                         continue
    1042             else:
    1043                 # we don't have \n as whitespace any more
    1044                 # This is the space between lines we join to one paragraph
    1045                 line += ' '
    1046                
    1047                 # Paragraph break on empty lines
    1048                 if not line.strip():
    1049                     if self.in_table:
    1050                         self.request.write(self.formatter.table(0))
    1051                         self.request.write(self._line_anchordef())
    1052                         self.in_table = 0
    1053                     # CHANGE: removed check for not self.list_types
    1054                     # p should close on every empty line
    1055                     if self.formatter.in_p:
    1056                         self.request.write(self.formatter.paragraph(0))
    1057                     self.line_is_empty = 1
    1058                     continue
    1059 
    1060                 # Check indent level
    1061                 indent = indent_re.match(line)
    1062                 indlen = len(indent.group(0))
    1063                 indtype = "ul"
    1064                 numtype = None
    1065                 numstart = None
    1066                 if indlen:
    1067                     match = number_re.match(line)
    1068                     if match:
    1069                         numtype, numstart = match.group(0).strip().split('.')
    1070                         numtype = numtype[0]
    1071 
    1072                         if numstart and numstart[0] == "#":
    1073                             numstart = int(numstart[1:])
    1074                         else:
    1075                             numstart = None
    1076 
    1077                         indtype = "ol"
    1078                     else:
    1079                         match = term_re.match(line)
    1080                         if match:
    1081                             indtype = "dl"
    1082 
    1083                 # output proper indentation tags
    1084                 self.request.write(self._indent_to(indlen, indtype, numtype, numstart))
    1085 
    1086                 # Table mode
    1087                 # TODO: move into function?               
    1088                 if (not self.in_table and line[indlen:indlen + 2] == "||"
    1089                     and line[-3:] == "|| " and len(line) >= 5 + indlen):
    1090                     # Start table
    1091                     if self.list_types and not self.in_li:
    1092                         self.request.write(self.formatter.listitem(1, style="list-style-type:none"))
    1093                         ## CHANGE: no automatic p on li
    1094                         ##self.request.write(self.formatter.paragraph(1))
    1095                         self.in_li = 1
    1096                        
    1097                     # CHANGE: removed check for self.in_li
    1098                     # paragraph should end before table, always!
    1099                     if self.formatter.in_p:
    1100                         self.request.write(self.formatter.paragraph(0))
    1101                     attrs, attrerr = self._getTableAttrs(line[indlen+2:])
    1102                     self.request.write(self.formatter.table(1, attrs) + attrerr)
    1103                     self.in_table = True # self.lineno
    1104                 elif (self.in_table and not
    1105                       # intra-table comments should not break a table
    1106                       (line[:2] == "##" or 
    1107                        line[indlen:indlen + 2] == "||" and
    1108                        line[-3:] == "|| " and
    1109                        len(line) >= 5 + indlen)):
    1110                    
    1111                     # Close table
    1112                     self.request.write(self.formatter.table(0))
    1113                     self.request.write(self._line_anchordef())
    1114                     self.in_table = 0
    1115                                            
    1116             # Scan line, format and write
    1117             formatted_line = self.scan(scan_re, line)
    1118             self.request.write(formatted_line)
    1119 
    1120             if self.in_pre == 3:
    1121                 self.request.write(self.formatter.linebreak())
    1122 
    1123         # Close code displays, paragraphs, tables and open lists
    1124         self.request.write(self._undent())
    1125         if self.in_pre: self.request.write(self.formatter.preformatted(0))
    1126         if self.formatter.in_p: self.request.write(self.formatter.paragraph(0))
    1127         if self.in_table: self.request.write(self.formatter.table(0))
    1128 
    1129     # --------------------------------------------------------------------
    1130     # Private helpers
    1131    
    1132     def setProcessor(self, name):
    1133         """ Set processer to either processor or parser named 'name' """
    1134         cfg = self.request.cfg
    1135         try:
    1136             self.processor = wikiutil.importPlugin(cfg, "processor", name,
    1137                                                    "process")
    1138             self.processor_is_parser = 0
    1139         except wikiutil.PluginMissingError:
    1140             try:
    1141                 self.processor = wikiutil.importPlugin(cfg, "parser", name,
    1142                                                    "Parser")
    1143                 self.processor_is_parser = 1
    1144             except wikiutil.PluginMissingError:
    1145                 self.processor = None
    1146 
    1147 
    1148 
    1149 class Request:
    1150     def __init__(self):
    1151         self._s = ''
    1152     def clock(self, *args, **kwds):
    1153         pass
    1154     def write(self, w):
    1155         print "writing ", w
    1156         self._s += w
    1157     def getText(self):
    1158         return ''
    1159 
    1160 class Page:
    1161     pass
    1162 
    1163 class Cfg:
    1164     pass
    1165 
    1166 
    1167 def wiki2html(s):
    1168     """
    1169     INPUT:
    1170          s -- a string formatted using wiki markup
    1171     OUTPUT:
    1172          string -- formatted as HTML
    1173     """
    1174     request = Request()
    1175     request.form = ''
    1176     request.rootpage = Page()
    1177     request.cfg = Cfg()
    1178     request.cfg.siteid = ''
    1179     request.cfg.data_underlay_dir = ''
    1180     request._page_headings = ''
    1181