Ticket #12713: trac_12713-sagenb.patch

File trac_12713-sagenb.patch, 44.4 KB (added by jhpalmieri, 2 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