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