Attachment 'formatter-patch-r4.diff'
Download 1 diff -urN moin-1.5.0/MoinMoin/formatter/base.py moin-1.5.0-formatter-patch/MoinMoin/formatter/base.py
2 --- moin-1.5.0/MoinMoin/formatter/base.py 2005-11-18 14:16:35.000000000 -0500
3 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/base.py 2006-01-19 14:52:29.000000000 -0500
4 @@ -135,23 +135,27 @@
5 def line_anchordef(self, lineno):
6 return ""
7
8 - def anchorlink(self, on, name='', id=None):
9 + def anchorlink(self, on, name='', **kw):
10 return ""
11
12 def line_anchorlink(self, on, lineno=0):
13 return ""
14
15 - def image(self, **kw):
16 - """ Take HTML <IMG> tag attributes in `attr`.
17 + def image(self, src=None, **kw):
18 + """An inline image.
19
20 - Attribute names have to be lowercase!
21 + Extra keyword arguments are according to the HTML <img> tag attributes.
22 + In particular an 'alt' or 'title' argument will should give a
23 + description of the image.
24 """
25 - attrstr = u''
26 - for attr, value in kw.items():
27 - if attr=='html_class':
28 - attr='class'
29 - attrstr = attrstr + u' %s="%s"' % (attr, wikiutil.escape(value))
30 - return u'<img%s>' % attrstr
31 + title = src
32 + for titleattr in ('title', 'html__title', 'alt', 'html__alt'):
33 + if kw.has_key(titleattr):
34 + title = kw[titleattr]
35 + break
36 + if title:
37 + return '[Image:%s]' % title
38 + return '[Image]'
39
40 def smiley(self, text):
41 return text
42 @@ -161,7 +165,7 @@
43
44 # Text and Text Attributes ###########################################
45
46 - def text(self, text):
47 + def text(self, text, **kw):
48 if not self._highlight_re:
49 return self._text(text)
50
51 @@ -185,37 +189,37 @@
52 def _text(self, text):
53 raise NotImplementedError
54
55 - def strong(self, on):
56 + def strong(self, on, **kw):
57 raise NotImplementedError
58
59 - def emphasis(self, on):
60 + def emphasis(self, on, **kw):
61 raise NotImplementedError
62
63 - def underline(self, on):
64 + def underline(self, on, **kw):
65 raise NotImplementedError
66
67 - def highlight(self, on):
68 + def highlight(self, on, **kw):
69 raise NotImplementedError
70
71 - def sup(self, on):
72 + def sup(self, on, **kw):
73 raise NotImplementedError
74
75 - def sub(self, on):
76 + def sub(self, on, **kw):
77 raise NotImplementedError
78
79 - def strike(self, on):
80 + def strike(self, on, **kw):
81 raise NotImplementedError
82
83 def code(self, on, **kw):
84 raise NotImplementedError
85
86 - def preformatted(self, on):
87 + def preformatted(self, on, **kw):
88 self.in_pre = on != 0
89
90 - def small(self, on):
91 + def small(self, on, **kw):
92 raise NotImplementedError
93
94 - def big(self, on):
95 + def big(self, on, **kw):
96 raise NotImplementedError
97
98 # special markup for syntax highlighting #############################
99 @@ -234,10 +238,10 @@
100 def linebreak(self, preformatted=1):
101 raise NotImplementedError
102
103 - def paragraph(self, on):
104 + def paragraph(self, on, **kw):
105 self.in_p = (on != 0)
106
107 - def rule(self, size=0):
108 + def rule(self, size=0, **kw):
109 raise NotImplementedError
110
111 def icon(self, type):
112 @@ -245,22 +249,22 @@
113
114 # Lists ##############################################################
115
116 - def number_list(self, on, type=None, start=None):
117 + def number_list(self, on, type=None, start=None, **kw):
118 raise NotImplementedError
119
120 - def bullet_list(self, on):
121 + def bullet_list(self, on, **kw):
122 raise NotImplementedError
123
124 def listitem(self, on, **kw):
125 raise NotImplementedError
126
127 - def definition_list(self, on):
128 + def definition_list(self, on, **kw):
129 raise NotImplementedError
130
131 - def definition_term(self, on, compact=0):
132 + def definition_term(self, on, compact=0, **kw):
133 raise NotImplementedError
134
135 - def definition_desc(self, on):
136 + def definition_desc(self, on, **kw):
137 raise NotImplementedError
138
139 def heading(self, on, depth, **kw):
140 @@ -268,13 +272,13 @@
141
142 # Tables #############################################################
143
144 - def table(self, on, attrs={}):
145 + def table(self, on, attrs={}, **kw):
146 raise NotImplementedError
147
148 - def table_row(self, on, attrs={}):
149 + def table_row(self, on, attrs={}, **kw):
150 raise NotImplementedError
151
152 - def table_cell(self, on, attrs={}):
153 + def table_cell(self, on, attrs={}, **kw):
154 raise NotImplementedError
155
156 # Dynamic stuff / Plugins ############################################
157 @@ -342,7 +346,7 @@
158
159 return self.text(f.getvalue())
160
161 - def escapedText(self, on):
162 + def escapedText(self, on, **kw):
163 """ This allows emitting text as-is, anything special will
164 be escaped (at least in HTML, some text output format
165 would possibly do nothing here)
166 diff -urN moin-1.5.0/MoinMoin/formatter/dom_xml.py moin-1.5.0-formatter-patch/MoinMoin/formatter/dom_xml.py
167 --- moin-1.5.0/MoinMoin/formatter/dom_xml.py 2005-11-18 14:16:35.000000000 -0500
168 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/dom_xml.py 2006-01-16 12:09:17.000000000 -0500
169 @@ -253,7 +253,7 @@
170 kw['type'] = 'inline'
171 return self._add_tag('attachment', **kw)
172
173 - def rule(self, size=0):
174 + def rule(self, size=0, **kw):
175 return self._add_tag('hr', size=str(size))
176
177 def icon(self, type):
178 @@ -262,41 +262,41 @@
179 def smiley(self, type):
180 return self._add_tag('smiley', type=type)
181
182 - def strong(self, on):
183 + def strong(self, on, **kw):
184 return self._set_tag('b', on)
185
186 - def emphasis(self, on):
187 + def emphasis(self, on, **kw):
188 return self._set_tag('em', on)
189
190 - def highlight(self, on):
191 + def highlight(self, on, **kw):
192 return self._set_tag('highlight', on)
193
194 - def number_list(self, on, type=None, start=None):
195 + def number_list(self, on, type=None, start=None, **kw):
196 return self._set_tag('ol', on, type=type, start=start)
197
198 - def bullet_list(self, on):
199 + def bullet_list(self, on, **kw):
200 return self._set_tag('ul', on)
201
202 def listitem(self, on, **kw):
203 return self._set_tag('li', on)
204
205 - def sup(self, on):
206 + def sup(self, on, **kw):
207 return self._set_tag('sup', on)
208
209 - def sub(self, on):
210 + def sub(self, on, **kw):
211 return self._set_tag('sub', on)
212
213 - def strike(self, on):
214 + def strike(self, on, **kw):
215 return self._set_tag('strike', on)
216
217 def code(self, on, **kw):
218 return self._set_tag('code', on)
219
220 - def preformatted(self, on):
221 + def preformatted(self, on, **kw):
222 self.in_pre = on != 0
223 return self._set_tag('pre', on)
224
225 - def paragraph(self, on):
226 + def paragraph(self, on, **kw):
227 FormatterBase.paragraph(self, on)
228 return self._set_tag('p', on)
229
230 @@ -315,31 +315,28 @@
231 result[str(name)] = value
232 return result
233
234 - def table(self, on, attrs={}):
235 + def table(self, on, attrs={}, **kw):
236 return self._set_tag('table', on, **self._check_attrs(attrs))
237
238 - def table_row(self, on, attrs={}):
239 + def table_row(self, on, attrs={}, **kw):
240 return self._set_tag('tr', on, **self._check_attrs(attrs))
241
242 - def table_cell(self, on, attrs={}):
243 + def table_cell(self, on, attrs={}, **kw):
244 return self._set_tag('td', on, **self._check_attrs(attrs))
245
246 def anchordef(self, name):
247 return self._add_tag('anchor', name=name)
248
249 - def anchorlink(self, on, name, id=None):
250 - kw = {}
251 - if id:
252 - kw['id'] = str(id)
253 + def anchorlink(self, on, name, **kw):
254 return self.url(on, "#" + name, **kw)
255
256 - def underline(self, on):
257 + def underline(self, on, **kw):
258 return self._set_tag('u', on)
259
260 - def definition_list(self, on):
261 + def definition_list(self, on, **kw):
262 return self._set_tag('dl', on)
263
264 - def definition_term(self, on, compact=0):
265 + def definition_term(self, on, compact=0, **kw):
266 # XXX may be not correct
267 # self._langAttr() missing
268 if compact and on:
269 @@ -347,24 +344,26 @@
270 else:
271 return self._set_tag('dt', on)
272
273 - def definition_desc(self, on):
274 + def definition_desc(self, on, **kw):
275 # self._langAttr() missing
276 return self._set_tag('dd', on)
277
278 - def image(self, **kw):
279 + def image(self, src=None, **kw):
280 """ Take HTML <IMG> tag attributes in `attr`.
281
282 Attribute names have to be lowercase!
283 """
284 + if src:
285 + kw['src']=src
286 return self._add_tag('img', **kw)
287
288 - def escapedText(self, text):
289 + def escapedText(self, text, **kw):
290 return wikiutil.escape(text)
291
292 - def small(self, on):
293 + def small(self, on, **kw):
294 return self._set_tag('small', on)
295
296 - def big(self, on):
297 + def big(self, on, **kw):
298 return self._set_tag('big', on)
299
300 def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
301 diff -urN moin-1.5.0/MoinMoin/formatter/text_gedit.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_gedit.py
302 --- moin-1.5.0/MoinMoin/formatter/text_gedit.py 2005-12-04 07:37:54.000000000 -0500
303 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_gedit.py 2006-01-16 12:06:31.000000000 -0500
304 @@ -140,13 +140,13 @@
305
306 # Change nesting: sub lists are no longer within the <li> tags
307
308 - def number_list(self, on, type=None, start=None):
309 + def number_list(self, on, type=None, start=None, **kw):
310 li = ""
311 if self._in_li: # close <li>
312 li = self.listitem(False)
313 return li + text_html.Formatter.number_list(self, on, type, start)
314
315 - def bullet_list(self, on):
316 + def bullet_list(self, on, **kw):
317 li = ""
318 if self._in_li: # close <li>
319 li = self.listitem(False)
320 @@ -197,7 +197,7 @@
321 attrs = text_html.Formatter._checkTableAttr(self, attrs, prefix)
322 return self._style_to_attributes(attrs)
323
324 - def table(self, on, attrs=None):
325 + def table(self, on, attrs=None, **kw):
326 """ Create table
327
328 @param on: start table
329 @@ -221,29 +221,31 @@
330
331 return ''.join(result)
332
333 - def comment(self, text):
334 + def comment(self, text, **kw):
335 text = text.rstrip() # workaround for growing amount of blanks at EOL
336 return self.preformatted(1, attr={'class': 'comment'}) + text + self.preformatted(0)
337
338 - def underline(self, on):
339 + def underline(self, on, **kw):
340 tag = 'u'
341 if on:
342 return self.open(tag)
343 return self.close(tag)
344
345 - def strong(self, on):
346 + def strong(self, on, **kw):
347 tag = 'b'
348 if on:
349 return self.open(tag)
350 return self.close(tag)
351
352 - def emphasis(self, on):
353 + def emphasis(self, on, **kw):
354 tag = 'i'
355 if on:
356 return self.open(tag)
357 return self.close(tag)
358
359 - def code(self, on, css=None):
360 + def code(self, on, css=None, **kw):
361 + if not css and kw.has_key('css_class'):
362 + css=kw['css_class']
363 tag = 'tt'
364 # Maybe we don't need this, because we have tt will be in inlineStack.
365 self._in_code = on
366 diff -urN moin-1.5.0/MoinMoin/formatter/text_html.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_html.py
367 --- moin-1.5.0/MoinMoin/formatter/text_html.py 2005-12-04 07:37:54.000000000 -0500
368 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_html.py 2006-01-19 13:42:06.000000000 -0500
369 @@ -11,12 +11,171 @@
370 from MoinMoin.Page import Page
371 from MoinMoin.action import AttachFile
372
373 +# TODO: when we require Python 2.3+ use the 'Set' type from the sets module.
374 +# TODO: when we require Python 2.4+ use the builtin 'set' type
375 +class Set:
376 + def __init__(self, *items):
377 + self._dict = {}
378 + if items:
379 + self._dict = dict( [(i,1) for i in list(items)] )
380 +
381 + def __contains__(self, item):
382 + if self._dict.has_key(item):
383 + return True
384 + return False
385 +
386 +# These are the HTML elements that we treat as block elements.
387 +_blocks = Set('dd', 'div', 'dl', 'dt', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
388 + 'hr', 'li', 'ol', 'p', 'pre', 'table', 'tbody', 'td', 'tfoot', 'th',
389 + 'thead', 'tr', 'ul')
390 +
391 +# These are the HTML elements which are typically only used with
392 +# an opening tag without a separate closing tag. We do not
393 +# include 'script' or 'style' because sometimes they do have
394 +# content, and also IE has a parsing bug with those two elements (only)
395 +# when they don't have a closing tag even if valid XHTML.
396 +
397 +_self_closing_tags = Set('area','base', 'br', 'col', 'frame', 'hr', 'img', 'input',
398 + 'isindex', 'link', 'meta', 'param')
399 +
400 +# These are the elements which generally should cause an increase in the
401 +# indention level in the html souce code.
402 +_indenting_tags = Set('ol', 'ul', 'dl', 'li', 'dt', 'dd', 'tr', 'td')
403 +
404 +# These are the elements that discard any whitespace they contain as
405 +# immediate child nodes.
406 +_space_eating_tags = Set('colgroup', 'dl', 'frameset', 'head', 'map' 'menu',
407 + 'ol', 'optgroup', 'select', 'table', 'tbody', 'tfoot',
408 + 'thead', 'tr', 'ul')
409 +
410 +# These are standard HTML attributes which are typically used without any
411 +# value; e.g., as boolean flags indicated by their presence.
412 +_html_attribute_boolflags = Set('compact', 'disabled', 'ismap', 'nohref',
413 + 'noresize', 'noshade', 'nowrap', 'readonly',
414 + 'selected', 'wrap')
415 +
416 +# These are all the standard HTML attributes that are allowed on any element.
417 +_common_attributes = Set('accesskey', 'class', 'dir', 'disabled', 'id', 'lang',
418 + 'style', 'tabindex', 'title')
419 +
420 +
421 +def rewrite_attribute_name(name, default_namespace='html'):
422 + """Takes an attribute name and tries to make it HTML correct.
423 +
424 + This function takes an attribute name as a string, as it may be
425 + passed in to a formatting method using a keyword-argument syntax,
426 + and tries to convert it into a real attribute name. This is
427 + necessary because some attributes may conflict with Python
428 + reserved words or variable syntax (such as 'for', 'class', or
429 + 'z-index'); and also to help with backwards compatibility with
430 + older versions of MoinMoin where different names may have been
431 + used (such as 'content_id' or 'css').
432 +
433 + Returns a tuple of strings: (namespace, attribute).
434 +
435 + Namespaces: The default namespace is always assumed to be 'html',
436 + unless the input string contains a colon or a double-underscore;
437 + in which case the first such occurance is assumed to separate the
438 + namespace prefix from name. So, for example, to get the HTML
439 + attribute 'for' (as on a <label> element), you can pass in the
440 + string 'html__for' or even '__for'.
441 +
442 + Hyphens: To better support hyphens (which are not allowed in Python
443 + variable names), all occurances of two underscores will be replaced
444 + with a hyphen. If you use this, then you must also provide a
445 + namespace since the first occurance of '__' separates a namespace
446 + from the name.
447 +
448 + Special cases: Within the 'html' namespace, mainly to preserve
449 + backwards compatibility, these exceptions ars recognized:
450 + 'content_type', 'content_id', 'css_class', and 'css'.
451 + Additionally all html attributes starting with 'on' are forced to
452 + lower-case. Also the string 'xmlns' is recognized as having
453 + no namespace.
454 +
455 + Examples:
456 + 'id' -> ('html', 'id')
457 + 'css_class' -> ('html', 'class')
458 + 'content_id' -> ('html', 'id')
459 + 'content_type' -> ('html', 'type')
460 + 'html__for' -> ('html', 'for)
461 + 'xml__space' -> ('xml', 'space')
462 + '__z__index' -> ('html', 'z-index')
463 + '__http__equiv' -> ('html', 'http-equiv')
464 + 'onChange' -> ('html', 'onchange')
465 + 'xmlns' -> ('', 'xmlns')
466 + 'xmlns__abc' -> ('xmlns', 'abc')
467 +
468 + (In actuality we only deal with namespace prefixes, not any real
469 + namespace URI...we only care about the syntax not the meanings.)
470 + """
471 +
472 + # Handle any namespaces (just in case someday we support XHTML)
473 + if ':' in name:
474 + ns, name = name.split(':',1)
475 + elif '__' in name:
476 + ns, name = name.split('__',1)
477 + elif name == 'xmlns':
478 + ns = ''
479 + else:
480 + ns = default_namespace
481 +
482 + name.replace('__', '-')
483 + if ns == 'html':
484 + # We have an HTML attribute, fix according to DTD
485 + if name == 'content_type': # MIME type such as in <a> and <link> elements
486 + name = 'type'
487 + elif name == 'content_id': # moin historical convention
488 + name = 'id'
489 + elif name in ('css_class', 'css'): # to avoid python word 'class'
490 + name = 'class'
491 + elif name.startswith('on'): # event handler hook
492 + name = name.lower()
493 + return ns, name
494 +
495 +
496 +def extend_attribute_dictionary(attributedict, ns, name, value):
497 + """Add a new attribute to an attribute dictionary, merging values where possible.
498 +
499 + The attributedict must be a dictionary with tuple-keys of the form:
500 + (namespace, attrname).
501 +
502 + The given ns, name, and value will be added to the dictionary. It
503 + will replace the old value if it existed, except for a few special
504 + cases where the values are logically merged instead (CSS class
505 + names and style rules).
506 +
507 + As a special case, if value is None (not just ''), then the
508 + attribute is actually deleted from the dictionary.
509 + """
510 +
511 + key = ns, name
512 + if value is None:
513 + if attributedict.has_key(key):
514 + del attributedict[key]
515 + else:
516 + if ns == 'html' and attributedict.has_key(key):
517 + if name == 'class':
518 + # CSS classes are appended by space-separated list
519 + value = attributedict[key] + ' ' + value
520 + elif name == 'style':
521 + # CSS styles are appended by semicolon-separated rules list
522 + value = attributedict[key] + '; ' + value
523 + elif name in _html_attribute_boolflags:
524 + # All attributes must have a value. According to XHTML those
525 + # traditionally used as flags should have their value set to
526 + # the same as the attribute name.
527 + value = name
528 + attributedict[key] = value
529 +
530 +
531 class Formatter(FormatterBase):
532 """
533 Send HTML data.
534 """
535
536 hardspace = ' '
537 + indentspace = ' '
538
539 def __init__(self, request, **kw):
540 apply(FormatterBase.__init__, (self, request), kw)
541 @@ -26,8 +185,12 @@
542 # the stack are closed.
543 self._inlineStack = []
544
545 - self._in_li = 0
546 - self._in_code = 0
547 + # stack of all tags
548 + self._tag_stack = []
549 + self._indent_level = 0
550 +
551 + self._in_li = 0 # not useful here, but used by text_gedit
552 + self._in_code = 0 # used by text_gedit
553 self._in_code_area = 0
554 self._in_code_line = 0
555 self._code_area_num = 0
556 @@ -67,36 +230,115 @@
557 # lang is inherited from content div
558 return {}
559
560 - attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
561 + attr = {'xml:lang': lang, 'lang': lang, 'dir': i18n.getDirection(lang),}
562 return attr
563
564 - def formatAttributes(self, attr=None):
565 - """ Return formatted attributes string
566 + def formatAttributes(self, attr=None, allowed_attrs=None, **kw):
567 + """ Return HTML attributes formatted as a single string.
568
569 @param attr: dict containing keys and values
570 - @rtype: string ?
571 + @param allowed_attrs: A list of allowable attribute names
572 + @param **kw: other arbitrary attributes expressed as keyword arguments.
573 + @rtype: string
574 @return: formated attributes or empty string
575 +
576 + The attributes and their values can either be given in the
577 + 'attr' dictionary, or as extra keyword arguments. They are
578 + both merged together. See the function
579 + rewrite_attribute_name() for special notes on how to name
580 + attributes.
581 +
582 + Setting a value to None rather than a string (or string
583 + coercible) will remove that attribute from the list.
584 +
585 + If the list of allowed_attrs is provided, then an error is
586 + raised if an HTML attribute is encountered that is not in that
587 + list (or is not a common attribute which is always allowed or
588 + is not in another XML namespace using the double-underscore
589 + syntax).
590 """
591 +
592 + # Merge the attr dict and kw dict into a single attributes
593 + # dictionary (rewriting any attribute names, extracting
594 + # namespaces, and merging some values like css classes).
595 + attributes = {} # dict of key=(namespace,name): value=attribute_value
596 if attr:
597 - attr = [' %s="%s"' % (k, v) for k, v in attr.items()]
598 - return ''.join(attr)
599 + for a, v in attr.items():
600 + a_ns, a_name = rewrite_attribute_name(a)
601 + extend_attribute_dictionary(attributes, a_ns, a_name, v)
602 + if kw:
603 + for a, v in kw.items():
604 + a_ns, a_name = rewrite_attribute_name(a)
605 + extend_attribute_dictionary(attributes, a_ns, a_name, v)
606 +
607 + # Add title attribute if missing, but it has an alt.
608 + if attributes.has_key(('html','alt')) and not attributes.has_key(('html','title')):
609 + attributes[('html','title')] = attributes[('html','alt')]
610 +
611 + # Force both lang and xml:lang to be present and identical if
612 + # either exists. The lang takes precedence over xml:lang if
613 + # both exist.
614 + if attributes.has_key(('html','lang')):
615 + attributes[('xml','lang')] = attributes[('html','lang')]
616 + elif attributes.has_key(('xml','lang')):
617 + attributes[('html','lang')] = attributes[('xml','lang')]
618 +
619 + # Check all the HTML attributes to see if they are known and
620 + # allowed. Ignore attributes if in non-HTML namespaces.
621 + if allowed_attrs:
622 + for name in [key[1] for key in attributes.keys() if key[0] == 'html']:
623 + if name in _common_attributes or name in allowed_attrs:
624 + pass
625 + elif name.startswith('on'):
626 + pass # Too many event handlers to enumerate, just let them all pass.
627 + else:
628 + # Unknown or unallowed attribute.
629 + err = 'Illegal HTML attribute "%s" passed to formatter' % name
630 + raise ValueError(err)
631 +
632 + # Finally, format them all as a single string.
633 + if attributes:
634 + # Construct a formatted string containing all attributes
635 + # with their values escaped. Any html:* namespace
636 + # attributes drop the namespace prefix. We build this by
637 + # separating the attributes into three categories:
638 + #
639 + # * Those without any namespace (should only be xmlns attributes)
640 + # * Those in the HTML namespace (we drop the html: prefix for these)
641 + # * Those in any other non-HTML namespace, including xml:
642 +
643 + xmlnslist = ['%s="%s"' % (k[1], wikiutil.escape(v,1))
644 + for k, v in attributes.items() if not k[0]]
645 + htmllist = ['%s="%s"' % (k[1], wikiutil.escape(v,1))
646 + for k, v in attributes.items() if k[0] == 'html']
647 + otherlist = ['%s:%s="%s"' % (k[0], k[1], wikiutil.escape(v,1))
648 + for k, v in attributes.items() if k[0] and k[0] != 'html']
649 +
650 + # Join all these lists together in a space-separated string. Also
651 + # prefix the whole thing with a space too.
652 + htmllist.sort()
653 + otherlist.sort()
654 + all = [''] + xmlnslist + htmllist + otherlist
655 + return ' '.join(all)
656 return ''
657
658 - # TODO: use set when we require Python 2.3
659 - # TODO: The list is not complete, add missing from dtd
660 - _blocks = 'p div pre table tr td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
661 - _blocks = dict(zip(_blocks.split(), [1] * len(_blocks)))
662 -
663 - def open(self, tag, newline=False, attr=None):
664 + def open(self, tag, newline=False, attr=None, allowed_attrs=None, **kw):
665 """ Open a tag with optional attributes
666
667 @param tag: html tag, string
668 - @param newline: render tag on a separate line
669 - @parm attr: dict with tag attributes
670 + @param newline: render tag so following data is on a separate line
671 + @param attr: dict with tag attributes
672 + @param allowed_attrs: list of allowed attributes for this element
673 + @param kw: arbitrary attributes and values
674 @rtype: string ?
675 - @return: open tag with attributes
676 + @return: open tag with attributes as a string
677 """
678 - if tag in self._blocks:
679 + is_self_closing = ''
680 + if tag in _self_closing_tags:
681 + # Don't expect a closing tag later on.
682 + is_self_closing = ' /'
683 +
684 + if tag in _blocks:
685 # Block elements
686 result = []
687
688 @@ -106,26 +348,39 @@
689 attributes.update(attr)
690
691 # Format
692 - attributes = self.formatAttributes(attributes)
693 - result.append('<%s%s>' % (tag, attributes))
694 + attributes = self.formatAttributes(attributes, allowed_attrs=allowed_attrs, **kw)
695 + result.append('<%s%s%s>' % (tag, attributes, is_self_closing))
696 if newline:
697 - result.append('\n')
698 - return ''.join(result)
699 + result.append(self._newline())
700 + tagstr = ''.join(result)
701 else:
702 # Inline elements
703 # Add to inlineStack
704 - self._inlineStack.append(tag)
705 + if not is_self_closing:
706 + # Only push on stack if we expect a close-tag later
707 + self._inlineStack.append(tag)
708 # Format
709 - return '<%s%s>' % (tag, self.formatAttributes(attr))
710 -
711 + tagstr = '<%s%s%s>' % (tag,
712 + self.formatAttributes(attr, allowed_attrs, **kw),
713 + is_self_closing)
714 + if not self.close:
715 + self._tag_stack.append(tag)
716 + if tag in _indenting_tags:
717 + self._indent_level = self._indent_level + 1
718 + return tagstr
719 +
720 def close(self, tag, newline=False):
721 """ Close tag
722
723 @param tag: html tag, string
724 - @rtype: string ?
725 - @return: closing tag
726 + @param newline: render tag so following data is on a separate line
727 + @rtype: string
728 + @return: closing tag as a string
729 """
730 - if tag in self._blocks:
731 + if tag in _self_closing_tags:
732 + # This tag was already closed
733 + tagstr = ''
734 + elif tag in _blocks:
735 # Block elements
736 # Close all tags in inline stack
737 # Work on a copy, because close(inline) manipulate the stack
738 @@ -136,25 +391,38 @@
739 result.append(self.close(inline))
740 # Format with newline
741 if newline:
742 - result.append('\n')
743 - result.append('</%s>\n' % (tag))
744 - return ''.join(result)
745 + result.append(self._newline())
746 + result.append('</%s>' % (tag))
747 + tagstr = ''.join(result)
748 else:
749 # Inline elements
750 # Pull from stack, ignore order, that is not our problem.
751 # The code that calls us should keep correct calling order.
752 if tag in self._inlineStack:
753 self._inlineStack.remove(tag)
754 - return '</%s>' % tag
755 + tagstr = '</%s>' % tag
756
757 + if tag in _self_closing_tags:
758 + self._tag_stack.pop()
759 + if tag in _indenting_tags:
760 + # decrease indent level
761 + self._indent_level = self._indent_level - 1
762 + if newline:
763 + tagstr += self._newline()
764 + return tagstr
765
766 # Public methods ###################################################
767
768 - def startContent(self, content_id='content', **kwargs):
769 - """ Start page content div """
770 + def startContent(self, content_id='content', newline=True, **kw):
771 + """ Start page content div.
772 +
773 + A link anchor is provided at the beginning of the div, with
774 + an id of 'top' or 'top_xxxx' if the content_id argument is
775 + set to 'xxxx'.
776 + """
777
778 # Setup id
779 - if content_id!='content':
780 + if content_id != 'content':
781 aid = 'top_%s' % (content_id,)
782 else:
783 aid = 'top'
784 @@ -163,12 +431,20 @@
785 # Use the content language
786 attr = self.langAttr(self.request.content_lang)
787 attr['id'] = content_id
788 - result.append(self.open('div', newline=1, attr=attr))
789 + result.append(self.open('div', newline=False, attr=attr,
790 + allowed_attrs=['align'], **kw))
791 result.append(self.anchordef(aid))
792 + if newline:
793 + result.append('\n')
794 return ''.join(result)
795
796 - def endContent(self):
797 - """ Close page content div """
798 + def endContent(self,newline=True):
799 + """ Close page content div.
800 +
801 + A link anchor is provided at the end of the div, with
802 + an id of 'bottom' or 'bottom_xxxx' if the content_id argument
803 + to the previus startContent() call was set to 'xxxx'.
804 + """
805
806 # Setup id
807 try:
808 @@ -182,7 +458,7 @@
809
810 result = []
811 result.append(self.anchordef(aid))
812 - result.append(self.close('div', newline=1))
813 + result.append(self.close('div', newline=newline))
814 return ''.join(result)
815
816 def lang(self, on, lang_name):
817 @@ -205,7 +481,7 @@
818 def sysmsg(self, on, **kw):
819 tag = 'div'
820 if on:
821 - return self.open(tag, attr={'class': 'message'})
822 + return self.open(tag, attr={'class': 'message'}, **kw)
823 return self.close(tag)
824
825 # Links ##############################################################
826 @@ -237,7 +513,7 @@
827 @keyword title: override using the interwiki wikiname as title
828 """
829 if not on:
830 - return '</a>'
831 + return self.url(0)
832 wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
833 wikiurl = wikiutil.mapURL(self.request, wikiurl)
834 if wikitag == 'Self': # for own wiki, do simple links
835 @@ -259,41 +535,96 @@
836 # unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
837
838 def url(self, on, url=None, css=None, **kw):
839 - """
840 + """ Inserts an <a> element.
841 +
842 + Call once with on=1 to start the link, and again with on=0
843 + to end it (no other arguments are needed when on==0).
844 +
845 Keyword params:
846 - title - <a> title attribute
847 - attrs - just include those <a> attrs "as is"
848 + url - the URL to link to; will go through Wiki URL mapping.
849 + css - a space-separated list of CSS classes
850 + attrs - just include this string verbatim inside
851 + the <a> element; can be used for arbitrary attrs;
852 + all escaping and quoting is the caller's responsibility.
853 +
854 + Note that the 'attrs' keyword argument is for backwards compatibility
855 + only. It should not be used for new code--instead just pass
856 + any attributes in as separate keyword arguments.
857 """
858 + if not on:
859 + return self.close('a')
860 + attrs = self.langAttr()
861 +
862 + # Handle the URL mapping
863 + if url is None and kw.has_key('href'):
864 + url = kw['href']
865 + del kw['href']
866 if url is not None:
867 url = wikiutil.mapURL(self.request, url)
868 - title = kw.get('title', None)
869 - attrs = kw.get('attrs', None)
870 + attrs['href'] = url
871 +
872 + if css:
873 + attrs['class'] = css
874 +
875 + if kw.has_key('attrs'):
876 + # for backwards compatibility, raw pre-formated attribute string
877 + extra_attrs = kw['attrs']
878 + del kw['attrs']
879 + else:
880 + extra_attrs = None
881 +
882 + # create link
883 if on:
884 - str = '<a'
885 - if css:
886 - str = '%s class="%s"' % (str, css)
887 - if title:
888 - str = '%s title="%s"' % (str, title)
889 - if attrs:
890 - str = '%s %s' % (str, attrs)
891 - str = '%s href="%s">' % (str, wikiutil.escape(url, 1))
892 + str = self.open('a', attr=attrs, **kw)
893 + if extra_attrs:
894 + # insert this into the tag verbatim (messy)
895 + if str[-2:] == '/>':
896 + str = '%s %s />' % (str[:-2], extra_attrs)
897 + else:
898 + str = '%s %s>' % (str[:-1], extra_attrs)
899 else:
900 - str = '</a>'
901 + str = self.close('a')
902 return str
903
904 def anchordef(self, id):
905 - #return '<a id="%s"></a>' % (id, ) # this breaks PRE sections for IE
906 - # do not add a \n here, it breaks pre sections with line_anchordef
907 - return '<span id="%s" class="anchor"></span>' % (id, )
908 + """Inserts an invisible element used as a link target.
909 +
910 + Inserts an empty <span> element with an id attribute, used as an anchor
911 + for link references. We use <span></span> rather than <span/>
912 + for browser portability.
913 + """
914 + # Don't add newlines, \n, as it will break pre and
915 + # line-numbered code sections (from line_achordef() method).
916 + return '<span class="anchor" id="%s"></span>' % wikiutil.escape(id, 1)
917
918 def line_anchordef(self, lineno):
919 return self.anchordef("line-%d" % lineno)
920
921 - def anchorlink(self, on, name='', id=None):
922 - extra = ''
923 - if id:
924 - extra = ' id="%s"' % id
925 - return ['<a href="#%s"%s>' % (name, extra), '</a>'][not on]
926 + def anchorlink(self, on, name='', **kw):
927 + """Insert an <a> link pointing to an anchor on the same page.
928 +
929 + Call once with on=1 to start the link, and a second time with
930 + on=0 to end it. No other arguments are needed on the second
931 + call.
932 +
933 + The name argument should be the same as the id provided to the
934 + anchordef() method, or some other elment. It should NOT start
935 + with '#' as that will be added automatically.
936 +
937 + The id argument, if provided, is instead the id of this link
938 + itself and not of the target element the link references.
939 + """
940 +
941 + attrs = self.langAttr()
942 + if name:
943 + attrs['href'] = '#%s' % name
944 + if kw.has_key('href'):
945 + del kw['href']
946 + if on:
947 + str = self.open('a', attr=attrs, **kw)
948 + else:
949 + str = self.close('a')
950 + return str
951
952 def line_anchorlink(self, on, lineno=0):
953 return self.anchorlink(on, name="line-%d" % lineno)
954 @@ -415,81 +746,143 @@
955
956 # Inline ###########################################################
957
958 - def strong(self, on):
959 + def strong(self, on, **kw):
960 + """Creates an HTML <strong> element.
961 +
962 + Call once with on=1 to start the region, and a second time
963 + with on=0 to end it.
964 + """
965 +
966 tag = 'strong'
967 if on:
968 - return self.open(tag)
969 + return self.open(tag, allowed_attrs=[], **kw)
970 return self.close(tag)
971
972 - def emphasis(self, on):
973 + def emphasis(self, on, **kw):
974 + """Creates an HTML <em> element.
975 +
976 + Call once with on=1 to start the region, and a second time
977 + with on=0 to end it.
978 + """
979 +
980 tag = 'em'
981 if on:
982 - return self.open(tag)
983 + return self.open(tag, allowed_attrs=[], **kw)
984 return self.close(tag)
985
986 - def underline(self, on):
987 + def underline(self, on, **kw):
988 + """Creates a text span for underlining (css class "u").
989 +
990 + Call once with on=1 to start the region, and a second time
991 + with on=0 to end it.
992 + """
993 tag = 'span'
994 if on:
995 - return self.open(tag, attr={'class': 'u'})
996 + return self.open(tag, attr={'class': 'u'}, allowed_attrs=[], **kw)
997 return self.close(tag)
998
999 - def highlight(self, on):
1000 + def highlight(self, on, **kw):
1001 + """Creates a text span for highlighting (css class "highlight").
1002 +
1003 + Call once with on=1 to start the region, and a second time
1004 + with on=0 to end it.
1005 + """
1006 tag = 'strong'
1007 if on:
1008 - return self.open(tag, attr={'class': 'highlight'})
1009 + return self.open(tag, attr={'class': 'highlight'}, allowed_attrs=[], **kw)
1010 return self.close(tag)
1011
1012 - def sup(self, on):
1013 + def sup(self, on, **kw):
1014 + """Creates a <sup> element for superscript text.
1015 +
1016 + Call once with on=1 to start the region, and a second time
1017 + with on=0 to end it.
1018 + """
1019 tag = 'sup'
1020 if on:
1021 - return self.open(tag)
1022 + return self.open(tag, allowed_attrs=[], **kw)
1023 return self.close(tag)
1024
1025 - def sub(self, on):
1026 + def sub(self, on, **kw):
1027 + """Creates a <sub> element for subscript text.
1028 +
1029 + Call once with on=1 to start the region, and a second time
1030 + with on=0 to end it.
1031 + """
1032 tag = 'sub'
1033 if on:
1034 - return self.open(tag)
1035 + return self.open(tag, allowed_attrs=[], **kw)
1036 return self.close(tag)
1037
1038 - def strike(self, on):
1039 - tag = 'strike'
1040 + def strike(self, on, **kw):
1041 + """Creates a text span for line-through (strikeout) text (css class 'strike').
1042 +
1043 + Call once with on=1 to start the region, and a second time
1044 + with on=0 to end it.
1045 + """
1046 + # This does not use <strike> because has been deprecated in standard HTML.
1047 + tag = 'span'
1048 if on:
1049 - return self.open(tag)
1050 + return self.open(tag, attr={'class': 'strike'},
1051 + allowed_attrs=[], **kw)
1052 return self.close(tag)
1053
1054 def code(self, on, **kw):
1055 + """Creates a <tt> element for inline code or monospaced text.
1056 +
1057 + Call once with on=1 to start the region, and a second time
1058 + with on=0 to end it.
1059 +
1060 + Any text within this section will have spaces converted to
1061 + non-break spaces.
1062 + """
1063 tag = 'tt'
1064 # Maybe we don't need this, because we have tt will be in inlineStack.
1065 self._in_code = on
1066 if on:
1067 - return self.open(tag)
1068 + return self.open(tag, allowed_attrs=[], **kw)
1069 return self.close(tag)
1070
1071 - def small(self, on):
1072 + def small(self, on, **kw):
1073 + """Creates a <small> element for smaller font.
1074 +
1075 + Call once with on=1 to start the region, and a second time
1076 + with on=0 to end it.
1077 + """
1078 tag = 'small'
1079 if on:
1080 - return self.open(tag)
1081 + return self.open(tag, allowed_attrs=[], **kw)
1082 return self.close(tag)
1083
1084 - def big(self, on):
1085 + def big(self, on, **kw):
1086 + """Creates a <big> element for larger font.
1087 +
1088 + Call once with on=1 to start the region, and a second time
1089 + with on=0 to end it.
1090 + """
1091 tag = 'big'
1092 if on:
1093 - return self.open(tag)
1094 + return self.open(tag, allowed_attrs=[], **kw)
1095 return self.close(tag)
1096
1097
1098 # Block elements ####################################################
1099
1100 - def preformatted(self, on, attr=None):
1101 + def preformatted(self, on, **kw):
1102 + """Creates a preformatted text region, with a <pre> element.
1103 +
1104 + Call once with on=1 to start the region, and a second time
1105 + with on=0 to end it.
1106 + """
1107 FormatterBase.preformatted(self, on)
1108 tag = 'pre'
1109 if on:
1110 - return self.open(tag, newline=1, attr=attr)
1111 + return self.open(tag, newline=1, **kw)
1112 return self.close(tag)
1113
1114 # Use by code area
1115 _toggleLineNumbersScript = """
1116 -<script type="text/JavaScript">
1117 +<script type="text/javascript">
1118 function isnumbered(obj) {
1119 return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber';
1120 }
1121 @@ -541,6 +934,19 @@
1122 """
1123
1124 def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
1125 + """Creates a formatted code region, with line numbering.
1126 +
1127 + This region is formatted as a <div> with a <pre> inside it. The
1128 + code_id argument is assigned to the 'id' of the div element, and
1129 + must be unique within the document. The show, start, and step are
1130 + used for line numbering.
1131 +
1132 + Note this is not like most formatter methods, it can not take any
1133 + extra keyword arguments.
1134 +
1135 + Call once with on=1 to start the region, and a second time
1136 + with on=0 to end it.
1137 + """
1138 res = []
1139 ci = self.request.makeUniqueID('CA-%s_%03d' % (code_id, self._code_area_num))
1140 if on:
1141 @@ -562,7 +968,7 @@
1142 if self._code_area_state[1] >= 0:
1143 toggleLineNumbersLink = r'''
1144 <script type="text/javascript">
1145 -document.write('<a href="#" onClick="return togglenumber(\'%s\', %d, %d);" \
1146 +document.write('<a href="#" onclick="return togglenumber(\'%s\', %d, %d);" \
1147 class="codenumbers">Toggle line numbers<\/a>');
1148 </script>
1149 ''' % (self._code_area_state[0], self._code_area_state[2], self._code_area_state[3])
1150 @@ -602,12 +1008,32 @@
1151
1152 # Paragraphs, Lines, Rules ###########################################
1153
1154 + def _indent_spaces(self):
1155 + """Returns space(s) for indenting the html source so list nesting is easy to read.
1156 +
1157 + Note that this mostly works, but because of caching may not always be accurate."""
1158 + return self.indentspace * self._indent_level
1159 +
1160 + def _newline(self):
1161 + """Returns the whitespace for starting a new html source line, properly indented."""
1162 + return '\n' + self._indent_spaces()
1163 +
1164 def linebreak(self, preformatted=1):
1165 + """Creates a line break in the HTML output.
1166 +
1167 + If preformatted is true a <br> element is inserted, otherwise
1168 + the linebreak will only be visible in the HTML source.
1169 + """
1170 if self._in_code_area:
1171 preformatted = 1
1172 - return ['\n', '<br>\n'][not preformatted]
1173 + return ['\n', '<br />\n'][not preformatted] + self._indent_spaces()
1174 +
1175 + def paragraph(self, on, **kw):
1176 + """Creates a paragraph with a <p> element.
1177
1178 - def paragraph(self, on):
1179 + Call once with on=1 to start the region, and a second time
1180 + with on=0 to end it.
1181 + """
1182 if self._terse:
1183 return ''
1184 FormatterBase.paragraph(self, on)
1185 @@ -615,14 +1041,22 @@
1186 self._in_li = self._in_li + 1
1187 tag = 'p'
1188 if on:
1189 - return self.open(tag)
1190 - return self.close(tag)
1191 -
1192 - def rule(self, size=None):
1193 - if size:
1194 + tagstr = self.open(tag, **kw)
1195 + else:
1196 + tagstr = self.close(tag)
1197 + return tagstr
1198 +
1199 + def rule(self, size=None, **kw):
1200 + """Creates a horizontal rule with an <hr> element.
1201 +
1202 + If size is a number in the range [1..6], the CSS class of the rule
1203 + is set to 'hr1' through 'hr6'. The intent is that the larger the
1204 + size number the thicker or bolder the rule will be.
1205 + """
1206 + if size and 1 <= size <= 6:
1207 # Add hr class: hr1 - hr6
1208 - return self.open('hr', newline=1, attr={'class': 'hr%d' % size})
1209 - return self.open('hr', newline=1)
1210 + return self.open('hr', newline=1, attr={'class': 'hr%d' % size}, **kw)
1211 + return self.open('hr', newline=1, **kw)
1212
1213 def icon(self, type):
1214 return self.request.theme.make_icon(type)
1215 @@ -634,9 +1068,30 @@
1216 href = self.request.theme.img_url(img)
1217 return self.image(src=href, alt=text, width=str(w), height=str(h))
1218
1219 + def image(self, src=None, **kw):
1220 + """Creates an inline image with an <img> element.
1221 +
1222 + The src argument must be the URL to the image file.
1223 + """
1224 + if src:
1225 + kw['src']=src
1226 + return self.open('img',**kw)
1227 +
1228 # Lists ##############################################################
1229
1230 - def number_list(self, on, type=None, start=None):
1231 +
1232 + def number_list(self, on, type=None, start=None, **kw):
1233 + """Creates an HTML ordered list, <ol> element.
1234 +
1235 + The 'type' if specified can be any legal numbered
1236 + list-style-type, such as 'decimal','lower-roman', etc.
1237 +
1238 + The 'start' argument if specified gives the numeric value of
1239 + the first list item (default is 1).
1240 +
1241 + Call once with on=1 to start the list, and a second time
1242 + with on=0 to end it.
1243 + """
1244 tag = 'ol'
1245 if on:
1246 attr = {}
1247 @@ -644,49 +1099,82 @@
1248 attr['type'] = type
1249 if start is not None:
1250 attr['start'] = start
1251 - return self.open(tag, newline=1, attr=attr)
1252 - return self.close(tag)
1253 + tagstr =self.open(tag, newline=1, attr=attr, **kw)
1254 + else:
1255 + tagstr = self.close(tag, newline=1)
1256 + return tagstr
1257
1258 - def bullet_list(self, on):
1259 + def bullet_list(self, on, **kw):
1260 + """Creates an HTML ordered list, <ol> element.
1261 +
1262 + The 'type' if specified can be any legal unnumbered
1263 + list-style-type, such as 'disc','square', etc.
1264 +
1265 + Call once with on=1 to start the list, and a second time
1266 + with on=0 to end it.
1267 + """
1268 tag = 'ul'
1269 if on:
1270 - return self.open(tag, newline=1)
1271 - return self.close(tag)
1272 -
1273 + tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1274 + else:
1275 + tagstr = '\n' + self._indent_spaces() + self.close(tag, newline=0)
1276 + return tagstr
1277 +
1278 def listitem(self, on, **kw):
1279 - """ List item inherit its lang from the list. """
1280 + """Adds a list item, <li> element, to a previously opened
1281 + bullet or number list.
1282 +
1283 + Call once with on=1 to start the region, and a second time
1284 + with on=0 to end it.
1285 + """
1286 tag = 'li'
1287 self._in_li = on != 0
1288 if on:
1289 - attr = {}
1290 - css_class = kw.get('css_class', None)
1291 - if css_class:
1292 - attr['class'] = css_class
1293 - style = kw.get('style', None)
1294 - if style:
1295 - attr['style'] = style
1296 - return self.open(tag, attr=attr)
1297 - return self.close(tag)
1298 + tagstr = self.open(tag, newline=1, **kw)
1299 + else:
1300 + tagstr = self.close(tag, newline=1)
1301 + return tagstr
1302 +
1303 + def definition_list(self, on, **kw):
1304 + """Creates an HTML definition list, <dl> element.
1305
1306 - def definition_list(self, on):
1307 + Call once with on=1 to start the list, and a second time
1308 + with on=0 to end it.
1309 + """
1310 tag = 'dl'
1311 if on:
1312 - return self.open(tag, newline=1)
1313 - return self.close(tag)
1314 + tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1315 + else:
1316 + tagstr = '\n' + self._indent_spaces() + self.close(tag, newline=0)
1317 + return tagstr
1318 +
1319 + def definition_term(self, on, **kw):
1320 + """Adds a new term to a definition list, HTML element <dt>.
1321
1322 - def definition_term(self, on):
1323 + Call once with on=1 to start the term, and a second time
1324 + with on=0 to end it.
1325 + """
1326 tag = 'dt'
1327 if on:
1328 - return self.open(tag)
1329 - return self.close(tag)
1330 -
1331 - def definition_desc(self, on):
1332 + tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1333 + else:
1334 + tagstr = self.close(tag, newline=0)
1335 + return tagstr
1336 +
1337 + def definition_desc(self, on, **kw):
1338 + """Gives the definition to a definition item, HTML element <dd>.
1339 +
1340 + Call once with on=1 to start the definition, and a second time
1341 + with on=0 to end it.
1342 + """
1343 tag = 'dd'
1344 if on:
1345 - return self.open(tag)
1346 - return self.close(tag)
1347 + tagstr = '\n' + self._indent_spaces() + self.open(tag, newline=0, **kw)
1348 + else:
1349 + tagstr = self.close(tag, newline=0)
1350 + return tagstr
1351
1352 - def heading(self, on, depth, id = None, **kw):
1353 + def heading(self, on, depth, **kw):
1354 # remember depth of first heading, and adapt counting depth accordingly
1355 if not self._base_depth:
1356 self._base_depth = depth
1357 @@ -722,11 +1210,8 @@
1358 number = '.'.join(map(str, self.request._fmt_hd_counters[self._show_section_numbers-1:]))
1359 if number: number += ". "
1360
1361 - attr = {}
1362 - if id:
1363 - attr['id'] = id
1364 # Add space before heading, easier to check source code
1365 - result = '\n' + self.open('h%d' % heading_depth, attr=attr)
1366 + result = '\n' + self.open('h%d' % heading_depth, **kw)
1367
1368 # TODO: convert this to readable code
1369 if self.request.user.show_topbottom:
1370 @@ -795,7 +1280,7 @@
1371 return result
1372
1373
1374 - def table(self, on, attrs=None):
1375 + def table(self, on, attrs=None, **kw):
1376 """ Create table
1377
1378 @param on: start table
1379 @@ -814,36 +1299,53 @@
1380 attrs = {}
1381 else:
1382 attrs = self._checkTableAttr(attrs, 'table')
1383 - result.append(self.open('table', newline=1, attr=attrs))
1384 + result.append(self.open('table', newline=1, attr=attrs,
1385 + allowed_attrs=self._allowed_table_attrs['table'],
1386 + **kw))
1387 + result.append(self.open('tbody', newline=1))
1388 else:
1389 - # Close table then div
1390 + # Close tbody, table, and then div
1391 + result.append(self.close('tbody'))
1392 result.append(self.close('table'))
1393 result.append(self.close('div'))
1394
1395 return ''.join(result)
1396
1397 - def table_row(self, on, attrs=None):
1398 + def table_row(self, on, attrs=None, **kw):
1399 tag = 'tr'
1400 if on:
1401 if not attrs:
1402 attrs = {}
1403 else:
1404 attrs = self._checkTableAttr(attrs, 'row')
1405 - return self.open(tag, newline=1, attr=attrs)
1406 - return self.close(tag)
1407 + return self.open(tag, newline=1, attr=attrs,
1408 + allowed_attrs=self._allowed_table_attrs['row'],
1409 + **kw)
1410 + return self.close(tag) + '\n'
1411
1412 - def table_cell(self, on, attrs=None):
1413 + def table_cell(self, on, attrs=None, **kw):
1414 tag = 'td'
1415 if on:
1416 if not attrs:
1417 attrs = {}
1418 else:
1419 attrs = self._checkTableAttr(attrs, '')
1420 - return self.open(tag, newline=1, attr=attrs)
1421 - return self.close(tag)
1422 -
1423 - def escapedText(self, text):
1424 - return wikiutil.escape(text)
1425 + return ' ' + self.open(tag, attr=attrs,
1426 + allowed_attrs=self._allowed_table_attrs[''],
1427 + **kw)
1428 + return self.close(tag) + '\n'
1429 +
1430 + def text(self, text, **kw):
1431 + txt = FormatterBase.text( self, text, **kw )
1432 + if kw:
1433 + return self.open('span',**kw) + txt + self.close('span')
1434 + return txt
1435 +
1436 + def escapedText(self, text, **kw):
1437 + txt = wikiutil.escape(text)
1438 + if kw:
1439 + return self.open('span',**kw) + txt + self.close('span')
1440 + return txt
1441
1442 def rawHTML(self, markup):
1443 return markup
1444 diff -urN moin-1.5.0/MoinMoin/formatter/text_plain.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_plain.py
1445 --- moin-1.5.0/MoinMoin/formatter/text_plain.py 2005-11-18 14:24:24.000000000 -0500
1446 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_plain.py 2006-01-19 14:59:50.000000000 -0500
1447 @@ -73,33 +73,38 @@
1448
1449 def attachment_link(self, url, text, **kw):
1450 return "[%s]" % text
1451 +
1452 def attachment_image(self, url, **kw):
1453 - return "[image:%s]" % url
1454 + title = ''
1455 + for a in (u'title', u'html__title', u'alt', u'html_alt'):
1456 + if kw.has_key(a):
1457 + title = ':' + kw[a]
1458 + return "[image:%s%s]" % (url, title)
1459
1460 def attachment_drawing(self, url, text, **kw):
1461 return "[drawing:%s]" % text
1462
1463 - def text(self, text):
1464 + def text(self, text, **kw):
1465 self._did_para = 0
1466 if self._text is not None:
1467 self._text.append(text)
1468 return text
1469
1470 - def rule(self, size=0):
1471 + def rule(self, size=0, **kw):
1472 size = min(size, 10)
1473 ch = u"---~=*+#####"[size]
1474 return (ch * 79) + u'\n'
1475
1476 - def strong(self, on):
1477 + def strong(self, on, **kw):
1478 return u'*'
1479
1480 - def emphasis(self, on):
1481 + def emphasis(self, on, **kw):
1482 return u'/'
1483
1484 - def highlight(self, on):
1485 + def highlight(self, on, **kw):
1486 return u''
1487
1488 - def number_list(self, on, type=None, start=None):
1489 + def number_list(self, on, type=None, start=None, **kw):
1490 if on:
1491 self._in_list = 1
1492 return [u'\n', u'\n\n'][not self._did_para]
1493 @@ -110,7 +115,7 @@
1494 return u'\n'
1495 return u''
1496
1497 - def bullet_list(self, on):
1498 + def bullet_list(self, on, **kw):
1499 if on:
1500 self._in_list = -1
1501 return [u'\n', u'\n\n'][not self._did_para]
1502 @@ -136,20 +141,20 @@
1503 self._did_para = 1
1504 return u'\n'
1505
1506 - def sup(self, on):
1507 + def sup(self, on, **kw):
1508 return u'^'
1509
1510 - def sub(self, on):
1511 + def sub(self, on, **kw):
1512 return u'_'
1513
1514 - def strike(self, on):
1515 + def strike(self, on, **kw):
1516 return u'__'
1517
1518 def code(self, on, **kw):
1519 #return [unichr(0x60), unichr(0xb4)][not on]
1520 return u"'" # avoid high-ascii
1521
1522 - def preformatted(self, on):
1523 + def preformatted(self, on, **kw):
1524 FormatterBase.preformatted(self, on)
1525 snip = u'---%<'
1526 snip = snip + (u'-' * (78 - len(snip)))
1527 @@ -158,10 +163,10 @@
1528 else:
1529 return snip + u'\n'
1530
1531 - def small(self, on):
1532 + def small(self, on, **kw):
1533 return u''
1534
1535 - def big(self, on):
1536 + def big(self, on, **kw):
1537 return u''
1538
1539 def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
1540 @@ -191,7 +196,7 @@
1541 def code_token(self, on, tok_type):
1542 return ""
1543
1544 - def paragraph(self, on):
1545 + def paragraph(self, on, **kw):
1546 FormatterBase.paragraph(self, on)
1547 if self._did_para:
1548 on = 0
1549 @@ -212,33 +217,34 @@
1550 self._text = None
1551 return result
1552
1553 - def table(self, on, attrs={}):
1554 + def table(self, on, attrs={}, **kw):
1555 return u''
1556
1557 - def table_row(self, on, attrs={}):
1558 + def table_row(self, on, attrs={}, **kw):
1559 return u''
1560
1561 - def table_cell(self, on, attrs={}):
1562 + def table_cell(self, on, attrs={}, **kw):
1563 return u''
1564
1565 - def underline(self, on):
1566 + def underline(self, on, **kw):
1567 return u'_'
1568
1569 - def definition_list(self, on):
1570 + def definition_list(self, on, **kw):
1571 return u''
1572
1573 - def definition_term(self, on, compact=0):
1574 + def definition_term(self, on, compact=0, **kw):
1575 result = u''
1576 if not compact: result = result + u'\n'
1577 if not on: result = result + u':\n'
1578 return result
1579
1580 - def definition_desc(self, on):
1581 + def definition_desc(self, on, **kw):
1582 return [u' ', u'\n'][not on]
1583
1584 - def image(self, **kw):
1585 - if kw.has_key(u'alt'):
1586 - return kw[u'alt']
1587 + def image(self, src=None, **kw):
1588 + for a in (u'title', u'html__title', u'alt', u'html_alt'):
1589 + if kw.has_key(a):
1590 + return kw[a]
1591 return u''
1592
1593 def lang(self, on, lang_name):
1594 diff -urN moin-1.5.0/MoinMoin/formatter/text_xml.py moin-1.5.0-formatter-patch/MoinMoin/formatter/text_xml.py
1595 --- moin-1.5.0/MoinMoin/formatter/text_xml.py 2005-12-01 20:30:07.000000000 -0500
1596 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/text_xml.py 2006-01-19 15:03:41.000000000 -0500
1597 @@ -77,12 +77,12 @@
1598 return '<attachmentdrawing href="%s">%s</attachmentdrawing>' % (
1599 url, text)
1600
1601 - def text(self, text):
1602 + def text(self, text, **kw):
1603 if self.in_pre:
1604 return text.replace(']]>', ']]>]]><![CDATA[')
1605 return self._escape(text)
1606
1607 - def rule(self, size=0):
1608 + def rule(self, size=0, **kw):
1609 return "\n<br/>%s<br/>\n" % ("-"*78,) # <hr/> not supported in stylebook
1610 if size:
1611 return '<hr size="%d"/>\n' % (size,)
1612 @@ -92,21 +92,21 @@
1613 def icon(self, type):
1614 return '<icon type="%s" />' % type
1615
1616 - def strong(self, on):
1617 + def strong(self, on, **kw):
1618 return ['<strong>', '</strong>'][not on]
1619
1620 - def emphasis(self, on):
1621 + def emphasis(self, on, **kw):
1622 return ['<em>', '</em>'][not on]
1623
1624 - def highlight(self, on):
1625 + def highlight(self, on, **kw):
1626 return ['<strong>', '</strong>'][not on]
1627
1628 - def number_list(self, on, type=None, start=None):
1629 + def number_list(self, on, type=None, start=None, **kw):
1630 result = ''
1631 if self.in_p: result = self.paragraph(0)
1632 return result + ['<ol>', '</ol>\n'][not on]
1633
1634 - def bullet_list(self, on):
1635 + def bullet_list(self, on, **kw):
1636 result = ''
1637 if self.in_p: result = self.paragraph(0)
1638 return result + ['<ul>', '</ul>\n'][not on]
1639 @@ -117,22 +117,22 @@
1640 def code(self, on, **kw):
1641 return ['<code>', '</code>'][not on]
1642
1643 - def sup(self, on):
1644 + def sup(self, on, **kw):
1645 return ['<sup>', '</sup>'][not on]
1646
1647 - def sub(self, on):
1648 + def sub(self, on, **kw):
1649 return ['<sub>', '</sub>'][not on]
1650
1651 - def strike(self, on):
1652 + def strike(self, on, **kw):
1653 return ['<strike>', '</strike>'][not on]
1654
1655 - def preformatted(self, on):
1656 + def preformatted(self, on, **kw):
1657 FormatterBase.preformatted(self, on)
1658 result = ''
1659 if self.in_p: result = self.paragraph(0)
1660 return result + ['<source><![CDATA[', ']]></source>'][not on]
1661
1662 - def paragraph(self, on):
1663 + def paragraph(self, on, **kw):
1664 FormatterBase.paragraph(self, on)
1665 return ['<p>', '</p>\n'][not on]
1666
1667 @@ -160,41 +160,42 @@
1668
1669 return result + '<s%d%s title="' % (depth, id_text)
1670
1671 - def table(self, on, attrs={}):
1672 + def table(self, on, attrs={}, **kw):
1673 return ['<table>', '</table>'][not on]
1674
1675 - def table_row(self, on, attrs={}):
1676 + def table_row(self, on, attrs={}, **kw):
1677 return ['<tr>', '</tr>'][not on]
1678
1679 - def table_cell(self, on, attrs={}):
1680 + def table_cell(self, on, attrs={}, **kw):
1681 return ['<td>', '</td>'][not on]
1682
1683 def anchordef(self, id):
1684 return '<anchor id="%s"/>' % id
1685
1686 - def anchorlink(self, on, name='', id=None):
1687 + def anchorlink(self, on, name='', **kw):
1688 + id = kw.get('id',None)
1689 extra = ''
1690 if id:
1691 extra = ' id="%s"' % id
1692 return ('<link anchor="%s"%s>' % (name, extra) ,'</link>') [not on]
1693
1694 - def underline(self, on):
1695 + def underline(self, on, **kw):
1696 return self.strong(on) # no underline in StyleBook
1697
1698 - def definition_list(self, on):
1699 + def definition_list(self, on, **kw):
1700 result = ''
1701 if self.in_p: result = self.paragraph(0)
1702 return result + ['<gloss>', '</gloss>'][not on]
1703
1704 - def definition_term(self, on, compact=0):
1705 + def definition_term(self, on, compact=0, **kw):
1706 return ['<label>', '</label>'][not on]
1707
1708 - def definition_desc(self, on):
1709 + def definition_desc(self, on, **kw):
1710 return ['<item>', '</item>'][not on]
1711
1712 - def image(self, **kw):
1713 - valid_attrs = ['src', 'width', 'height', 'alt']
1714 - attrs = {}
1715 + def image(self, src=None, **kw):
1716 + valid_attrs = ['src', 'width', 'height', 'alt', 'title']
1717 + attrs = {'src':src}
1718 for key, value in kw.items():
1719 if key in valid_attrs:
1720 attrs[key] = value
1721 diff -urN moin-1.5.0/MoinMoin/formatter/xml_docbook.py moin-1.5.0-formatter-patch/MoinMoin/formatter/xml_docbook.py
1722 --- moin-1.5.0/MoinMoin/formatter/xml_docbook.py 2005-12-01 20:30:07.000000000 -0500
1723 +++ moin-1.5.0-formatter-patch/MoinMoin/formatter/xml_docbook.py 2006-01-19 15:02:51.000000000 -0500
1724 @@ -132,7 +132,7 @@
1725 def endDocument(self):
1726 return self.outputFormatter.getFooter()
1727
1728 - def text(self, text):
1729 + def text(self, text, **kw):
1730 if text == "\\n":
1731 srcText = "\n"
1732 else:
1733 @@ -183,7 +183,7 @@
1734
1735 return ""
1736
1737 - def paragraph(self, on):
1738 + def paragraph(self, on, **kw):
1739 FormatterBase.paragraph(self, on)
1740 if on:
1741 para = self.doc.createElement("para")
1742 @@ -269,40 +269,40 @@
1743
1744 return self._handleNode(name, on, attributes)
1745
1746 - def strong(self, on):
1747 + def strong(self, on, **kw):
1748 return self._handleFormatting("emphasis", on, (('role','strong'), ))
1749
1750 - def emphasis(self, on):
1751 + def emphasis(self, on, **kw):
1752 return self._handleFormatting("emphasis", on)
1753
1754 - def underline(self, on):
1755 + def underline(self, on, **kw):
1756 return self._handleFormatting("emphasis", on, (('role','underline'), ))
1757
1758 - def highlight(self, on):
1759 + def highlight(self, on, **kw):
1760 return self._handleFormatting("emphasis", on, (('role','highlight'), ))
1761
1762 - def sup(self, on):
1763 + def sup(self, on, **kw):
1764 return self._handleFormatting("superscript", on)
1765
1766 - def sub(self, on):
1767 + def sub(self, on, **kw):
1768 return self._handleFormatting("subscript", on)
1769
1770 - def strike(self, on):
1771 + def strike(self, on, **kw):
1772 # does not yield <strike> using the HTML XSLT files here ...
1773 # but seems to be correct
1774 return self._handleFormatting("emphasis", on,
1775 (('role','strikethrough'), ))
1776
1777 - def code(self, on, **kwargs):
1778 + def code(self, on, **kw):
1779 return self._handleFormatting("code", on)
1780
1781 - def preformatted(self, on):
1782 + def preformatted(self, on, **kw):
1783 return self._handleFormatting("screen", on)
1784
1785
1786 ### Lists ###########################################################
1787
1788 - def number_list(self, on, type=None, start=None):
1789 + def number_list(self, on, type=None, start=None, **kw):
1790 docbook_ol_types = {'1': "arabic",
1791 'a': "loweralpha",
1792 'A': "upperalpha",
1793 @@ -316,16 +316,16 @@
1794
1795 return self._handleNode('orderedlist', on, attrs)
1796
1797 - def bullet_list(self, on):
1798 + def bullet_list(self, on, **kw):
1799 return self._handleNode("itemizedlist", on)
1800
1801 - def definition_list(self, on):
1802 + def definition_list(self, on, **kw):
1803 return self._handleNode("glosslist", on)
1804
1805 '''When on is false, we back out just on level. This is
1806 ok because we know definition_desc gets called, and we
1807 back out two levels there'''
1808 - def definition_term(self, on, compact=0):
1809 + def definition_term(self, on, compact=0, **kw):
1810 if on:
1811 entry=self.doc.createElement('glossentry')
1812 term=self.doc.createElement('glossterm')
1813 @@ -337,7 +337,7 @@
1814 return ""
1815
1816 '''We backout two levels when 'on' is false, to leave the glossentry stuff'''
1817 - def definition_desc(self, on):
1818 + def definition_desc(self, on, **kw):
1819 if on:
1820 return self._handleNode("glossdef", on)
1821 else:
1822 @@ -381,7 +381,8 @@
1823 self._handleNode("ulink", False)
1824 return ""
1825
1826 - def anchorlink(self, on, name='', id=None):
1827 + def anchorlink(self, on, name='', **kw):
1828 + id = kw.get('id',None)
1829 attrs = []
1830 if name != '':
1831 attrs.append(('endterm', name))
1832 @@ -438,7 +439,9 @@
1833
1834
1835 ### Images and Smileys ##############################################
1836 - def image(self, **kw):
1837 + def image(self, src=None, **kw):
1838 + if src:
1839 + kw['src']=src
1840 media = self.doc.createElement('inlinemediaobject')
1841
1842 imagewrap = self.doc.createElement('imageobject')
1843 @@ -452,12 +455,17 @@
1844 if kw.has_key('height'):
1845 image.setAttribute('depth', kw['height'])
1846 imagewrap.appendChild(image)
1847 -
1848 - if kw.has_key('alt'):
1849 +
1850 + title = ''
1851 + for a in ('title', 'html_title', 'alt', 'html_alt'):
1852 + if kw.has_key(titleattr):
1853 + title = kw[a]
1854 + break
1855 + if title:
1856 txtcontainer = self.doc.createElement('textobject')
1857 media.appendChild(txtcontainer)
1858 txtphrase = self.doc.createElement('phrase')
1859 - txtphrase.appendChild(self.doc.createTextNode(kw['alt']))
1860 + txtphrase.appendChild(self.doc.createTextNode(title))
1861 txtcontainer.appendChild(txtphrase)
1862
1863 self.cur.appendChild(media)
1864 @@ -477,7 +485,7 @@
1865
1866 #FIXME: We should copy code from text_html.py for attr handling
1867
1868 - def table(self, on, attrs=None):
1869 + def table(self, on, attrs=None, **kw):
1870 sanitized_attrs = []
1871 if attrs and attrs.has_key('id'):
1872 sanitized_attrs[id] = attrs['id']
1873 @@ -489,14 +497,14 @@
1874 self._handleNode("tbody", on)
1875 return ""
1876
1877 - def table_row(self, on, attrs=None):
1878 + def table_row(self, on, attrs=None, **kw):
1879 self.table_current_row_cells = 0
1880 sanitized_attrs = []
1881 if attrs and attrs.has_key('id'):
1882 sanitized_attrs[id] = attrs['id']
1883 return self._handleNode("row", on, sanitized_attrs)
1884
1885 - def table_cell(self, on, attrs=None):
1886 + def table_cell(self, on, attrs=None, **kw):
1887 # Finish row definition
1888 sanitized_attrs = []
1889 if attrs and attrs.has_key('id'):
1890 @@ -572,11 +580,11 @@
1891 return u""
1892
1893 ### Not supported ###################################################
1894 - def rule(self, size = 0):
1895 + def rule(self, size = 0, **kw):
1896 return ""
1897
1898 - def small(self, on):
1899 + def small(self, on, **kw):
1900 return ""
1901
1902 - def big(self, on):
1903 + def big(self, on, **kw):
1904 return ""
1905 diff -urN moin-1.5.0/wiki/htdocs/classic/css/common.css moin-1.5.0-formatter-patch/wiki/htdocs/classic/css/common.css
1906 --- moin-1.5.0/wiki/htdocs/classic/css/common.css 2005-12-17 16:22:37.000000000 -0500
1907 +++ moin-1.5.0-formatter-patch/wiki/htdocs/classic/css/common.css 2006-01-18 17:12:49.000000000 -0500
1908 @@ -110,8 +110,9 @@
1909 .hr5 {height: 7px;}
1910 .hr6 {height: 8px;}
1911
1912 -/* Replacement for html 3 u tag */
1913 +/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
1914 .u {text-decoration: underline;}
1915 +.strike {text-decoration: line-through;}
1916
1917 .footnotes ul {
1918 padding: 0 2em;
1919 diff -urN moin-1.5.0/wiki/htdocs/modern/css/common.css moin-1.5.0-formatter-patch/wiki/htdocs/modern/css/common.css
1920 --- moin-1.5.0/wiki/htdocs/modern/css/common.css 2005-12-17 16:18:10.000000000 -0500
1921 +++ moin-1.5.0-formatter-patch/wiki/htdocs/modern/css/common.css 2006-01-18 17:12:23.000000000 -0500
1922 @@ -187,8 +187,9 @@
1923 .hr5 {height: 6pt;}
1924 .hr6 {height: 7pt;}
1925
1926 -/* Replacement for html 3 u tag */
1927 +/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
1928 .u {text-decoration: underline;}
1929 +.strike {text-decoration: line-through;}
1930
1931 /* eye catchers */
1932 .warning
1933 diff -urN moin-1.5.0/wiki/htdocs/rightsidebar/css/common.css moin-1.5.0-formatter-patch/wiki/htdocs/rightsidebar/css/common.css
1934 --- moin-1.5.0/wiki/htdocs/rightsidebar/css/common.css 2005-12-17 16:24:52.000000000 -0500
1935 +++ moin-1.5.0-formatter-patch/wiki/htdocs/rightsidebar/css/common.css 2006-01-18 17:13:22.000000000 -0500
1936 @@ -123,8 +123,9 @@
1937 .hr5 {height: 6px;}
1938 .hr6 {height: 7px;}
1939
1940 -/* Replacement for html 3 u tag */
1941 +/* Replacement for deprecated html 3 <u> element and html 4 <strike> */
1942 .u {text-decoration: underline;}
1943 +.strike {text-decoration: line-through;}
1944
1945 .footnotes ul {
1946 padding: 0 2em;
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.