# -*- coding: iso-8859-1 -*- """ MoinMoin - DOM XML Formatter @copyright: 2000-2004 by Jürgen Hermann @license: GNU GPL, see COPYING for details. """ line_anchors = True from xml.dom import minidom from MoinMoin.formatter.base import FormatterBase #def print_dom(element, indent=''): # print indent + element.tagName # for child in element.get class Formatter(FormatterBase): """ This defines the output interface used all over the rest of the code. Note that no other means should be used to generate _content_ output, while navigational elements (HTML page header/footer) and the like can be printed directly without violating output abstraction. """ hardspace = ' ' # those tags will be automatically reopened if they were auto-closed due to # an enclosing tag being closed: format_tags = ['b', 'em', 'highlight', 'sup', 'sub', 'strike', 'code', 'u'] # those tags want a

around them: need_p = [] # format_tags[:] need_p.extend(['ol', 'a', 'pagelink', 'interwiki', 'macro']) # XXX add more # those tags inhibit auto-generation of a

after them: no_p_after = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'ol', 'ul', 'pre', 'small', 'big', 'table', 'td', 'tr', 'dt', 'codearea', 'codeline', 'codetoken', 'sysmesg'] # if key tag is opened, auto-close all tags in value list if they are open close_on_open = { 'h1': ['p'], 'li': ['li'], 'p': ['p'], #'pre': ['p'], } for i in xrange(2, 7): close_on_open['h%i' % i] = close_on_open['h1'] # if key tag is closed, auto-close all tags in value list if they are open close_on_close = { 'table': ['td', 'tr'], 'td': ['tr'], # XXX WTF? 'tr': ['td'], 'ol': ['li'], 'ul': ['li'], } # FIXME - this overrides the values defined above - FIXME XXX close_on_open = {} close_on_close = {} def __init__(self, request, **kw): FormatterBase.__init__(self, request, **kw) self.document = minidom.Document() self.document.documentElement = self.document.createElement('xml') self.position = self.document.documentElement self.tag_stack = [('xml', {})] def setPage(self, page): self.page = page def _open_tag(self, tag, **attrs): """ low level function: opens tag right now @param tag: tag name, string @param attrs: attributes keywords, ascii or unicode """ if tag == 'p': FormatterBase.paragraph(self, 1) self.tag_stack.append((tag, attrs)) node = self.document.createElement(tag) for name, value in attrs.items(): if value: node.setAttribute(name, unicode(value)) self.position.appendChild(node) self.position = node return '' def _close_tag(self, tag): """ low level function: closes tag right now must be the last opened tag!!! """ if tag == 'p': FormatterBase.paragraph(self, 0) if self.tag_stack[-1][0] != tag: raise ValueError, "closing of <%s> expected, but <%s> closed" % (self.tag_stack[-1][0], tag) self.position = self.position.parentNode return self.tag_stack.pop() def _add_tag(self, tag, **attrs): """ low level function: insert self closing tag right now does check_p """ self._check_p(tag) node = self.document.createElement(tag) for name, value in attrs.items(): if value: node.setAttribute(name, str(value)) self.position.appendChild(node) return '' def _text(self, text): if text.strip(): self._check_p() self.position.appendChild(self.document.createTextNode(text)) return '' def _set_tag(self, tag, on, **attrs): if on: close_on_open = self.close_on_open.get(tag, []) tags_to_reopen = [] while True: last_tag = self.tag_stack[-1][0] if last_tag in close_on_open: self._close_tag(last_tag) elif last_tag in self.format_tags: tags_to_reopen.append(self._close_tag(last_tag)) else: break # XXX check if enclosing tag is ok if tag in self.need_p: self._check_p(tag) self._open_tag(tag, **attrs) tags_to_reopen.reverse() for tag_name, args in tags_to_reopen: self._open_tag(tag_name, **args) else: tags_to_reopen = [] close_on_close = self.close_on_close.get(tag, []) # walk up while self.tag_stack: # collect format tags last_tag = self.tag_stack[-1][0] if last_tag == tag: break elif last_tag in close_on_close: self._close_tag(last_tag) elif last_tag in self.format_tags: tags_to_reopen.append(self._close_tag(last_tag)) else: self.request.write("tag_stack: %r\n" % self.tag_stack) self.request.write(self.document.documentElement.toprettyxml(" ")) raise ValueError, "closing of <%s> expected, but <%s> closed" % (last_tag, tag) self._close_tag(tag) tags_to_reopen.reverse() for tag_name, args in tags_to_reopen: self._open_tag(tag_name, **args) return '' def _check_p(self, opening_tag=None): if (opening_tag is not None) and (opening_tag not in self.need_p): return for tag in self.tag_stack: if tag[0] in self.no_p_after: return if self.in_p: return self._open_tag('p', type=str(opening_tag)) def sysmsg(self, on, **kw): """ Emit a system message (embed it into the page). Normally used to indicate disabled options, or invalid markup. """ return self._set_tag('sysmesg', on, **kw) def startDocument(self, pagename): return '' def endDocument(self): #return self.document.documentElement.toxml() return self.document.documentElement.toprettyxml(" ") def lang(self, on, lang_name): return self._set_tag('lang', on, value=lang_name) def pagelink(self, on, pagename='', page=None, **kw): apply(FormatterBase.pagelink, (self, pagename, page), kw) if not pagename and page is not None: pagename = page.page_name kw['pagename'] = pagename return self._set_tag('pagelink', on, **kw) def interwikilink(self, on, interwiki='', pagename='', **kw): kw['wiki'] = interwiki kw['pagename'] = pagename return self._set_tag('interwiki', on, **kw) def macro(self, macro_obj, name, args): # call the macro return self._add_tag('macro', name=name, args=(args or '')) def processor(self, processor_name, lines, is_parser=0): """ processor_name MUST be valid! writes out the result instead of returning it! """ node = self.document.createElement('processor') node.setAttribute('name', processor_name) node.setAttribute('isparser', is_parser) node.appendChild(self.document.createTextNode('\n'.join(lines))) return (self._set_tag('processor', True, name=processor_name, isparser=is_parser) + self.text('\n'.join(lines)) + self._set_tag('processor', False)) def dynamic_content(self, parser, callback, arg_list=[], arg_dict={}, returns_content=1): content = parser[callback](*arg_list, **arg_dict) if returns_content: return content else: return '' def url(self, on, url='', css=None, **kw): kw['href'] = str(url) if css: kw['class'] = str(css) return self._set_tag('a', on, **kw) def attachment_link(self, url, text, **kw): kw['href'] = url kw['type'] = 'link' return self._set_tag('attachment', 1, **kw) + self.text(text) + self._set_tag('attachment', 0, **kw) def attachment_image(self, url, **kw): kw['href'] = url kw['type'] = 'image' return self._add_tag('attachment', **kw) def attachment_drawing(self, url, **kw): kw['href'] = url kw['type'] = 'drawing' return self._add_tag('attachment', **kw) def attachment_inlined(self, url, **kw): kw['href'] = url kw['type'] = 'inline' return self._add_tag('attachment', **kw) def rule(self, size=0, **kw): return self._add_tag('hr', size=str(size)) def icon(self, type): return self._add_tag('icon', type=type) def smiley(self, type): return self._add_tag('smiley', type=type) def strong(self, on, **kw): return self._set_tag('b', on) def emphasis(self, on, **kw): return self._set_tag('em', on) def highlight(self, on, **kw): return self._set_tag('highlight', on) def number_list(self, on, type=None, start=None, **kw): return self._set_tag('ol', on, type=type, start=start) def bullet_list(self, on, **kw): return self._set_tag('ul', on) def listitem(self, on, **kw): return self._set_tag('li', on) def sup(self, on, **kw): return self._set_tag('sup', on) def sub(self, on, **kw): return self._set_tag('sub', on) def strike(self, on, **kw): return self._set_tag('strike', on) def code(self, on, **kw): return self._set_tag('code', on) def preformatted(self, on, **kw): self.in_pre = on != 0 return self._set_tag('pre', on) def paragraph(self, on, **kw): FormatterBase.paragraph(self, on) return self._set_tag('p', on) def linebreak(self, preformatted=1): if self.tag_stack[-1][0] == 'pre': return self.text('\n') else: return self._add_tag('br') def heading(self, on, depth, **kw): return self._set_tag('h%d' %depth, on, **kw) def _check_attrs(self, attrs): result = {} for name, value in attrs.iteritems(): result[str(name)] = value return result def table(self, on, attrs={}, **kw): return self._set_tag('table', on, **self._check_attrs(attrs)) def table_row(self, on, attrs={}, **kw): return self._set_tag('tr', on, **self._check_attrs(attrs)) def table_cell(self, on, attrs={}, **kw): return self._set_tag('td', on, **self._check_attrs(attrs)) def anchordef(self, name): return self._add_tag('anchor', name=name) def anchorlink(self, on, name, **kw): return self.url(on, "#" + name, **kw) def line_anchordef(self, lineno): if line_anchors: return self.anchordef("line-%d" % lineno) else: return '' def underline(self, on, **kw): return self._set_tag('u', on) def definition_list(self, on, **kw): return self._set_tag('dl', on) def definition_term(self, on, compact=0, **kw): # XXX may be not correct # self._langAttr() missing if compact and on: return self._set_tag('dt compact', on) else: return self._set_tag('dt', on) def definition_desc(self, on, **kw): # self._langAttr() missing return self._set_tag('dd', on) def image(self, src=None, **kw): """ Take HTML tag attributes in `attr`. Attribute names have to be lowercase! """ if src: kw['src'] = src return self._add_tag('img', **kw) def escapedText(self, text, **kw): return wikiutil.escape(text) def small(self, on, **kw): return self._set_tag('small', on) def big(self, on, **kw): return self._set_tag('big', on) def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1): kw = {'id': code_id, 'type': code_type, 'show': show, } if start != -1: kw['start'] = start if step != -1: kw['step'] = step return self._set_tag('codearea', on, **kw) def code_line(self, on): return self._set_tag('codeline', on) def code_token(self, on, tok_type): return self._set_tag('codetoken', on, type=tok_type)